diff --git a/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/GDBProcesses_7_0.java b/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/GDBProcesses_7_0.java index 724abf7d03b..1492c1fa544 100644 --- a/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/GDBProcesses_7_0.java +++ b/plugins/org.eclipse.dd.gdb/src/org/eclipse/dd/gdb/internal/provisional/service/GDBProcesses_7_0.java @@ -12,6 +12,7 @@ package org.eclipse.dd.gdb.internal.provisional.service; import java.util.HashMap; import java.util.Hashtable; +import java.util.Iterator; import java.util.Map; import org.eclipse.core.runtime.IStatus; @@ -40,6 +41,7 @@ import org.eclipse.dd.dsf.debug.service.IRunControl.ISuspendedDMEvent; import org.eclipse.dd.dsf.debug.service.ISignals.ISignalsDMContext; import org.eclipse.dd.dsf.debug.service.ISourceLookup.ISourceLookupDMContext; import org.eclipse.dd.dsf.debug.service.command.CommandCache; +import org.eclipse.dd.dsf.debug.service.command.IEventListener; import org.eclipse.dd.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; import org.eclipse.dd.dsf.service.AbstractDsfService; import org.eclipse.dd.dsf.service.DsfServiceEventHandler; @@ -50,16 +52,23 @@ import org.eclipse.dd.mi.service.IMIContainerDMContext; import org.eclipse.dd.mi.service.IMIExecutionDMContext; import org.eclipse.dd.mi.service.IMIProcessDMContext; import org.eclipse.dd.mi.service.IMIProcesses; +import org.eclipse.dd.mi.service.MIProcesses; import org.eclipse.dd.mi.service.command.commands.MIListThreadGroups; import org.eclipse.dd.mi.service.command.commands.MITargetAttach; import org.eclipse.dd.mi.service.command.commands.MITargetDetach; import org.eclipse.dd.mi.service.command.commands.MIThreadInfo; import org.eclipse.dd.mi.service.command.events.MIThreadGroupCreatedEvent; import org.eclipse.dd.mi.service.command.events.MIThreadGroupExitedEvent; +import org.eclipse.dd.mi.service.command.output.MIConst; import org.eclipse.dd.mi.service.command.output.MIInfo; import org.eclipse.dd.mi.service.command.output.MIListThreadGroupsInfo; +import org.eclipse.dd.mi.service.command.output.MINotifyAsyncOutput; +import org.eclipse.dd.mi.service.command.output.MIOOBRecord; +import org.eclipse.dd.mi.service.command.output.MIOutput; +import org.eclipse.dd.mi.service.command.output.MIResult; import org.eclipse.dd.mi.service.command.output.MIThread; import org.eclipse.dd.mi.service.command.output.MIThreadInfoInfo; +import org.eclipse.dd.mi.service.command.output.MIValue; import org.eclipse.dd.mi.service.command.output.MIListThreadGroupsInfo.IThreadGroupInfo; import org.osgi.framework.BundleContext; @@ -70,7 +79,8 @@ import org.osgi.framework.BundleContext; * which really mean it supports the new -list-thread-groups command. * */ -public class GDBProcesses_7_0 extends AbstractDsfService implements IMIProcesses, ICachingService { +public class GDBProcesses_7_0 extends AbstractDsfService + implements IMIProcesses, ICachingService, IEventListener { // Below is the context hierarchy that is implemented between the // MIProcesses service and the MIRunControl service for the MI @@ -329,6 +339,11 @@ public class GDBProcesses_7_0 extends AbstractDsfService implements IMIProcesses } } + /** + * 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 @@ -379,7 +394,8 @@ public class GDBProcesses_7_0 extends AbstractDsfService implements IMIProcesses fThreadCommandCache.setContextAvailable(fCommandControl.getContext(), true); getSession().addServiceEventListener(this, null); - + fCommandControl.addEventListener(this); + // Register this service. register(new String[] { IProcesses.class.getName(), IMIProcesses.class.getName(), @@ -401,6 +417,7 @@ public class GDBProcesses_7_0 extends AbstractDsfService implements IMIProcesses public void shutdown(RequestMonitor requestMonitor) { unregister(); getSession().removeServiceEventListener(this); + fCommandControl.removeEventListener(this); super.shutdown(requestMonitor); } @@ -427,10 +444,16 @@ public class GDBProcesses_7_0 extends AbstractDsfService implements IMIProcesses } public IMIContainerDMContext createContainerContext(IProcessDMContext processDmc, - String groupId) { + 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); + } + /** * This method obtains the model data for a given IThreadDMContext object * which can represent a thread or a process. @@ -765,4 +788,80 @@ public class GDBProcesses_7_0 extends AbstractDsfService implements IMIProcesses 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/plugins/org.eclipse.dd.mi/src/org/eclipse/dd/mi/service/IMIProcesses.java b/plugins/org.eclipse.dd.mi/src/org/eclipse/dd/mi/service/IMIProcesses.java index 3e7bbf04e34..9e353a9e783 100644 --- a/plugins/org.eclipse.dd.mi/src/org/eclipse/dd/mi/service/IMIProcesses.java +++ b/plugins/org.eclipse.dd.mi/src/org/eclipse/dd/mi/service/IMIProcesses.java @@ -36,7 +36,7 @@ public interface IMIProcesses extends IProcesses IProcessDMContext createProcessContext(ICommandControlDMContext controlDmc, String pid); /** - * Create a execution context. + * Create an execution context. * * @param containerDmc The parent process debugging context * @param threadDmc The parent thread context @@ -54,5 +54,14 @@ public interface IMIProcesses extends IProcesses */ 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/plugins/org.eclipse.dd.mi/src/org/eclipse/dd/mi/service/MIProcesses.java b/plugins/org.eclipse.dd.mi/src/org/eclipse/dd/mi/service/MIProcesses.java index ce34bba6ab0..4ecfadea5d2 100644 --- a/plugins/org.eclipse.dd.mi/src/org/eclipse/dd/mi/service/MIProcesses.java +++ b/plugins/org.eclipse.dd.mi/src/org/eclipse/dd/mi/service/MIProcesses.java @@ -393,6 +393,12 @@ public class MIProcesses extends AbstractDsfService implements IMIProcesses, ICa 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. diff --git a/plugins/org.eclipse.dd.mi/src/org/eclipse/dd/mi/service/command/MIRunControlEventProcessor.java b/plugins/org.eclipse.dd.mi/src/org/eclipse/dd/mi/service/command/MIRunControlEventProcessor.java index 63dd7e61499..88dbe6469b5 100644 --- a/plugins/org.eclipse.dd.mi/src/org/eclipse/dd/mi/service/command/MIRunControlEventProcessor.java +++ b/plugins/org.eclipse.dd.mi/src/org/eclipse/dd/mi/service/command/MIRunControlEventProcessor.java @@ -183,7 +183,7 @@ public class MIRunControlEventProcessor IContainerDMContext processContainerDmc = procService.createContainerContext(procDmc, groupId); IExecutionDMContext execDmc = processContainerDmc; - if (procService != null && threadId != null) { + if (threadId != null) { IThreadDMContext threadDmc = procService.createThreadContext(procDmc, threadId); execDmc = procService.createExecutionContext(processContainerDmc, threadDmc, threadId); } diff --git a/plugins/org.eclipse.dd.mi/src/org/eclipse/dd/mi/service/command/MIRunControlEventProcessor_7_0.java b/plugins/org.eclipse.dd.mi/src/org/eclipse/dd/mi/service/command/MIRunControlEventProcessor_7_0.java index 05cde605897..3ae87afc138 100644 --- a/plugins/org.eclipse.dd.mi/src/org/eclipse/dd/mi/service/command/MIRunControlEventProcessor_7_0.java +++ b/plugins/org.eclipse.dd.mi/src/org/eclipse/dd/mi/service/command/MIRunControlEventProcessor_7_0.java @@ -11,12 +11,10 @@ *******************************************************************************/ package org.eclipse.dd.mi.service.command; -import java.util.HashMap; -import java.util.Iterator; import java.util.LinkedList; import java.util.List; -import java.util.Map; +import org.eclipse.dd.dsf.datamodel.DMContexts; import org.eclipse.dd.dsf.debug.service.IProcesses.IProcessDMContext; import org.eclipse.dd.dsf.debug.service.IProcesses.IThreadDMContext; import org.eclipse.dd.dsf.debug.service.IRunControl.IContainerDMContext; @@ -91,11 +89,6 @@ public class MIRunControlEventProcessor_7_0 private final DsfServicesTracker fServicesTracker; - /** - * 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(); - /** * Creates the event processor and registers it as listener with the debugger * control. @@ -190,22 +183,16 @@ public class MIRunControlEventProcessor_7_0 } } - if ("thread-created".equals(miEvent)) { //$NON-NLS-1$ - // 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 a groupId - if (groupId == null) { - groupId = MIProcesses.UNIQUE_GROUP_ID; - } - // Update the thread to groupId map with the new groupId - fThreadToGroupMap.put(threadId, groupId); - } else { - // It was not clear if MI would specify the groupId in the thread-exited event - if (groupId == null) { - groupId = fThreadToGroupMap.get(threadId); - } - fThreadToGroupMap.remove(threadId); - } + // 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) { @@ -245,18 +232,6 @@ public class MIRunControlEventProcessor_7_0 event = new MIThreadGroupCreatedEvent(procDmc, exec.getToken(), groupId); } else if ("thread-group-exited".equals(miEvent)) { //$NON-NLS-1$ event = new MIThreadGroupExitedEvent(procDmc, exec.getToken(), groupId); - - // 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(); - } - } - } } fCommandControl.getSession().dispatchEvent(event, fCommandControl.getProperties()); @@ -291,19 +266,26 @@ public class MIRunControlEventProcessor_7_0 return null; } - // MI does not currently provide the group-id in these events + IProcessDMContext procDmc = null; + IContainerDMContext containerDmc = null; if (groupId == null) { - groupId = fThreadToGroupMap.get(threadId); - } - IProcessDMContext procDmc = procService.createProcessContext(fControlDmc, groupId); - IContainerDMContext processContainerDmc = procService.createContainerContext(procDmc, groupId); - - IExecutionDMContext execDmc = processContainerDmc; - if (procService != null && threadId != null) { - IThreadDMContext threadDmc = procService.createThreadContext(procDmc, threadId); - execDmc = procService.createExecutionContext(processContainerDmc, threadDmc, threadId); + // 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());