mirror of
https://github.com/eclipse-cdt/cdt
synced 2025-07-23 17:05:26 +02:00
bug 498782 - [debug] synchronize selection between the DV and GDB
This adds a new service, GDBFocusSynchronizer, that helps keep the internal GDB selection and the Debug View selection synchronized. Change-Id: I021b3f65d61e82f6971bdb9232369b6fdf58ea5b
This commit is contained in:
parent
e9beafae10
commit
dc6e3a06ff
19 changed files with 1363 additions and 8 deletions
|
@ -212,6 +212,9 @@ implements IConsoleView, IDebuggerConsoleView, IConsoleListener, IPropertyChange
|
|||
fConsoleToPart.put(registered, part);
|
||||
fPartToConsole.put(part, registered);
|
||||
partActivated(part);
|
||||
if (console instanceof IDebuggerConsole) {
|
||||
display((IDebuggerConsole)console);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -258,6 +261,8 @@ implements IConsoleView, IDebuggerConsoleView, IConsoleListener, IPropertyChange
|
|||
DebuggerConsoleWorkbenchPart part = fConsoleToPart.get(console);
|
||||
if (part != null) {
|
||||
partActivated(part);
|
||||
// let the console know it's being activated
|
||||
fActiveConsole.consoleSelected();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -35,4 +35,10 @@ public interface IDebuggerConsole extends IConsole {
|
|||
* Request a re-computation of the name of the console.
|
||||
*/
|
||||
void resetName();
|
||||
|
||||
/**
|
||||
* This console has become selected, the implementation shall use this
|
||||
* notification to e.g. keep other views in sync with the context of the console
|
||||
*/
|
||||
public void consoleSelected();
|
||||
}
|
||||
|
|
|
@ -14,6 +14,7 @@ package org.eclipse.cdt.dsf.gdb.internal.ui;
|
|||
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
|
||||
import org.eclipse.cdt.dsf.gdb.internal.ui.console.GdbCliConsoleManager;
|
||||
import org.eclipse.cdt.dsf.gdb.internal.ui.console.TracingConsoleManager;
|
||||
import org.eclipse.cdt.dsf.gdb.internal.ui.sync.GdbDebugContextSyncManager;
|
||||
import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch;
|
||||
import org.eclipse.cdt.dsf.gdb.launching.LaunchMessages;
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
|
@ -48,6 +49,8 @@ public class GdbUIPlugin extends AbstractUIPlugin {
|
|||
|
||||
private static TracingConsoleManager fTracingConsoleManager;
|
||||
private static GdbCliConsoleManager fGdbConsoleManager;
|
||||
|
||||
private static GdbDebugContextSyncManager fGdbSelectionSyncManager;
|
||||
|
||||
private static IPreferenceStore fCorePreferenceStore;
|
||||
|
||||
|
@ -72,6 +75,9 @@ public class GdbUIPlugin extends AbstractUIPlugin {
|
|||
|
||||
fGdbConsoleManager = new GdbCliConsoleManager();
|
||||
fGdbConsoleManager.startup();
|
||||
|
||||
fGdbSelectionSyncManager = new GdbDebugContextSyncManager();
|
||||
fGdbSelectionSyncManager.startup();
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -82,6 +88,7 @@ public class GdbUIPlugin extends AbstractUIPlugin {
|
|||
public void stop(BundleContext context) throws Exception {
|
||||
fTracingConsoleManager.shutdown();
|
||||
fGdbConsoleManager.shutdown();
|
||||
fGdbSelectionSyncManager.shutdown();
|
||||
|
||||
disposeAdapterSets();
|
||||
plugin = null;
|
||||
|
@ -93,6 +100,10 @@ public class GdbUIPlugin extends AbstractUIPlugin {
|
|||
return fGdbConsoleManager;
|
||||
}
|
||||
|
||||
public static GdbDebugContextSyncManager getGdbSelectionSyncManager() {
|
||||
return fGdbSelectionSyncManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispose adapter sets for all launches.
|
||||
*/
|
||||
|
|
|
@ -9,7 +9,6 @@ package org.eclipse.cdt.dsf.gdb.internal.ui.console;
|
|||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.eclipse.cdt.debug.ui.debuggerconsole.IDebuggerConsole;
|
||||
import org.eclipse.cdt.debug.ui.debuggerconsole.IDebuggerConsoleView;
|
||||
import org.eclipse.cdt.dsf.gdb.IGdbDebugPreferenceConstants;
|
||||
import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin;
|
||||
|
@ -37,7 +36,7 @@ import org.eclipse.ui.part.IPageBookViewPage;
|
|||
* towards GDB. It is used whenever {@link IGDBBackend#isFullGdbConsoleSupported()}
|
||||
* returns false.
|
||||
*/
|
||||
public class GdbBasicCliConsole extends IOConsole implements IDebuggerConsole {
|
||||
public class GdbBasicCliConsole extends IOConsole implements IGDBDebuggerConsole {
|
||||
|
||||
/**
|
||||
* A conversion factor used to resolve number of characters from number of lines
|
||||
|
|
|
@ -13,7 +13,6 @@ import java.io.OutputStream;
|
|||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import org.eclipse.cdt.debug.ui.debuggerconsole.IDebuggerConsole;
|
||||
import org.eclipse.cdt.debug.ui.debuggerconsole.IDebuggerConsoleView;
|
||||
import org.eclipse.cdt.utils.pty.PTY;
|
||||
import org.eclipse.core.runtime.CoreException;
|
||||
|
@ -36,7 +35,7 @@ import org.eclipse.ui.part.IPageBookViewPage;
|
|||
* full-featured CLI interface. This is only supported with GDB >= 7.12
|
||||
* and if IGDBBackend.isFullGdbConsoleSupported() returns true.
|
||||
*/
|
||||
public class GdbFullCliConsole extends AbstractConsole implements IDebuggerConsole {
|
||||
public class GdbFullCliConsole extends AbstractConsole implements IGDBDebuggerConsole {
|
||||
private final ILaunch fLaunch;
|
||||
private final String fLabel;
|
||||
private final PTY fGdbPty;
|
||||
|
@ -206,4 +205,5 @@ public class GdbFullCliConsole extends AbstractConsole implements IDebuggerConso
|
|||
public IGdbTerminalControlConnector getTerminalControlConnector() {
|
||||
return fTerminalConnector;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,57 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2016 Ericsson and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*******************************************************************************/
|
||||
|
||||
package org.eclipse.cdt.dsf.gdb.internal.ui.console;
|
||||
|
||||
import org.eclipse.cdt.debug.ui.debuggerconsole.IDebuggerConsole;
|
||||
import org.eclipse.cdt.dsf.gdb.internal.service.IGDBFocusSynchronizer;
|
||||
import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin;
|
||||
import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch;
|
||||
import org.eclipse.cdt.dsf.service.DsfServicesTracker;
|
||||
import org.eclipse.cdt.dsf.service.DsfSession;
|
||||
import org.eclipse.core.runtime.IAdaptable;
|
||||
import org.eclipse.debug.core.ILaunch;
|
||||
import org.eclipse.debug.ui.DebugUITools;
|
||||
|
||||
/**
|
||||
* GDB specifics to IDebuggerConsole e.g. default implementations
|
||||
*/
|
||||
public interface IGDBDebuggerConsole extends IDebuggerConsole {
|
||||
/*
|
||||
* (non-Javadoc)
|
||||
*
|
||||
* @see org.eclipse.cdt.debug.ui.debuggerconsole.IDebuggerConsole#consoleSelected()
|
||||
*/
|
||||
@Override
|
||||
public default void consoleSelected() {
|
||||
DsfSession session = ((GdbLaunch)getLaunch()).getSession();
|
||||
if (!session.isActive()) {return;}
|
||||
|
||||
// only trigger the DV selection if the current selection is in
|
||||
// a different session
|
||||
IAdaptable context = DebugUITools.getDebugContext();
|
||||
if (context != null) {
|
||||
ILaunch selectedLaunch = context.getAdapter(ILaunch.class);
|
||||
if (getLaunch().equals(selectedLaunch)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
session.getExecutor().execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
DsfServicesTracker tracker = new DsfServicesTracker(GdbUIPlugin.getBundleContext(), session.getId());
|
||||
IGDBFocusSynchronizer gdbSync = tracker.getService(IGDBFocusSynchronizer.class);
|
||||
tracker.dispose();
|
||||
if (gdbSync != null) {
|
||||
gdbSync.sessionSelected();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2016 Ericsson AB and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*******************************************************************************/
|
||||
package org.eclipse.cdt.dsf.gdb.internal.ui.sync;
|
||||
|
||||
import org.eclipse.cdt.dsf.concurrent.ImmediateRequestMonitor;
|
||||
import org.eclipse.cdt.dsf.datamodel.IDMContext;
|
||||
import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext;
|
||||
import org.eclipse.cdt.dsf.gdb.internal.service.IGDBFocusSynchronizer;
|
||||
import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin;
|
||||
import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext;
|
||||
import org.eclipse.cdt.dsf.service.DsfServicesTracker;
|
||||
import org.eclipse.cdt.dsf.service.DsfSession;
|
||||
import org.eclipse.core.runtime.IAdaptable;
|
||||
import org.eclipse.debug.ui.DebugUITools;
|
||||
import org.eclipse.debug.ui.contexts.DebugContextEvent;
|
||||
import org.eclipse.debug.ui.contexts.IDebugContextListener;
|
||||
|
||||
/**
|
||||
* This instance propagates the selection of debug context elements e.g. Thread to the back end GDB
|
||||
*/
|
||||
public class GdbDebugContextSyncManager implements IDebugContextListener {
|
||||
|
||||
public void startup() {
|
||||
DebugUITools.getDebugContextManager().addDebugContextListener(this);
|
||||
}
|
||||
|
||||
public void shutdown() {
|
||||
DebugUITools.getDebugContextManager().removeDebugContextListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void debugContextChanged(DebugContextEvent event) {
|
||||
// Make sure that it's a change of selection that caused the event
|
||||
if ((event.getFlags() != DebugContextEvent.ACTIVATED)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Get selected element in the Debug View
|
||||
IAdaptable context = DebugUITools.getDebugContext();
|
||||
|
||||
if (context != null) {
|
||||
final IDMContext dmc = context.getAdapter(IDMContext.class);
|
||||
|
||||
if (dmc instanceof IMIExecutionDMContext || dmc instanceof IFrameDMContext) {
|
||||
// A thread or stack frame was selected. In either case, have GDB switch to the new
|
||||
// corresponding thread, if required.
|
||||
|
||||
// Resolve the debug session
|
||||
String eventSessionId = dmc.getSessionId();
|
||||
if (!(DsfSession.isSessionActive(eventSessionId))) {
|
||||
return;
|
||||
}
|
||||
|
||||
DsfSession session = DsfSession.getSession(eventSessionId);
|
||||
|
||||
// order GDB to switch thread
|
||||
session.getExecutor().execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
DsfServicesTracker tracker = new DsfServicesTracker(GdbUIPlugin.getBundleContext(),
|
||||
eventSessionId);
|
||||
IGDBFocusSynchronizer gdbSync = tracker.getService(IGDBFocusSynchronizer.class);
|
||||
tracker.dispose();
|
||||
|
||||
if (gdbSync != null) {
|
||||
gdbSync.setFocus(new IDMContext[] {dmc}, new ImmediateRequestMonitor() {});
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,121 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2016 Ericsson and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*******************************************************************************/
|
||||
|
||||
package org.eclipse.cdt.dsf.gdb.internal.ui.viewmodel.launch;
|
||||
|
||||
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.IStack.IFrameDMContext;
|
||||
import org.eclipse.cdt.dsf.debug.ui.viewmodel.launch.StackFramesVMNode;
|
||||
import org.eclipse.cdt.dsf.gdb.internal.service.IGDBFocusSynchronizer.IGDBFocusChangedEvent;
|
||||
import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext;
|
||||
import org.eclipse.cdt.dsf.service.DsfSession;
|
||||
import org.eclipse.cdt.dsf.ui.viewmodel.VMChildrenUpdate;
|
||||
import org.eclipse.cdt.dsf.ui.viewmodel.VMDelta;
|
||||
import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMProvider;
|
||||
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
|
||||
|
||||
public class GdbStackFramesVMNode extends StackFramesVMNode {
|
||||
|
||||
public GdbStackFramesVMNode(AbstractDMVMProvider provider, DsfSession session) {
|
||||
super(provider, session);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getDeltaFlags(Object e) {
|
||||
if (e instanceof IGDBFocusChangedEvent) {
|
||||
return IModelDelta.SELECT;
|
||||
}
|
||||
|
||||
return super.getDeltaFlags(e);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void buildDelta(final Object e, final VMDelta parentDelta, final int nodeOffset, final RequestMonitor rm) {
|
||||
if (e instanceof IGDBFocusChangedEvent) {
|
||||
buildDeltaForFocusChangedEvent((IGDBFocusChangedEvent)e, parentDelta, rm);
|
||||
}
|
||||
else {
|
||||
super.buildDelta(e, parentDelta, nodeOffset, rm);
|
||||
}
|
||||
}
|
||||
|
||||
private void buildDeltaForFocusChangedEvent(IGDBFocusChangedEvent event, VMDelta parentDelta, RequestMonitor rm) {
|
||||
getSession().getExecutor().execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
IDMContext ctx = event.getDMContext();
|
||||
|
||||
// Is IGDBFocusChangedEvent pertinent for this VMNode?
|
||||
if (ctx instanceof IFrameDMContext) {
|
||||
IFrameDMContext newFrameFocus = (IFrameDMContext)ctx;
|
||||
IMIExecutionDMContext execDmc = DMContexts.getAncestorOfType(newFrameFocus, IMIExecutionDMContext.class);
|
||||
if (execDmc == null) {
|
||||
rm.done();
|
||||
return;
|
||||
}
|
||||
|
||||
IRunControl runControl = getServicesTracker().getService(IRunControl.class);
|
||||
if (runControl == null) {
|
||||
// Required services have not initialized yet. Ignore the event.
|
||||
rm.done();
|
||||
return;
|
||||
}
|
||||
|
||||
if (runControl.isSuspended(execDmc) || runControl.isStepping(execDmc)) {
|
||||
// find the VMC index for the frame that switched, so we can select it correctly.
|
||||
getVMCIndexForDmc(
|
||||
GdbStackFramesVMNode.this,
|
||||
newFrameFocus,
|
||||
parentDelta,
|
||||
new DataRequestMonitor<Integer>(getExecutor(), rm) {
|
||||
@Override
|
||||
protected void handleSuccess() {
|
||||
// change to frameOffset
|
||||
final int frameOffset = getData();
|
||||
|
||||
// Retrieve the list of stack frames
|
||||
getVMProvider().updateNode(GdbStackFramesVMNode.this,
|
||||
new VMChildrenUpdate(parentDelta,
|
||||
getVMProvider().getPresentationContext(), -1,
|
||||
-1, new DataRequestMonitor<List<Object>>(
|
||||
getExecutor(), rm) {
|
||||
@Override
|
||||
public void handleSuccess() {
|
||||
final List<Object> data = getData();
|
||||
if (data != null && data.size() != 0) {
|
||||
// create the delta to select the
|
||||
// current stack frame
|
||||
parentDelta.addNode(
|
||||
data.get(frameOffset),
|
||||
frameOffset,
|
||||
IModelDelta.SELECT | IModelDelta.FORCE
|
||||
);
|
||||
}
|
||||
rm.done();
|
||||
}
|
||||
}));
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// thread is running - no delta to produce for the stack frame node
|
||||
rm.done();
|
||||
}
|
||||
} else {
|
||||
// context not a frame - nothing to do here
|
||||
rm.done();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
|
@ -25,7 +25,6 @@ import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommand
|
|||
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.gdb.internal.ui.GdbUIPlugin;
|
||||
import org.eclipse.cdt.dsf.gdb.service.IGDBTraceControl.ITraceRecordSelectedChangedDMEvent;
|
||||
import org.eclipse.cdt.dsf.gdb.service.IGDBTraceControl.ITracingStartedDMEvent;
|
||||
|
@ -73,7 +72,7 @@ public class LaunchVMProvider extends AbstractLaunchVMProvider
|
|||
IVMNode threadsNode = new ThreadVMNode(this, getSession());
|
||||
addChildNodes(containerNode, new IVMNode[] { threadsNode });
|
||||
|
||||
IVMNode stackFramesNode = new StackFramesVMNode(this, getSession());
|
||||
IVMNode stackFramesNode = new GdbStackFramesVMNode(this, getSession());
|
||||
addChildNodes(threadsNode, new IVMNode[] { stackFramesNode });
|
||||
}
|
||||
|
||||
|
|
|
@ -18,8 +18,11 @@ import java.util.Map;
|
|||
|
||||
import org.eclipse.cdt.debug.internal.ui.pinclone.PinCloneUtils;
|
||||
import org.eclipse.cdt.debug.ui.IPinProvider.IPinElementColorDescriptor;
|
||||
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
|
||||
import org.eclipse.cdt.dsf.concurrent.DsfRunnable;
|
||||
import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor;
|
||||
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.IProcesses.IThreadDMContext;
|
||||
|
@ -32,6 +35,7 @@ import org.eclipse.cdt.dsf.debug.ui.viewmodel.launch.AbstractThreadVMNode;
|
|||
import org.eclipse.cdt.dsf.debug.ui.viewmodel.launch.ExecutionContextLabelText;
|
||||
import org.eclipse.cdt.dsf.debug.ui.viewmodel.launch.ILaunchVMConstants;
|
||||
import org.eclipse.cdt.dsf.gdb.IGdbDebugPreferenceConstants;
|
||||
import org.eclipse.cdt.dsf.gdb.internal.service.IGDBFocusSynchronizer.IGDBFocusChangedEvent;
|
||||
import org.eclipse.cdt.dsf.gdb.internal.ui.GdbPinProvider;
|
||||
import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin;
|
||||
import org.eclipse.cdt.dsf.gdb.service.IGDBProcesses.IGdbThreadDMData;
|
||||
|
@ -373,6 +377,9 @@ public class ThreadVMNode extends AbstractThreadVMNode
|
|||
// being displayed.
|
||||
return IModelDelta.CONTENT;
|
||||
}
|
||||
else if (e instanceof IGDBFocusChangedEvent) {
|
||||
return IModelDelta.SELECT;
|
||||
}
|
||||
return super.getDeltaFlags(e);
|
||||
}
|
||||
|
||||
|
@ -395,10 +402,49 @@ public class ThreadVMNode extends AbstractThreadVMNode
|
|||
ancestorDelta.setFlags(ancestorDelta.getFlags() | IModelDelta.CONTENT);
|
||||
}
|
||||
rm.done();
|
||||
} else if (e instanceof IGDBFocusChangedEvent) {
|
||||
buildDeltaForFocusChangedEvent((IGDBFocusChangedEvent)e, parentDelta, nodeOffset, rm);
|
||||
} else {
|
||||
super.buildDelta(e, parentDelta, nodeOffset, rm);
|
||||
}
|
||||
}
|
||||
|
||||
private void buildDeltaForFocusChangedEvent(IGDBFocusChangedEvent event, VMDelta parentDelta, int nodeOffset, RequestMonitor rm) {
|
||||
getSession().getExecutor().execute(new DsfRunnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
// can we find a thread context in the hierarchy of the IGDBFocusChangedEvent's context?
|
||||
IDMContext thread = DMContexts.getAncestorOfType(event.getDMContext(), IMIExecutionDMContext.class);
|
||||
final IDMContext newThreadFocus = thread;
|
||||
if (newThreadFocus != null) {
|
||||
// we need to find the VMC index for the thread that switched, so we can
|
||||
// select it correctly.
|
||||
getVMCIndexForDmc(
|
||||
ThreadVMNode.this,
|
||||
newThreadFocus,
|
||||
parentDelta,
|
||||
new DataRequestMonitor<Integer>(getExecutor(), rm) {
|
||||
@Override
|
||||
protected void handleSuccess() {
|
||||
final int threadOffset = getData();
|
||||
// Create a delta for the thread node - Select it whether it's running or not
|
||||
// this way the thread will be visible in the DV even if we end-up selecting one
|
||||
// of its frame. Using the FORCE flag to override the sticky selection
|
||||
// policy.
|
||||
parentDelta.addNode(createVMContext(newThreadFocus), nodeOffset + threadOffset,
|
||||
IModelDelta.SELECT | IModelDelta.FORCE);
|
||||
rm.done();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
// context not a thread - nothing to do here
|
||||
rm.done();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private static final String MEMENTO_NAME = "THREAD_MEMENTO_NAME"; //$NON-NLS-1$
|
||||
|
||||
/*
|
||||
|
|
|
@ -28,6 +28,7 @@ Export-Package: org.eclipse.cdt.dsf.gdb,
|
|||
org.eclipse.cdt.examples.dsf.gdb",
|
||||
org.eclipse.cdt.dsf.gdb.internal.commands;x-friends:="org.eclipse.cdt.dsf.gdb.ui",
|
||||
org.eclipse.cdt.dsf.gdb.internal.memory;x-friends:="org.eclipse.cdt.dsf.gdb.ui",
|
||||
org.eclipse.cdt.dsf.gdb.internal.service;x-friends:="org.eclipse.cdt.dsf.gdb.ui,org.eclipse.cdt.tests.dsf.gdb",
|
||||
org.eclipse.cdt.dsf.gdb.internal.service.command.commands;x-internal:=true,
|
||||
org.eclipse.cdt.dsf.gdb.internal.service.command.events;x-internal:=true,
|
||||
org.eclipse.cdt.dsf.gdb.internal.service.command.output;x-internal:=true,
|
||||
|
|
|
@ -0,0 +1,367 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2016 Ericsson and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*******************************************************************************/
|
||||
|
||||
package org.eclipse.cdt.dsf.gdb.internal.service;
|
||||
|
||||
import java.util.Hashtable;
|
||||
|
||||
import org.eclipse.cdt.dsf.concurrent.ImmediateDataRequestMonitor;
|
||||
import org.eclipse.cdt.dsf.concurrent.ImmediateRequestMonitor;
|
||||
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.DataModelInitializedEvent;
|
||||
import org.eclipse.cdt.dsf.datamodel.IDMContext;
|
||||
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;
|
||||
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;
|
||||
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.IEventListener;
|
||||
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
|
||||
import org.eclipse.cdt.dsf.gdb.service.IGDBProcesses;
|
||||
import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl;
|
||||
import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.CommandFactory;
|
||||
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.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.MITuple;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.MIValue;
|
||||
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;
|
||||
|
||||
/**
|
||||
* This service keeps synchronized the CDT Debug View selection and GDB's
|
||||
* internal focus.
|
||||
*
|
||||
* To keep the Debug View selection synchronized to CDT's selection, the service keeps
|
||||
* track of what is the current GDB focus, by listening to the GDB MI notification
|
||||
* "=thread-selected". When this notification is received, the service orders a change
|
||||
* to CDT's Debug View selection to match, by sending an IGDBFocusChangedEvent.
|
||||
*
|
||||
* To keep GDB's focus synchronized to the Debug View selections, the UI listens to
|
||||
* platform 'Debug Selection changed' events, and then uses this service, to order GDB
|
||||
* to change focus to match the selection.
|
||||
*
|
||||
* Note: the mapping between the DV selection and GDB focus is not 1 to 1; there can
|
||||
* be multiple debug sessions at one time, all shown in the DV. There is however a single
|
||||
* effective DV selection. On the other end, each debug session has a dedicated instance
|
||||
* of GDB, having its own unique focus, at any given time. Also not all DV selections map
|
||||
* to a valid GDB focus.
|
||||
*
|
||||
* @since 5.2
|
||||
*/
|
||||
public class GDBFocusSynchronizer extends AbstractDsfService implements IGDBFocusSynchronizer, IEventListener
|
||||
{
|
||||
/** This service's opinion of what is the current GDB focus - it can be
|
||||
* a thread or stack frame context */
|
||||
private IDMContext fCurrentGDBFocus;
|
||||
|
||||
private IStack fStackService;
|
||||
private IGDBProcesses fProcesses;
|
||||
private IGDBControl fGdbcontrol;
|
||||
private CommandFactory fCommandFactory;
|
||||
|
||||
// default initial values
|
||||
private static final String THREAD_ID_DEFAULT = "1"; //$NON-NLS-1$
|
||||
|
||||
public GDBFocusSynchronizer(DsfSession session) {
|
||||
super(session);
|
||||
}
|
||||
|
||||
private class GDBFocusChangedEvent extends AbstractDMEvent<IDMContext>
|
||||
implements IGDBFocusChangedEvent
|
||||
{
|
||||
public GDBFocusChangedEvent(IDMContext ctx) {
|
||||
super(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected BundleContext getBundleContext() {
|
||||
return GdbPlugin.getBundleContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void initialize(final RequestMonitor requestMonitor) {
|
||||
super.initialize(new ImmediateRequestMonitor(requestMonitor) {
|
||||
@Override
|
||||
protected void handleSuccess() {
|
||||
doInitialize(requestMonitor);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void doInitialize(RequestMonitor requestMonitor) {
|
||||
// obtain reference to a few needed services
|
||||
fProcesses = getServicesTracker().getService(IGDBProcesses.class);
|
||||
fStackService = getServicesTracker().getService(IStack.class);
|
||||
fGdbcontrol = getServicesTracker().getService(IGDBControl.class);
|
||||
fCommandFactory = fGdbcontrol.getCommandFactory();
|
||||
|
||||
register(new String[] { IGDBFocusSynchronizer.class.getName()},
|
||||
new Hashtable<String, String>());
|
||||
|
||||
fGdbcontrol.addEventListener(this);
|
||||
getSession().addServiceEventListener(this, null);
|
||||
|
||||
// set a sane initial value for current GDB focus.
|
||||
// This value will be updated when the session has finished launching.
|
||||
// See updateContexts() below.
|
||||
fCurrentGDBFocus = createThreadContextFromThreadId(THREAD_ID_DEFAULT);
|
||||
requestMonitor.done();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void shutdown(RequestMonitor requestMonitor) {
|
||||
fGdbcontrol.removeEventListener(this);
|
||||
getSession().removeServiceEventListener(this);
|
||||
|
||||
unregister();
|
||||
super.shutdown(requestMonitor);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void setFocus(final IDMContext[] focus, RequestMonitor rm) {
|
||||
assert focus != null;
|
||||
// new Debug View thread or stack frame selection
|
||||
IDMContext elem = focus[0];
|
||||
|
||||
// new selection is a frame?
|
||||
if (elem instanceof IFrameDMContext) {
|
||||
final IFrameDMContext finalFrameCtx = (IFrameDMContext)elem;
|
||||
|
||||
setFrameFocus(finalFrameCtx, new ImmediateRequestMonitor(rm) {
|
||||
@Override
|
||||
public void handleSuccess() {
|
||||
// update the current focus, to match new GDB focus
|
||||
fCurrentGDBFocus = finalFrameCtx;
|
||||
rm.done();
|
||||
}
|
||||
});
|
||||
}
|
||||
// new selection is a thread?
|
||||
else if (elem instanceof IMIExecutionDMContext) {
|
||||
final IMIExecutionDMContext finalThreadCtx = (IMIExecutionDMContext)elem;
|
||||
|
||||
setThreadFocus(finalThreadCtx, new ImmediateRequestMonitor(rm) {
|
||||
@Override
|
||||
protected void handleSuccess() {
|
||||
// update the current focus, to match new GDB focus
|
||||
fCurrentGDBFocus = finalThreadCtx;
|
||||
rm.done();
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
assert false;
|
||||
rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID,
|
||||
INVALID_HANDLE, "Invalid context to set focus to", null)); //$NON-NLS-1$);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
protected void setThreadFocus(IMIExecutionDMContext newThread, RequestMonitor rm) {
|
||||
if (newThread == null) {
|
||||
rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID,
|
||||
INVALID_HANDLE, "GdbFocusSynchronizer unable to resolve thread context for the selected element", null)); //$NON-NLS-1$
|
||||
return;
|
||||
}
|
||||
|
||||
// Create a mi-thread-select and send the command
|
||||
ICommand<MIInfo> command = fCommandFactory.createMIThreadSelect(fGdbcontrol.getContext(), newThread.getThreadId());
|
||||
fGdbcontrol.queueCommand(command, new ImmediateDataRequestMonitor<MIInfo> (rm));
|
||||
}
|
||||
|
||||
protected void setFrameFocus(IFrameDMContext newFrame, RequestMonitor rm) {
|
||||
if (newFrame == null) {
|
||||
rm.done(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID,
|
||||
INVALID_HANDLE, "GdbFocusSynchronizer unable to resolve frame context for the selected element", null)); //$NON-NLS-1$
|
||||
return;
|
||||
}
|
||||
|
||||
// We must specify the thread for which we want to set the frame in the -stack-select-frame command
|
||||
IMIExecutionDMContext threadDmc = DMContexts.getAncestorOfType(newFrame, IMIExecutionDMContext.class);
|
||||
if (isThreadSuspended(threadDmc)) {
|
||||
// Create a mi-stack-select-frame and send the command
|
||||
ICommand<MIInfo> command = fCommandFactory.createMIStackSelectFrame(threadDmc, newFrame.getLevel());
|
||||
fGdbcontrol.queueCommand(command, new ImmediateDataRequestMonitor<MIInfo>(rm));
|
||||
}
|
||||
else {
|
||||
rm.done();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isThreadSuspended(IExecutionDMContext ctx) {
|
||||
assert ctx != null;
|
||||
IRunControl runControl = getServicesTracker().getService(IRunControl.class);
|
||||
if (runControl != null) {
|
||||
return runControl.isSuspended(ctx);
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses gdb output for the =thread-selected notification.
|
||||
* When this is detected, generate a DSF event to notify listeners
|
||||
*
|
||||
* example :
|
||||
* =thread-selected,id="7",frame={level="0",addr="0x000000000041eab0",func="main",args=[]}
|
||||
*/
|
||||
@Override
|
||||
public void eventReceived(Object output) {
|
||||
for (MIOOBRecord oobr : ((MIOutput)output).getMIOOBRecords()) {
|
||||
if (oobr instanceof MINotifyAsyncOutput) {
|
||||
MINotifyAsyncOutput out = (MINotifyAsyncOutput) oobr;
|
||||
String miEvent = out.getAsyncClass();
|
||||
if ("thread-selected".equals(miEvent)) { //$NON-NLS-1$
|
||||
// extract tid
|
||||
MIResult[] results = out.getMIResults();
|
||||
String tid = null;
|
||||
String frameLevel = null;
|
||||
for (int i = 0; i < results.length; i++) {
|
||||
String var = results[i].getVariable();
|
||||
MIValue val = results[i].getMIValue();
|
||||
|
||||
if (var.equals("frame") && val instanceof MITuple) { //$NON-NLS-1$
|
||||
// dig deeper to get the frame level
|
||||
MIResult[] res = ((MITuple)val).getMIResults();
|
||||
|
||||
for (int j = 0; j < res.length; j++) {
|
||||
var = res[j].getVariable();
|
||||
val = res[j].getMIValue();
|
||||
|
||||
if (var.equals("level")) { //$NON-NLS-1$
|
||||
if (val instanceof MIConst) {
|
||||
frameLevel = ((MIConst) val).getString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (var.equals("id")) { //$NON-NLS-1$
|
||||
if (val instanceof MIConst) {
|
||||
tid = ((MIConst) val).getString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// tid should never be null
|
||||
assert (tid != null);
|
||||
if (tid == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// update current focus
|
||||
if (frameLevel == null) {
|
||||
// thread running - current focus is a thread
|
||||
fCurrentGDBFocus = createThreadContextFromThreadId(tid);
|
||||
createAndDispatchGDBFocusChangedEvent();
|
||||
}
|
||||
else {
|
||||
// thread suspended - current focus is a stack frame
|
||||
int intFrameNum = 0;
|
||||
try {
|
||||
intFrameNum = Integer.parseInt(frameLevel);
|
||||
}
|
||||
catch (NumberFormatException e){
|
||||
GdbPlugin.log(e);
|
||||
}
|
||||
String finalTid = tid;
|
||||
fStackService.getFrames(
|
||||
createThreadContextFromThreadId(finalTid),
|
||||
intFrameNum, intFrameNum,
|
||||
new ImmediateDataRequestMonitor<IFrameDMContext[]>() {
|
||||
@Override
|
||||
protected void handleCompleted() {
|
||||
if (isSuccess() && getData().length > 0) {
|
||||
fCurrentGDBFocus = getData()[0];
|
||||
} else {
|
||||
fCurrentGDBFocus = createThreadContextFromThreadId(finalTid);
|
||||
}
|
||||
createAndDispatchGDBFocusChangedEvent();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void createAndDispatchGDBFocusChangedEvent() {
|
||||
assert fCurrentGDBFocus != null;
|
||||
|
||||
fGdbcontrol.getSession().dispatchEvent(new GDBFocusChangedEvent(fCurrentGDBFocus),
|
||||
fGdbcontrol.getProperties());
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an execution context from a thread id
|
||||
*
|
||||
* @param tid The thread id on which the execution context is based
|
||||
*/
|
||||
private IMIExecutionDMContext createThreadContextFromThreadId(String tid) {
|
||||
assert tid != null;
|
||||
|
||||
IContainerDMContext parentContainer =
|
||||
fProcesses.createContainerContextFromThreadId(fGdbcontrol.getContext(), tid);
|
||||
IProcessDMContext processDmc = DMContexts.getAncestorOfType(parentContainer, IProcessDMContext.class);
|
||||
IThreadDMContext threadDmc = fProcesses.createThreadContext(processDmc, tid);
|
||||
return fProcesses.createExecutionContext(parentContainer, threadDmc, tid);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void sessionSelected() {
|
||||
// get debug view to select this session's current thread/frame
|
||||
createAndDispatchGDBFocusChangedEvent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public IDMContext[] getFocus() {
|
||||
return new IDMContext[] { fCurrentGDBFocus };
|
||||
}
|
||||
|
||||
@DsfServiceEventHandler
|
||||
public void updateContexts(DataModelInitializedEvent event) {
|
||||
// the debug session has finished launching - update the current focus
|
||||
// to something sane. i.e. thread1 or thread1->frame0
|
||||
|
||||
IMIExecutionDMContext threadCtx = createThreadContextFromThreadId(THREAD_ID_DEFAULT);
|
||||
|
||||
if (!isThreadSuspended(threadCtx)) {
|
||||
fCurrentGDBFocus = threadCtx;
|
||||
}
|
||||
else {
|
||||
fStackService.getTopFrame(threadCtx, new ImmediateDataRequestMonitor<IFrameDMContext>() {
|
||||
@Override
|
||||
protected void handleCompleted() {
|
||||
if (isSuccess()) {
|
||||
fCurrentGDBFocus = getData();
|
||||
} else {
|
||||
fCurrentGDBFocus = threadCtx;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,53 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2016 Ericsson and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*******************************************************************************/
|
||||
package org.eclipse.cdt.dsf.gdb.internal.service;
|
||||
|
||||
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
|
||||
import org.eclipse.cdt.dsf.datamodel.IDMContext;
|
||||
import org.eclipse.cdt.dsf.datamodel.IDMEvent;
|
||||
import org.eclipse.cdt.dsf.service.IDsfService;
|
||||
|
||||
/**
|
||||
* This service keeps synchronized the CDT debug view selection and GDB's
|
||||
* internal focus - GDB's current thread, stack frame, and (implicitly) inferior.
|
||||
*
|
||||
* @since 5.2
|
||||
*/
|
||||
public interface IGDBFocusSynchronizer extends IDsfService {
|
||||
/**
|
||||
* Returns an array of contexts, representing the current synchronized focus
|
||||
*/
|
||||
IDMContext[] getFocus();
|
||||
|
||||
/**
|
||||
* Sets the service's current focus and propagate it to the GDB corresponding to this
|
||||
* service's instance, when appropriate.
|
||||
*
|
||||
* @param focus An array of IDMContext, each context representing a focus'ed element
|
||||
* from the Debug View
|
||||
* @param rm the request monitor
|
||||
*/
|
||||
void setFocus(IDMContext[] focus, RequestMonitor rm);
|
||||
|
||||
/**
|
||||
* The service sends this event to indicate that GDB has changed its focus, as a
|
||||
* result of an event not triggered by CDT. For example a console command typed by
|
||||
* the user.
|
||||
* Note: the full focus might not be reflected in the included context. The service
|
||||
* can be queried to get the complete picture.
|
||||
*/
|
||||
interface IGDBFocusChangedEvent extends IDMEvent<IDMContext> {}
|
||||
|
||||
/**
|
||||
* This tells the synchronizer that the session, corresponding to this service's
|
||||
* instance, has been selected. This can be called, for example, when a specific
|
||||
* debugger console has become active, so that the synchronizer will reflect this
|
||||
* in the Debug View selection.
|
||||
*/
|
||||
void sessionSelected();
|
||||
}
|
|
@ -30,6 +30,7 @@ import org.eclipse.cdt.dsf.debug.service.ISourceLookup.ISourceLookupDMContext;
|
|||
import org.eclipse.cdt.dsf.debug.service.IStack;
|
||||
import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService;
|
||||
import org.eclipse.cdt.dsf.gdb.service.IGDBHardwareAndOS;
|
||||
import org.eclipse.cdt.dsf.gdb.internal.service.IGDBFocusSynchronizer;
|
||||
import org.eclipse.cdt.dsf.gdb.service.IGDBTraceControl;
|
||||
import org.eclipse.cdt.dsf.mi.service.CSourceLookup;
|
||||
import org.eclipse.cdt.dsf.mi.service.IMIBackend;
|
||||
|
@ -128,6 +129,10 @@ public class ServicesLaunchSequence extends Sequence {
|
|||
public void execute(final RequestMonitor requestMonitor) {
|
||||
fLaunch.getServiceFactory().createService(MIBreakpointsSynchronizer.class, fSession).initialize(requestMonitor);
|
||||
}},
|
||||
new Step() { @Override
|
||||
public void execute(final RequestMonitor requestMonitor) {
|
||||
fLaunch.getServiceFactory().createService(IGDBFocusSynchronizer.class, fSession).initialize(requestMonitor);
|
||||
}},
|
||||
};
|
||||
|
||||
public ServicesLaunchSequence(DsfSession session, GdbLaunch launch, IProgressMonitor pm) {
|
||||
|
|
|
@ -38,6 +38,8 @@ 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.gdb.internal.service.GDBFocusSynchronizer;
|
||||
import org.eclipse.cdt.dsf.gdb.internal.service.IGDBFocusSynchronizer;
|
||||
import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch;
|
||||
import org.eclipse.cdt.dsf.gdb.launching.LaunchUtils;
|
||||
import org.eclipse.cdt.dsf.gdb.service.command.CommandFactory_6_8;
|
||||
|
@ -169,7 +171,10 @@ public class GdbDebugServicesFactory extends AbstractDsfDebugServicesFactory {
|
|||
}
|
||||
else if (MIBreakpointsSynchronizer.class.isAssignableFrom(clazz)) {
|
||||
return (V)createBreakpointsSynchronizerService(session);
|
||||
}
|
||||
}
|
||||
else if (IGDBFocusSynchronizer.class.isAssignableFrom(clazz)) {
|
||||
return (V)createFocusSynchronizerService(session);
|
||||
}
|
||||
|
||||
return super.createService(clazz, session, optionalArguments);
|
||||
}
|
||||
|
@ -369,6 +374,13 @@ public class GdbDebugServicesFactory extends AbstractDsfDebugServicesFactory {
|
|||
return new MIBreakpointsSynchronizer(session);
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 5.2
|
||||
*/
|
||||
protected IGDBFocusSynchronizer createFocusSynchronizerService(DsfSession session) {
|
||||
return new GDBFocusSynchronizer(session);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the GDB version of the current debug session with the one specified by
|
||||
* parameter 'version'. Returns -1, 0, or 1 if the current version is less than,
|
||||
|
|
|
@ -16,6 +16,7 @@ import org.eclipse.cdt.tests.dsf.gdb.tests.nonstop.MIExpressionsNonStopTest;
|
|||
import org.eclipse.cdt.tests.dsf.gdb.tests.nonstop.MIRunControlNonStopTargetAvailableTest;
|
||||
import org.eclipse.cdt.tests.dsf.gdb.tests.nonstop.OperationsWhileTargetIsRunningNonStopTest;
|
||||
import org.eclipse.cdt.tests.dsf.gdb.tests.nonstop.StepIntoSelectionNonStopTest;
|
||||
import org.eclipse.cdt.tests.dsf.gdb.tests.nonstop.ThreadStackFrameSyncTest;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.Suite;
|
||||
|
@ -57,6 +58,7 @@ import org.junit.runners.Suite;
|
|||
GDBProcessesTest.class,
|
||||
PostMortemCoreTest.class,
|
||||
CommandTimeoutTest.class,
|
||||
ThreadStackFrameSyncTest.class,
|
||||
/* Add your test class here */
|
||||
})
|
||||
public class SuiteGdb {
|
||||
|
|
|
@ -0,0 +1,556 @@
|
|||
/*******************************************************************************
|
||||
* Copyright (c) 2016 Ericsson and others.
|
||||
* All rights reserved. This program and the accompanying materials
|
||||
* are made available under the terms of the Eclipse Public License v1.0
|
||||
* which accompanies this distribution, and is available at
|
||||
* http://www.eclipse.org/legal/epl-v10.html
|
||||
*******************************************************************************/
|
||||
package org.eclipse.cdt.tests.dsf.gdb.tests.nonstop;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
|
||||
import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
|
||||
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
|
||||
import org.eclipse.cdt.dsf.concurrent.ImmediateRequestMonitor;
|
||||
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.datamodel.IDMEvent;
|
||||
import org.eclipse.cdt.dsf.debug.service.IMultiRunControl;
|
||||
import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext;
|
||||
import org.eclipse.cdt.dsf.debug.service.IStack.IFrameDMContext;
|
||||
import org.eclipse.cdt.dsf.gdb.IGDBLaunchConfigurationConstants;
|
||||
import org.eclipse.cdt.dsf.gdb.internal.service.IGDBFocusSynchronizer;
|
||||
import org.eclipse.cdt.dsf.gdb.internal.service.IGDBFocusSynchronizer.IGDBFocusChangedEvent;
|
||||
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.command.commands.CLICommand;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.events.MIStoppedEvent;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.CLIThreadInfo;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.MIConsoleStreamOutput;
|
||||
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.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.MITuple;
|
||||
import org.eclipse.cdt.dsf.mi.service.command.output.MIValue;
|
||||
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.BaseParametrizedTestCase;
|
||||
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.cdt.tests.dsf.gdb.tests.ITestConstants;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Assume;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
public class ThreadStackFrameSyncTest extends BaseParametrizedTestCase {
|
||||
|
||||
|
||||
final static private int DEFAULT_TIMEOUT = 1000;
|
||||
|
||||
private DsfServicesTracker fServicesTracker;
|
||||
private IMultiRunControl fMultiRunControl;
|
||||
private IGDBControl fCommandControl;
|
||||
private IGDBFocusSynchronizer fGdbSync;
|
||||
private DsfSession fSession;
|
||||
private List<IDMEvent<? extends IDMContext>> fEventsReceived = new ArrayList<IDMEvent<? extends IDMContext>>();
|
||||
|
||||
// Breakpoint tags in MultiThread.cc
|
||||
public static final String[] LINE_TAGS = new String[] {
|
||||
"LINE_MAIN_BEFORE_THREAD_START", // Just before StartThread
|
||||
"LINE_MAIN_AFTER_THREAD_START", // Just after StartThread
|
||||
"LINE_MAIN_ALL_THREADS_STARTED", // Where all threads are guaranteed to be started.
|
||||
};
|
||||
|
||||
/*
|
||||
* Name of the executable
|
||||
*/
|
||||
private static final String EXEC_NAME = "MultiThread.exe";
|
||||
private static final String SOURCE_NAME = "MultiThread.cc";
|
||||
|
||||
@BeforeClass
|
||||
public static void beforeClass() {
|
||||
Assume.assumeTrue(supportsNonStop());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doBeforeTest() throws Exception {
|
||||
assumeGdbVersionAtLeast(ITestConstants.SUFFIX_GDB_7_12);
|
||||
super.doBeforeTest();
|
||||
|
||||
resolveLineTagLocations(SOURCE_NAME, LINE_TAGS);
|
||||
|
||||
fSession = getGDBLaunch().getSession();
|
||||
Assert.assertNotNull(fSession);
|
||||
|
||||
Runnable runnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
fServicesTracker = new DsfServicesTracker(TestsPlugin.getBundleContext(), fSession.getId());
|
||||
Assert.assertTrue(fServicesTracker != null);
|
||||
|
||||
fCommandControl = fServicesTracker.getService(IGDBControl.class);
|
||||
Assert.assertTrue(fCommandControl != null);
|
||||
|
||||
fMultiRunControl = fServicesTracker.getService(IMultiRunControl.class);
|
||||
Assert.assertTrue(fMultiRunControl != null);
|
||||
|
||||
fGdbSync = fServicesTracker.getService(IGDBFocusSynchronizer.class);
|
||||
Assert.assertTrue(fGdbSync != null);
|
||||
|
||||
IMIProcesses procService = fServicesTracker.getService(IMIProcesses.class);
|
||||
Assert.assertTrue(procService != null);
|
||||
|
||||
// Register to receive DSF events
|
||||
fSession.addServiceEventListener(ThreadStackFrameSyncTest.this, null);
|
||||
}
|
||||
};
|
||||
fSession.getExecutor().submit(runnable).get();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void setLaunchAttributes() {
|
||||
super.setLaunchAttributes();
|
||||
|
||||
setLaunchAttribute(ICDTLaunchConfigurationConstants.ATTR_PROGRAM_NAME,
|
||||
EXEC_PATH + EXEC_NAME);
|
||||
|
||||
// Multi run control only makes sense for non-stop mode
|
||||
setLaunchAttribute(IGDBLaunchConfigurationConstants.ATTR_DEBUGGER_NON_STOP, true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void doAfterTest() throws Exception {
|
||||
super.doAfterTest();
|
||||
|
||||
if (fSession != null) {
|
||||
fSession.getExecutor().submit(() -> fSession.removeServiceEventListener(ThreadStackFrameSyncTest.this))
|
||||
.get();
|
||||
}
|
||||
fEventsReceived.clear();
|
||||
|
||||
if (fServicesTracker!=null) fServicesTracker.dispose();
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// Start of tests
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
/**
|
||||
* This test verifies that changing the active thread, in GDB, in CLI,
|
||||
* triggers a GDB notification that a new thread has been selected.
|
||||
*/
|
||||
@Test
|
||||
public void testChangingCurrentThreadCLINotification() throws Throwable {
|
||||
ServiceEventWaitor<MIStoppedEvent> eventWaitor =
|
||||
new ServiceEventWaitor<MIStoppedEvent>(fMultiRunControl.getSession(), MIStoppedEvent.class);
|
||||
|
||||
// add a breakpoint in main
|
||||
SyncUtil.addBreakpoint(SOURCE_NAME + ":" + getLineForTag("LINE_MAIN_ALL_THREADS_STARTED"), false);
|
||||
// add a breakpoint in thread code
|
||||
SyncUtil.addBreakpoint("36", false);
|
||||
// Run program
|
||||
SyncUtil.resumeAll();
|
||||
|
||||
eventWaitor.waitForEvent(TestsPlugin.massageTimeout(2000)); // Wait for first thread to stop
|
||||
eventWaitor.waitForEvent(TestsPlugin.massageTimeout(2000)); // Wait for second thread to stop
|
||||
eventWaitor.waitForEvent(TestsPlugin.massageTimeout(2000));
|
||||
eventWaitor.waitForEvent(TestsPlugin.massageTimeout(2000));
|
||||
eventWaitor.waitForEvent(TestsPlugin.massageTimeout(2000));
|
||||
|
||||
// *** at this point all 5 threads should be stopped
|
||||
|
||||
// Try some thread switching - SwitchThreadAndCaptureThreadSwitchedEvent will
|
||||
// capture the "=thread-selected" event and return the newly selected thread
|
||||
// for us to compare to what we ordered
|
||||
for (int i = 0; i < 2; i++) {
|
||||
assertEquals("2",switchThreadAndCaptureThreadSwitchedEvent("2"));
|
||||
assertEquals("3",switchThreadAndCaptureThreadSwitchedEvent("3"));
|
||||
assertEquals("4",switchThreadAndCaptureThreadSwitchedEvent("4"));
|
||||
assertEquals("5",switchThreadAndCaptureThreadSwitchedEvent("5"));
|
||||
assertEquals("1",switchThreadAndCaptureThreadSwitchedEvent("1"));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This test verifies that changing the active frame, in GDB, in CLI,
|
||||
* triggers a GDB notification that a new frame has been selected.
|
||||
*/
|
||||
@Test
|
||||
public void testChangingCurrentFrameCLINotification() throws Throwable {
|
||||
ServiceEventWaitor<MIStoppedEvent> eventWaitor =
|
||||
new ServiceEventWaitor<MIStoppedEvent>(fMultiRunControl.getSession(), MIStoppedEvent.class);
|
||||
|
||||
// add a breakpoint in main
|
||||
SyncUtil.addBreakpoint(SOURCE_NAME + ":" + getLineForTag("LINE_MAIN_ALL_THREADS_STARTED"), false);
|
||||
// add a breakpoint in thread code
|
||||
SyncUtil.addBreakpoint("36", false);
|
||||
// Run program
|
||||
SyncUtil.resumeAll();
|
||||
|
||||
eventWaitor.waitForEvent(TestsPlugin.massageTimeout(2000)); // Wait for first thread to stop
|
||||
eventWaitor.waitForEvent(TestsPlugin.massageTimeout(2000)); // Wait for second thread to stop
|
||||
eventWaitor.waitForEvent(TestsPlugin.massageTimeout(2000));
|
||||
eventWaitor.waitForEvent(TestsPlugin.massageTimeout(2000));
|
||||
eventWaitor.waitForEvent(TestsPlugin.massageTimeout(2000));
|
||||
|
||||
// *** at this point all 5 threads should be stopped
|
||||
|
||||
// switch to a thread that has some stack frames
|
||||
assertEquals("2",switchThreadAndCaptureThreadSwitchedEvent("2"));
|
||||
|
||||
// Try some stack frame switching - SwitchFrameAndCaptureThreadSwitchedEvent will
|
||||
// capture the "=thread-selected" event and return the newly selected stack frame,
|
||||
// for us to compare to what we ordered
|
||||
for (int i = 0; i < 5; i++) {
|
||||
assertEquals("1",switchFrameAndCaptureStackFrameSwitchedEvent("1"));
|
||||
assertEquals("0",switchFrameAndCaptureStackFrameSwitchedEvent("0"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This test verifies that the GDB Synchronizer service is able to set
|
||||
* the current GDB thread
|
||||
*/
|
||||
@Test
|
||||
public void testGdbSyncServiceCanSwitchGDBThread() throws Throwable {
|
||||
ServiceEventWaitor<MIStoppedEvent> eventWaitor =
|
||||
new ServiceEventWaitor<MIStoppedEvent>(fMultiRunControl.getSession(), MIStoppedEvent.class);
|
||||
|
||||
// add a breakpoint in main
|
||||
SyncUtil.addBreakpoint(SOURCE_NAME + ":" + getLineForTag("LINE_MAIN_ALL_THREADS_STARTED"), false);
|
||||
// add a breakpoint in thread code
|
||||
SyncUtil.addBreakpoint("36", false);
|
||||
// Run program
|
||||
SyncUtil.resumeAll();
|
||||
|
||||
eventWaitor.waitForEvent(TestsPlugin.massageTimeout(2000)); // Wait for first thread to stop
|
||||
eventWaitor.waitForEvent(TestsPlugin.massageTimeout(2000)); // Wait for second thread to stop
|
||||
eventWaitor.waitForEvent(TestsPlugin.massageTimeout(2000));
|
||||
eventWaitor.waitForEvent(TestsPlugin.massageTimeout(2000));
|
||||
eventWaitor.waitForEvent(TestsPlugin.massageTimeout(2000));
|
||||
|
||||
// *** at this point all 5 threads should be stopped
|
||||
|
||||
// have the sync service set GDB current tid to thread 5
|
||||
fGdbSync.setFocus(new IDMContext[] {getContextForThreadId(5)}, new ImmediateRequestMonitor());
|
||||
assertEquals("5", getCurrentThread());
|
||||
|
||||
fGdbSync.setFocus(new IDMContext[] {getContextForThreadId(4)}, new ImmediateRequestMonitor());
|
||||
assertEquals("4", getCurrentThread());
|
||||
|
||||
fGdbSync.setFocus(new IDMContext[] {getContextForThreadId(3)}, new ImmediateRequestMonitor());
|
||||
assertEquals("3", getCurrentThread());
|
||||
|
||||
fGdbSync.setFocus(new IDMContext[] {getContextForThreadId(2)}, new ImmediateRequestMonitor());
|
||||
assertEquals("2", getCurrentThread());
|
||||
|
||||
fGdbSync.setFocus(new IDMContext[] {getContextForThreadId(1)}, new ImmediateRequestMonitor());
|
||||
assertEquals("1", getCurrentThread());
|
||||
}
|
||||
|
||||
/**
|
||||
* This test verifies that the GDB Synchronizer service is able to set
|
||||
* the current GDB stack frame
|
||||
*/
|
||||
@Test
|
||||
public void testGdbSyncServiceCanSwitchGDBStackFrame() throws Throwable {
|
||||
ServiceEventWaitor<MIStoppedEvent> eventWaitor =
|
||||
new ServiceEventWaitor<MIStoppedEvent>(fMultiRunControl.getSession(), MIStoppedEvent.class);
|
||||
|
||||
// add a breakpoint in main
|
||||
SyncUtil.addBreakpoint(SOURCE_NAME + ":" + getLineForTag("LINE_MAIN_ALL_THREADS_STARTED"), false);
|
||||
// add a breakpoint in thread code
|
||||
SyncUtil.addBreakpoint("36", false);
|
||||
// Run program
|
||||
SyncUtil.resumeAll();
|
||||
|
||||
eventWaitor.waitForEvent(TestsPlugin.massageTimeout(2000)); // Wait for first thread to stop
|
||||
eventWaitor.waitForEvent(TestsPlugin.massageTimeout(2000)); // Wait for second thread to stop
|
||||
eventWaitor.waitForEvent(TestsPlugin.massageTimeout(2000));
|
||||
eventWaitor.waitForEvent(TestsPlugin.massageTimeout(2000));
|
||||
eventWaitor.waitForEvent(TestsPlugin.massageTimeout(2000));
|
||||
|
||||
// *** at this point all 5 threads should be stopped
|
||||
|
||||
final IFrameDMContext frame1 = SyncUtil.getStackFrame(1, 1);
|
||||
final IFrameDMContext frame0 = SyncUtil.getStackFrame(1, 0);
|
||||
|
||||
// do a few of times
|
||||
for (int i = 0; i < 50; i++) {
|
||||
// have the sync service switch stack frame to 1
|
||||
fSession.getExecutor().execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
fGdbSync.setFocus(new IDMContext[] {frame1}, new ImmediateRequestMonitor());
|
||||
}
|
||||
});
|
||||
Thread.sleep(100);
|
||||
assertEquals("1", getCurrentStackFrameLevel());
|
||||
|
||||
// have the sync service switch stack frame to 0
|
||||
fSession.getExecutor().execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
fGdbSync.setFocus(new IDMContext[] {frame0}, new ImmediateRequestMonitor());
|
||||
}
|
||||
});
|
||||
Thread.sleep(100);
|
||||
assertEquals("0", getCurrentStackFrameLevel());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
// End of tests
|
||||
//////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// SyncUtil.getExecutionContext() takes the index of the
|
||||
// array of all threads, so it will return a thread off by one.
|
||||
// We compensate for this in this method
|
||||
private IMIExecutionDMContext getContextForThreadId(int tid) throws InterruptedException, ExecutionException, TimeoutException {
|
||||
return SyncUtil.getExecutionContext(tid -1);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This is a wrapper around selectGdbThread(), that waits and captures the
|
||||
* expected "=thread-selected" event, and returns the thread id from it.
|
||||
* @throws Throwable
|
||||
*/
|
||||
private String switchThreadAndCaptureThreadSwitchedEvent(String tid) throws Throwable {
|
||||
Thread.sleep(100);
|
||||
fEventsReceived.clear();
|
||||
selectGdbThread(tid);
|
||||
|
||||
IDMContext ctx = waitForEvent(IGDBFocusChangedEvent.class).getDMContext();
|
||||
if (ctx instanceof IMIExecutionDMContext) {
|
||||
IMIExecutionDMContext execDmc = (IMIExecutionDMContext) ctx;
|
||||
return execDmc.getThreadId();
|
||||
}
|
||||
else if (ctx instanceof IFrameDMContext) {
|
||||
IMIExecutionDMContext execDmc = DMContexts.getAncestorOfType(ctx, IMIExecutionDMContext.class);
|
||||
return execDmc.getThreadId();
|
||||
}
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
/**
|
||||
* Waits and captures the expected "=thread-selected" event, and returns the frame id from it.
|
||||
* @throws Throwable
|
||||
*/
|
||||
private String switchFrameAndCaptureStackFrameSwitchedEvent(String frameLevel) throws Throwable {
|
||||
IFrameDMContext newFrame = null;
|
||||
|
||||
Thread.sleep(100);
|
||||
fEventsReceived.clear();
|
||||
selectGdbStackFrame(frameLevel);
|
||||
|
||||
Object[] elems = fGdbSync.getFocus();
|
||||
for (Object elem : elems) {
|
||||
if (elem instanceof IFrameDMContext) {
|
||||
newFrame = (IFrameDMContext)elem;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return newFrame != null ? Integer.toString(newFrame.getLevel()) : null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Changes the current thread, using the CLI command "thread <tid>"
|
||||
* @param tid: the thread id of the thread to switch-to. If empty,
|
||||
* the command will simply report the current thread.
|
||||
* @return the tid of the (possibly newly) currently selected gdb thread
|
||||
* @throws Exception
|
||||
*/
|
||||
private String sendCLIThread(String tid) throws Exception {
|
||||
IContainerDMContext containerDmc = SyncUtil.getContainerContext();
|
||||
|
||||
Query<CLIThreadInfo> query = new Query<CLIThreadInfo>() {
|
||||
@Override
|
||||
protected void execute(DataRequestMonitor<CLIThreadInfo> rm) {
|
||||
fCommandControl.queueCommand(new CLICommand<CLIThreadInfo>(containerDmc,"thread " + tid) {
|
||||
@Override
|
||||
public CLIThreadInfo getResult(MIOutput output) {
|
||||
return new CLIThreadInfo(output);
|
||||
}
|
||||
}, rm);
|
||||
}
|
||||
};
|
||||
|
||||
fCommandControl.getExecutor().execute(query);
|
||||
CLIThreadInfo info = query.get();
|
||||
|
||||
return info.getCurrentThread();
|
||||
}
|
||||
|
||||
private String getCurrentThread() throws Exception {
|
||||
return sendCLIThread("");
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the current stack frame, using the CLI command "frame <level>". Then parses
|
||||
* the output to extract the current frame.
|
||||
* @param level the frame level wanted. If empty, the command will report the current level
|
||||
* @return newly set level.
|
||||
* @throws Exception
|
||||
*/
|
||||
private String sendCLIFrame(String level) throws Exception {
|
||||
IContainerDMContext containerDmc = SyncUtil.getContainerContext();
|
||||
|
||||
Query<MIInfo> query = new Query<MIInfo>() {
|
||||
@Override
|
||||
protected void execute(DataRequestMonitor<MIInfo> rm) {
|
||||
fCommandControl.queueCommand(new CLICommand<MIInfo>(containerDmc,"frame " + level) {
|
||||
@Override
|
||||
public CLIThreadInfo getResult(MIOutput output) {
|
||||
return new CLIThreadInfo(output);
|
||||
}
|
||||
}, rm);
|
||||
}
|
||||
};
|
||||
|
||||
fCommandControl.getExecutor().execute(query);
|
||||
|
||||
String frameLevel = null;
|
||||
for (MIOOBRecord oobr : query.get().getMIOutput().getMIOOBRecords()) {
|
||||
// if frame changed, we'll get this printout:
|
||||
if (oobr instanceof MINotifyAsyncOutput) {
|
||||
// example of output:
|
||||
// =thread-selected,id="2",frame={level="1",addr="0x00007ffff7bc4184",func="start_thread",args=[],from="/lib/x86_64-linux-gnu/libpthread.so.0"}
|
||||
MINotifyAsyncOutput out = (MINotifyAsyncOutput) oobr;
|
||||
String miEvent = out.getAsyncClass();
|
||||
if ("thread-selected".equals(miEvent)) { //$NON-NLS-1$
|
||||
// parse =thread-selected to extract current stack frame
|
||||
MIResult[] results = out.getMIResults();
|
||||
for (int i = 0; i < results.length; i++) {
|
||||
String var = results[i].getVariable();
|
||||
MIValue val = results[i].getMIValue();
|
||||
if (var.equals("frame") && val instanceof MITuple) { //$NON-NLS-1$
|
||||
// dig deeper to get the frame level
|
||||
MIResult[] res = ((MITuple)val).getMIResults();
|
||||
for (int j = 0; j < res.length; j++) {
|
||||
var = res[j].getVariable();
|
||||
val = res[j].getMIValue();
|
||||
if (var.equals("level")) { //$NON-NLS-1$
|
||||
if (val instanceof MIConst) {
|
||||
frameLevel = ((MIConst) val).getString();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// if frame command was not given a parameter or the parameter is already
|
||||
// the current frame, we'll get this version of the printout:
|
||||
else if (oobr instanceof MIConsoleStreamOutput) {
|
||||
// example of output (here frame = 0):
|
||||
// ~"#0 main (argc=1 ...
|
||||
String printout = ((MIConsoleStreamOutput) oobr).getCString();
|
||||
int index1 = printout.indexOf('#');
|
||||
int index2 = printout.indexOf(' ');
|
||||
if (index1 != -1 && index2 != -1) {
|
||||
frameLevel = printout.substring(index1 + 1, index2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return frameLevel;
|
||||
}
|
||||
|
||||
private String getCurrentStackFrameLevel() throws Throwable {
|
||||
return sendCLIFrame("");
|
||||
}
|
||||
|
||||
|
||||
@DsfServiceEventHandler
|
||||
public void eventDispatched(IDMEvent<? extends IDMContext> e) {
|
||||
synchronized(this) {
|
||||
fEventsReceived.add(e);
|
||||
notifyAll();
|
||||
}
|
||||
}
|
||||
|
||||
private void selectGdbThread(String tid) throws Throwable {
|
||||
queueConsoleCommand(String.format("thread %s", tid));
|
||||
}
|
||||
|
||||
private void selectGdbStackFrame(String frameLevel) throws Throwable {
|
||||
queueConsoleCommand(String.format("frame %s", frameLevel));
|
||||
}
|
||||
|
||||
private void queueConsoleCommand(String command) throws Throwable {
|
||||
queueConsoleCommand(command, TestsPlugin.massageTimeout(DEFAULT_TIMEOUT), TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
||||
private void queueConsoleCommand(final String command, int timeout, TimeUnit unit) throws Throwable {
|
||||
Query<MIInfo> query = new Query<MIInfo>() {
|
||||
@Override
|
||||
protected void execute(DataRequestMonitor<MIInfo> rm) {
|
||||
fCommandControl.queueCommand(
|
||||
fCommandControl.getCommandFactory().createMIInterpreterExecConsole(
|
||||
fCommandControl.getContext(),
|
||||
command),
|
||||
rm);
|
||||
}
|
||||
};
|
||||
fSession.getExecutor().execute(query);
|
||||
query.get(timeout, unit);
|
||||
}
|
||||
|
||||
private <V extends IDMEvent<? extends IDMContext>> V waitForEvent(Class<V> eventType) throws Exception {
|
||||
return waitForEvent(eventType, TestsPlugin.massageTimeout(DEFAULT_TIMEOUT));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private <V extends IDMEvent<? extends IDMContext>> V waitForEvent(Class<V> eventType, int timeout) throws Exception {
|
||||
IDMEvent<?> event = getEvent(eventType);
|
||||
if (event == null) {
|
||||
synchronized(this) {
|
||||
try {
|
||||
wait(timeout);
|
||||
}
|
||||
catch (InterruptedException ex) {
|
||||
}
|
||||
}
|
||||
event = getEvent(eventType);
|
||||
if (event == null) {
|
||||
throw new Exception(String.format("Timed out waiting for '%s' to occur.", eventType.getName()));
|
||||
}
|
||||
}
|
||||
return (V)event;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private synchronized <V extends IDMEvent<? extends IDMContext>> V getEvent(Class<V> eventType) {
|
||||
for (IDMEvent<?> e : fEventsReceived) {
|
||||
if (eventType.isAssignableFrom(e.getClass())) {
|
||||
fEventsReceived.remove(e);
|
||||
return (V)e;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -10,6 +10,7 @@
|
|||
*******************************************************************************/
|
||||
package org.eclipse.cdt.dsf.ui.viewmodel.datamodel;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.RejectedExecutionException;
|
||||
|
||||
import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor;
|
||||
|
@ -27,6 +28,7 @@ import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMContext;
|
|||
import org.eclipse.cdt.dsf.ui.viewmodel.AbstractVMNode;
|
||||
import org.eclipse.cdt.dsf.ui.viewmodel.IVMContext;
|
||||
import org.eclipse.cdt.dsf.ui.viewmodel.IVMNode;
|
||||
import org.eclipse.cdt.dsf.ui.viewmodel.VMChildrenUpdate;
|
||||
import org.eclipse.cdt.dsf.ui.viewmodel.VMDelta;
|
||||
import org.eclipse.core.runtime.IStatus;
|
||||
import org.eclipse.core.runtime.Status;
|
||||
|
@ -347,4 +349,39 @@ abstract public class AbstractDMVMNode extends AbstractVMNode implements IVMNode
|
|||
|
||||
return retVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method looks for a specific DMC, used in a IVMNode type. If found, its index is returned, else
|
||||
* index 0.
|
||||
*
|
||||
* @param nodeType the node to search on
|
||||
* @param wantedCtx the dmc we are looking-for
|
||||
* @param parentDelta delta for the parent VMNode
|
||||
* @param rm request monitor
|
||||
*/
|
||||
protected void getVMCIndexForDmc(IVMNode nodetype, IDMContext wantedCtx, VMDelta parentDelta, DataRequestMonitor<Integer> rm) {
|
||||
final int indexFailed = 0;
|
||||
getVMProvider().updateNode(nodetype, new VMChildrenUpdate(
|
||||
parentDelta, getVMProvider().getPresentationContext(), -1, -1,
|
||||
new DataRequestMonitor<List<Object>>(getExecutor(), rm) {
|
||||
@Override
|
||||
protected void handleSuccess() {
|
||||
boolean found = false;
|
||||
for (int i = 0; i < getData().size(); i++) {
|
||||
if (getData().get(i) instanceof IDMVMContext) {
|
||||
IDMVMContext vmc = (IDMVMContext)getData().get(i);
|
||||
if (vmc.getDMContext().equals(wantedCtx)) {
|
||||
rm.setData(i);
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
rm.setData(indexFailed);
|
||||
}
|
||||
rm.done();
|
||||
}
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
Manifest-Version: 1.0
|
||||
Manifest-Version: 1.02.8.0-SNAPSHOT
|
||||
Bundle-ManifestVersion: 2
|
||||
Bundle-Name: %pluginName
|
||||
Bundle-Vendor: %providerName
|
||||
|
|
Loading…
Add table
Reference in a new issue