blob: 71d46a6f22d9d9507e3a149acec3dc23f6666a91 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2008, 2014 Ericsson and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Ericsson - initial API and implementation
* Anton Gorenkov - Need to use a process factory (Bug 210366)
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.service;
import java.io.IOException;
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.debug.core.CDebugUtils;
import org.eclipse.cdt.debug.core.ICDTLaunchConfigurationConstants;
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.DsfExecutor;
import org.eclipse.cdt.dsf.concurrent.ImmediateDataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor;
import org.eclipse.cdt.dsf.concurrent.ImmediateRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.concurrent.Sequence;
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.IMemory.IMemoryDMContext;
import org.eclipse.cdt.dsf.debug.service.IProcesses;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerDMContext;
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.ICommand;
import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext;
import org.eclipse.cdt.dsf.gdb.IGdbDebugConstants;
import org.eclipse.cdt.dsf.gdb.IGdbDebugPreferenceConstants;
import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin;
import org.eclipse.cdt.dsf.gdb.launching.InferiorRuntimeProcess;
import org.eclipse.cdt.dsf.gdb.launching.LaunchUtils;
import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl;
import org.eclipse.cdt.dsf.mi.service.IMICommandControl;
import org.eclipse.cdt.dsf.mi.service.IMIContainerDMContext;
import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext;
import org.eclipse.cdt.dsf.mi.service.IMIProcessDMContext;
import org.eclipse.cdt.dsf.mi.service.IMIProcesses;
import org.eclipse.cdt.dsf.mi.service.IMIRunControl;
import org.eclipse.cdt.dsf.mi.service.MIBreakpointsManager;
import org.eclipse.cdt.dsf.mi.service.MIProcesses;
import org.eclipse.cdt.dsf.mi.service.command.CommandFactory;
import org.eclipse.cdt.dsf.mi.service.command.MIInferiorProcess;
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.MIInfo;
import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
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.Platform;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.model.IProcess;
import org.osgi.framework.BundleContext;
public class GDBProcesses extends MIProcesses implements IGDBProcesses {
private class GDBContainerDMC extends MIContainerDMC implements IMemoryDMContext {
public GDBContainerDMC(String sessionId, IProcessDMContext processDmc, String groupId) {
super(sessionId, processDmc, groupId);
}
}
private IGDBControl fGdb;
private IGDBBackend fBackend;
private CommandFactory fCommandFactory;
// Indicates if we are currently connected to an inferior
// We only need a boolean type since we only support single process debugging
// in this version of the service
private boolean fConnected;
// A map of pid to names. It is filled when we get all the
// processes that are running
private Map<Integer, String> fProcessNames = new HashMap<>();
// Id of our process. Currently, we only know it for an attach session.
private String fProcId;
// If we can use a PTY, we store it here
private PTY fPty;
public GDBProcesses(DsfSession session) {
super(session);
}
@Override
public void initialize(final RequestMonitor requestMonitor) {
super.initialize(new ImmediateRequestMonitor(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);
fBackend = getServicesTracker().getService(IGDBBackend.class);
fCommandFactory = getServicesTracker().getService(IMICommandControl.class).getCommandFactory();
// Register this service.
register(new String[] { IProcesses.class.getName(), IMIProcesses.class.getName(), IGDBProcesses.class.getName(),
MIProcesses.class.getName(), GDBProcesses.class.getName() }, new Hashtable<String, String>());
getSession().addServiceEventListener(this, null);
requestMonitor.done();
}
@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();
}
@Override
public IMIContainerDMContext createContainerContext(IProcessDMContext processDmc, String groupId) {
return new GDBContainerDMC(getSession().getId(), processDmc, groupId);
}
/** @since 4.0 */
@Override
public IMIContainerDMContext createContainerContextFromGroupId(ICommandControlDMContext controlDmc,
String groupId) {
IProcessDMContext processDmc;
if (fProcId != null) {
processDmc = createProcessContext(controlDmc, fProcId);
} else {
processDmc = createProcessContext(controlDmc, MIProcesses.UNKNOWN_PROCESS_ID);
}
return createContainerContext(processDmc, groupId);
}
@Override
public void getExecutionData(IThreadDMContext dmc, DataRequestMonitor<IThreadDMData> 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) {
pidStr = fProcId;
}
int pid = -1;
try {
pid = Integer.parseInt(pidStr);
} catch (NumberFormatException e) {
}
String name = fProcessNames.get(pid);
if (name == null) {
// Hmm. Strange. But if the pid is our inferior's, we can just use the binary name
if (fProcId != null && Integer.parseInt(fProcId) == pid) {
name = fBackend.getProgramPath().lastSegment();
}
}
if (name == null) {
// This could happen if a process has terminated but the
// debug session is not terminated because the preference
// to keep GDB running has been selected.
name = "Unknown name"; //$NON-NLS-1$
// Until bug 305385 is fixed, the above code will not work, because
// we don't know the pid of our process unless we are in an attach session
// Therefore, we assume we are looking for our own process
name = fBackend.getProgramPath().lastSegment();
}
rm.setData(new MIThreadDMData(name, pidStr));
rm.done();
} else {
super.getExecutionData(dmc, rm);
}
}
@Override
public void isDebuggerAttachSupported(IDMContext dmc, DataRequestMonitor<Boolean> rm) {
if (fBackend.getIsAttachSession() && !fConnected) {
rm.setData(true);
} else {
rm.setData(false);
}
rm.done();
}
@Override
public void attachDebuggerToProcess(IProcessDMContext procCtx, DataRequestMonitor<IDMContext> rm) {
attachDebuggerToProcess(procCtx, null, rm);
}
/**
* @since 4.0
*/
@Override
public void attachDebuggerToProcess(final IProcessDMContext procCtx, String binaryPath,
final DataRequestMonitor<IDMContext> rm) {
final IMIContainerDMContext containerDmc = createContainerContext(procCtx, MIProcesses.UNIQUE_GROUP_ID);
DataRequestMonitor<MIInfo> attachRm = new ImmediateDataRequestMonitor<MIInfo>(rm) {
@Override
protected void handleSuccess() {
GDBProcesses.super.attachDebuggerToProcess(procCtx, new ImmediateDataRequestMonitor<IDMContext>(rm) {
@Override
protected void handleSuccess() {
// For an attach, we actually know the pid, so let's remember it
fProcId = ((IMIProcessDMContext) procCtx).getProcId();
final IDMContext ctx = getData();
rm.setData(ctx);
ImmediateExecutor.getInstance().execute(new Sequence(getExecutor(), rm) {
private Step[] fSteps = new Step[] {
// Initialize memory data for this process
new Step() {
@Override
public void execute(RequestMonitor memoryDataRm) {
IGDBMemory memory = getServicesTracker().getService(IGDBMemory.class);
IMemoryDMContext memContext = DMContexts.getAncestorOfType(ctx,
IMemoryDMContext.class);
if (memory == null || memContext == null) {
memoryDataRm.done();
return;
}
memory.initializeMemoryData(memContext, memoryDataRm);
}
},
// Start tracking breakpoints.
new Step() {
@Override
public void execute(RequestMonitor trackBpRm) {
MIBreakpointsManager bpmService = getServicesTracker()
.getService(MIBreakpointsManager.class);
IContainerDMContext containerDmc = DMContexts.getAncestorOfType(ctx,
IContainerDMContext.class);
bpmService.startTrackingBpForProcess(containerDmc, trackBpRm);
}
} };
@Override
public Step[] getSteps() {
return fSteps;
}
});
}
});
}
};
if (binaryPath != null) {
fGdb.queueCommand(fCommandFactory.createMIFileExecAndSymbols(containerDmc, binaryPath), attachRm);
return;
}
// If we get here, let's do the attach by completing the requestMonitor
attachRm.done();
}
@Override
public void canDetachDebuggerFromProcess(IDMContext dmc, DataRequestMonitor<Boolean> rm) {
if (fConnected) {
rm.setData(true);
} else {
rm.setData(false);
}
rm.done();
}
@Override
public void detachDebuggerFromProcess(IDMContext dmc, final RequestMonitor rm) {
super.detachDebuggerFromProcess(dmc, new RequestMonitor(getExecutor(), rm) {
@Override
protected void handleSuccess() {
fProcId = null;
rm.done();
}
});
}
@Override
public void debugNewProcess(IDMContext dmc, String file, Map<String, Object> attributes,
DataRequestMonitor<IDMContext> rm) {
ImmediateExecutor.getInstance()
.execute(getDebugNewProcessSequence(getExecutor(), true, dmc, file, attributes, rm));
}
/**
* Return the sequence that is to be used to create a new process.
* Allows others to extend more easily.
* @since 4.0
*/
protected Sequence getDebugNewProcessSequence(DsfExecutor executor, boolean isInitial, IDMContext dmc, String file,
Map<String, Object> attributes, DataRequestMonitor<IDMContext> rm) {
return new DebugNewProcessSequence(executor, isInitial, dmc, file, attributes, rm);
}
@Override
public void getProcessesBeingDebugged(IDMContext dmc, DataRequestMonitor<IDMContext[]> rm) {
if (fConnected) {
super.getProcessesBeingDebugged(dmc, rm);
} else {
rm.setData(new IDMContext[0]);
rm.done();
}
}
@Override
public void getRunningProcesses(IDMContext dmc, final DataRequestMonitor<IProcessDMContext[]> rm) {
final ICommandControlDMContext controlDmc = DMContexts.getAncestorOfType(dmc, ICommandControlDMContext.class);
if (fBackend.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();
IProcessInfo[] procInfos = list.getProcessList();
for (IProcessInfo procInfo : procInfos) {
fProcessNames.put(procInfo.getPid(), procInfo.getName());
}
rm.setData(makeProcessDMCs(controlDmc, procInfos));
}
rm.done();
} else {
// Pre-GDB 7.0, there is no way to list processes on a remote host
// 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 < procDmcs.length; i++) {
procDmcs[i] = createProcessContext(controlDmc, Integer.toString(processes[i].getPid()));
}
return procDmcs;
}
@Override
public void terminate(IThreadDMContext thread, final RequestMonitor rm) {
// For a core session, there is no concept of killing the inferior,
// so lets kill GDB
if (fBackend.getSessionType() == SessionType.CORE) {
fGdb.terminate(rm);
} else if (thread instanceof IMIProcessDMContext) {
getDebuggingContext(thread, new ImmediateDataRequestMonitor<IDMContext>(rm) {
@Override
protected void handleSuccess() {
if (getData() instanceof IMIContainerDMContext) {
IMIRunControl runControl = getServicesTracker().getService(IMIRunControl.class);
if (runControl != null && !runControl.isTargetAcceptingCommands()) {
fBackend.interrupt();
}
final IMIContainerDMContext container = (IMIContainerDMContext) getData();
fGdb.queueCommand(fCommandFactory.createMIInterpreterExecConsoleKill(container),
new ImmediateDataRequestMonitor<MIInfo>(rm) {
@Override
protected void handleSuccess() {
// Before GDB 7.0, we must send a container exited event ourselves
getSession().dispatchEvent(new ContainerExitedDMEvent(container),
getProperties());
rm.done();
}
});
} else {
rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR,
"Invalid process context.", null)); //$NON-NLS-1$
rm.done();
}
}
});
} else {
rm.setStatus(
new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid process context.", null)); //$NON-NLS-1$
rm.done();
}
}
/** @since 4.0 */
@Override
public IMIExecutionDMContext[] getExecutionContexts(IMIContainerDMContext containerDmc) {
assert false; // This is not being used before GDB 7.0
return null;
}
/** @since 4.0 */
@Override
public void canRestart(IContainerDMContext containerDmc, DataRequestMonitor<Boolean> rm) {
if (fBackend.getIsAttachSession() || fBackend.getSessionType() == SessionType.CORE) {
rm.setData(false);
rm.done();
return;
}
// 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 (fBackend.getSessionType() == SessionType.REMOTE) {
rm.setData(false);
rm.done();
return;
}
rm.setData(true);
rm.done();
}
/** @since 4.0 */
@Override
public void restart(IContainerDMContext containerDmc, Map<String, Object> attributes,
DataRequestMonitor<IContainerDMContext> rm) {
// Before performing the restart, check if the process is properly suspended.
// Don't need to worry about non-stop before GDB 7.0, so we can simply
// interrupt the backend, if needed
// Bug 246740
IMIRunControl runControl = getServicesTracker().getService(IMIRunControl.class);
if (runControl != null && !runControl.isTargetAcceptingCommands()) {
fBackend.interrupt();
}
// For a restart, we are given the container context of the original process. However, we want to start
// a new process with a new pid, so we should create a container for it, and not use the old container with the old pid.
// We must create the process dmc explicitly because using createContainerContextFromGroupId() may automatically insert
// the old pid.
IProcessDMContext processDmc = createProcessContext(fGdb.getContext(), MIProcesses.UNKNOWN_PROCESS_ID);
IContainerDMContext newContainerDmc = createContainerContext(processDmc, MIProcesses.UNIQUE_GROUP_ID);
startOrRestart(newContainerDmc, attributes, true, rm);
}
/** @since 4.0 */
@Override
public void start(IContainerDMContext containerDmc, Map<String, Object> attributes,
DataRequestMonitor<IContainerDMContext> rm) {
startOrRestart(containerDmc, attributes, false, rm);
}
/**
* 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.
*
* @since 4.0
*/
protected void initializeInputOutput(IContainerDMContext containerDmc, final RequestMonitor rm) {
if (fBackend.getSessionType() == SessionType.REMOTE || fBackend.getIsAttachSession()) {
// These types do not use a PTY
fPty = null;
rm.done();
} else {
// These types always use a PTY
try {
fPty = new PTY();
fPty.validateSlaveName();
// Tell GDB to use this PTY
fGdb.queueCommand(fCommandFactory.createMIInferiorTTYSet((IMIContainerDMContext) containerDmc,
fPty.getSlaveName()), new ImmediateDataRequestMonitor<MIInfo>(rm) {
@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;
rm.done();
}
});
} catch (IOException e) {
fPty = null;
rm.done();
}
}
}
/**
* @since 4.0
*/
protected void createConsole(final IContainerDMContext containerDmc, final boolean restart,
final RequestMonitor rm) {
if (fBackend.getSessionType() == SessionType.REMOTE || fBackend.getIsAttachSession()) {
// Remote or attach sessions shouldn't have a console, since the inferior is not started
// by eclipse but by gdbserver
rm.done();
return;
}
initializeInputOutput(containerDmc, new ImmediateRequestMonitor(rm) {
@Override
protected void handleSuccess() {
Process inferiorProcess;
if (fPty == null) {
inferiorProcess = new MIInferiorProcess(containerDmc, fBackend.getMIOutputStream());
} else {
inferiorProcess = new MIInferiorProcess(containerDmc, fPty);
}
final Process inferior = inferiorProcess;
final String label = fBackend.getProgramPath().lastSegment();
final ILaunch launch = (ILaunch) getSession().getModelAdapter(ILaunch.class);
// Add the inferior to the launch.
// This cannot be done on the executor or things deadlock.
DebugPlugin.getDefault().asyncExec(() -> {
if (restart) {
// For a restart, remove the old inferior
IProcess[] launchProcesses = launch.getProcesses();
for (IProcess p : launchProcesses) {
// We know there is only one inferior, so just find it.
if (p instanceof InferiorRuntimeProcess) {
launch.removeProcess(p);
break;
}
}
}
// Add the inferior
// Need to go through DebugPlugin.newProcess so that we can use
// the overrideable process factory to allow others to override.
// First set attribute to specify we want to create an inferior process.
// Bug 210366
Map<String, String> attributes = new HashMap<>();
attributes.put(IGdbDebugConstants.PROCESS_TYPE_CREATION_ATTR,
IGdbDebugConstants.INFERIOR_PROCESS_CREATION_VALUE);
IProcess runtimeInferior = DebugPlugin.newProcess(launch, inferior, label, attributes);
// Now set the inferior groupId
runtimeInferior.setAttribute(IGdbDebugConstants.INFERIOR_GROUPID_ATTR, MIProcesses.UNIQUE_GROUP_ID);
rm.done();
});
}
});
}
/**
* Insert breakpoint at entry if set, and start or restart the program.
*
* @since 4.0
*/
protected void startOrRestart(final IContainerDMContext containerDmc, final Map<String, Object> attributes,
boolean restart, final DataRequestMonitor<IContainerDMContext> requestMonitor) {
if (fBackend.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.setData(containerDmc);
requestMonitor.done();
return;
}
createConsole(containerDmc, restart, new ImmediateRequestMonitor(requestMonitor) {
@Override
protected void handleSuccess() {
final DataRequestMonitor<MIInfo> execMonitor = new DataRequestMonitor<MIInfo>(getExecutor(),
requestMonitor) {
@Override
protected void handleSuccess() {
if (fBackend.getSessionType() != SessionType.REMOTE) {
// Don't send the ContainerStarted event for a remote session because
// it has already been done by MIRunControlEventProcessor when receiving
// the ^connect
getSession().dispatchEvent(new ContainerStartedDMEvent(containerDmc), getProperties());
}
requestMonitor.setData(containerDmc);
requestMonitor.done();
}
};
final ICommand<MIInfo> execCommand;
if (useContinueCommand()) {
execCommand = fCommandFactory.createMIExecContinue(containerDmc);
} else {
execCommand = fCommandFactory.createMIExecRun(containerDmc);
}
boolean stopInMain = CDebugUtils.getAttribute(attributes,
ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN,
LaunchUtils.getStopAtMainDefault());
if (!stopInMain) {
// Just start the program.
fGdb.queueCommand(execCommand, execMonitor);
} else {
String stopSymbol = CDebugUtils.getAttribute(attributes,
ICDTLaunchConfigurationConstants.ATTR_DEBUGGER_STOP_AT_MAIN_SYMBOL,
LaunchUtils.getStopAtMainSymbolDefault());
// Insert a breakpoint at the requested stop symbol.
IBreakpointsTargetDMContext bpTarget = DMContexts.getAncestorOfType(containerDmc,
IBreakpointsTargetDMContext.class);
fGdb.queueCommand(
fCommandFactory.createMIBreakInsert(bpTarget, true, false, null, 0, stopSymbol, "0"), //$NON-NLS-1$
new DataRequestMonitor<MIBreakInsertInfo>(getExecutor(), requestMonitor) {
@Override
protected void handleSuccess() {
// After the break-insert is done, execute the -exec-run or -exec-continue command.
fGdb.queueCommand(execCommand, execMonitor);
}
});
}
}
});
}
/**
* This method indicates if we should use the -exec-continue command
* instead of the -exec-run command.
* This method can be overridden to allow for customization.
* @since 4.0
*/
protected boolean useContinueCommand() {
// When doing remote debugging, we use -exec-continue instead of -exec-run
// Restart does not apply to remote sessions
return fBackend.getSessionType() == SessionType.REMOTE;
}
@Override
@DsfServiceEventHandler
public void eventDispatched(IStartedDMEvent e) {
if (e.getDMContext() instanceof IContainerDMContext) {
fConnected = true;
}
super.eventDispatched(e);
}
@Override
@DsfServiceEventHandler
public void eventDispatched(IExitedDMEvent e) {
if (e.getDMContext() instanceof IContainerDMContext) {
fConnected = false;
if (Platform.getPreferencesService().getBoolean(GdbPlugin.PLUGIN_ID,
IGdbDebugPreferenceConstants.PREF_AUTO_TERMINATE_GDB, true, null)) {
// If the inferior finishes, let's terminate GDB
fGdb.terminate(new ImmediateRequestMonitor());
}
}
super.eventDispatched(e);
}
/**
* @since 3.0
*/
@DsfServiceEventHandler
public void eventDispatched(MIStoppedEvent e) {
// Postponed because 'info program' yields different result on different platforms.
// https://bugs.eclipse.org/bugs/show_bug.cgi?id=305385#c20
//
// // Get the PID of the inferior through gdb (if we don't have it already)
//
//
// fGdb.getInferiorProcess().update();
}
}