| /******************************************************************************* |
| * Copyright (c) 2010, 2015 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 |
| * Andy Jin (QNX) - Not output thread osId as a string when it is null (Bug 397039) |
| * Alvaro Sanchez-Leon - Bug 451396 - Improve extensibility to process MI "-thread-info" results |
| * Simon Marchi (Ericsson) - Bug 378154 - Pass thread name from MIThread to the data model |
| * Marc Khouzam (Ericsson) - Support for exited processes in the debug view (bug 407340) |
| *******************************************************************************/ |
| package org.eclipse.cdt.dsf.gdb.service; |
| |
| import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; |
| import org.eclipse.cdt.dsf.concurrent.ImmediateDataRequestMonitor; |
| import org.eclipse.cdt.dsf.concurrent.ImmediateRequestMonitor; |
| import org.eclipse.cdt.dsf.concurrent.Immutable; |
| 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.IContainerResumedDMEvent; |
| import org.eclipse.cdt.dsf.debug.service.IRunControl.IContainerSuspendedDMEvent; |
| import org.eclipse.cdt.dsf.debug.service.IRunControl.IExitedDMEvent; |
| import org.eclipse.cdt.dsf.debug.service.IRunControl.IResumedDMEvent; |
| import org.eclipse.cdt.dsf.debug.service.IRunControl.IStartedDMEvent; |
| import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent; |
| import org.eclipse.cdt.dsf.debug.service.command.BufferedCommandControl; |
| import org.eclipse.cdt.dsf.debug.service.command.CommandCache; |
| import org.eclipse.cdt.dsf.debug.service.command.ICommandControlService.ICommandControlDMContext; |
| import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; |
| import org.eclipse.cdt.dsf.gdb.service.command.IGDBControl; |
| import org.eclipse.cdt.dsf.mi.service.IMICommandControl; |
| import org.eclipse.cdt.dsf.mi.service.IMIProcessDMContext; |
| import org.eclipse.cdt.dsf.mi.service.command.CommandFactory; |
| import org.eclipse.cdt.dsf.mi.service.command.output.MIListThreadGroupsInfo; |
| import org.eclipse.cdt.dsf.mi.service.command.output.MIListThreadGroupsInfo.IThreadGroupInfo; |
| import org.eclipse.cdt.dsf.mi.service.command.output.MIThread; |
| import org.eclipse.cdt.dsf.mi.service.command.output.MIThreadInfoInfo; |
| 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; |
| |
| /** |
| * This class implements the IProcesses interface for GDB 7.1 |
| * which provides new information about cores for threads and processes. |
| * |
| * @since 4.0 |
| */ |
| public class GDBProcesses_7_1 extends GDBProcesses_7_0 { |
| |
| @Immutable |
| protected static class MIThreadDMData_7_1 extends MIThreadDMData implements IGdbThreadDMData { |
| final String[] fCores; |
| |
| public MIThreadDMData_7_1(String name, String id, String[] cores) { |
| super(name, id); |
| fCores = cores; |
| } |
| |
| @Override |
| public String[] getCores() { |
| return fCores; |
| } |
| |
| @Override |
| public String getOwner() { |
| return null; |
| } |
| } |
| |
| private CommandFactory fCommandFactory; |
| // This cache is used when we send command to get the cores. |
| // The value of the cores can change at any time, but we provide |
| // an updated value whenever there is a suspended event. |
| private CommandCache fCommandForCoresCache; |
| private IGDBControl fCommandControl; |
| |
| public GDBProcesses_7_1(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) { |
| fCommandControl = getServicesTracker().getService(IGDBControl.class); |
| |
| // This caches stores the result of a command when received; also, this cache |
| // is manipulated when receiving events. Currently, events are received after |
| // three scheduling of the executor, while command results after only one. This |
| // can cause problems because command results might be processed before an event |
| // that actually arrived before the command result. |
| // To solve this, we use a bufferedCommandControl that will delay the command |
| // result by two scheduling of the executor. |
| // See bug 280461 |
| fCommandForCoresCache = new CommandCache(getSession(), |
| new BufferedCommandControl(fCommandControl, getExecutor(), 2)); |
| fCommandForCoresCache.setContextAvailable(fCommandControl.getContext(), true); |
| |
| fCommandFactory = getServicesTracker().getService(IMICommandControl.class).getCommandFactory(); |
| getSession().addServiceEventListener(this, null); |
| |
| requestMonitor.done(); |
| } |
| |
| @Override |
| public void shutdown(RequestMonitor requestMonitor) { |
| getSession().removeServiceEventListener(this); |
| |
| super.shutdown(requestMonitor); |
| } |
| |
| @Override |
| public void getExecutionData(final IThreadDMContext dmc, final DataRequestMonitor<IThreadDMData> rm) { |
| if (dmc instanceof IMIProcessDMContext) { |
| // Starting with GDB 7.1, we can obtain the list of cores a process is currently |
| // running on (each core that has a thread of that process). |
| // We have to use -list-thread-groups to obtain that information |
| // Note that -list-thread-groups does not show the 'user' field |
| super.getExecutionData(dmc, new ImmediateDataRequestMonitor<IThreadDMData>(rm) { |
| @Override |
| protected void handleSuccess() { |
| final IThreadDMData firstLevelData = getData(); |
| |
| // No need to go further if we are dealing with an exited process |
| if (firstLevelData instanceof IGdbThreadExitedDMData) { |
| rm.done(firstLevelData); |
| return; |
| } |
| |
| ICommandControlDMContext controlDmc = DMContexts.getAncestorOfType(dmc, |
| ICommandControlDMContext.class); |
| final String groupId = getGroupFromPid(((IMIProcessDMContext) dmc).getProcId()); |
| |
| fCommandForCoresCache.execute(fCommandFactory.createMIListThreadGroups(controlDmc), |
| new ImmediateDataRequestMonitor<MIListThreadGroupsInfo>(rm) { |
| @Override |
| protected void handleCompleted() { |
| String[] cores = null; |
| if (isSuccess()) { |
| IThreadGroupInfo[] groups = getData().getGroupList(); |
| if (groups != null) { |
| for (IThreadGroupInfo group : groups) { |
| if (group.getGroupId().equals(groupId)) { |
| cores = group.getCores(); |
| break; |
| } |
| } |
| } |
| } |
| rm.setData(new MIThreadDMData_7_1(firstLevelData.getName(), firstLevelData.getId(), |
| cores)); |
| rm.done(); |
| } |
| }); |
| } |
| }); |
| } else if (dmc instanceof MIThreadDMC) { |
| // Starting with GDB 7.1, we can obtain the core on which a thread |
| // is currently located. The info is a new field in -thread-info |
| final MIThreadDMC threadDmc = (MIThreadDMC) dmc; |
| |
| ICommandControlDMContext controlDmc = DMContexts.getAncestorOfType(dmc, ICommandControlDMContext.class); |
| fCommandForCoresCache.execute(fCommandFactory.createMIThreadInfo(controlDmc, threadDmc.getId()), |
| new ImmediateDataRequestMonitor<MIThreadInfoInfo>(rm) { |
| @Override |
| protected void handleSuccess() { |
| IThreadDMData threadData = null; |
| if (getData().getThreadList().length != 0) { |
| MIThread thread = getData().getThreadList()[0]; |
| if (thread.getThreadId().equals(threadDmc.getId())) { |
| threadData = createThreadDMData(thread); |
| } |
| } |
| |
| if (threadData != null) { |
| rm.setData(threadData); |
| } else { |
| rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, |
| "Could not get thread info", null)); //$NON-NLS-1$ |
| } |
| rm.done(); |
| } |
| }); |
| } else { |
| rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, INVALID_HANDLE, "Invalid DMC type", null)); //$NON-NLS-1$ |
| rm.done(); |
| } |
| } |
| |
| /** |
| * @since 4.6 |
| */ |
| protected IGdbThreadDMData createThreadDMData(MIThread thread) { |
| String id = ""; //$NON-NLS-1$ |
| |
| if (thread.getOsId() != null) { |
| id = thread.getOsId(); |
| } |
| // append thread details (if any) to the thread ID |
| // as for GDB 6.x with CLIInfoThreadsInfo#getOsId() |
| final String details = thread.getDetails(); |
| if (details != null && !details.isEmpty()) { |
| if (!id.isEmpty()) |
| id += " "; //$NON-NLS-1$ |
| id += "(" + details + ")"; //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| // We must indicate and empty id by using null |
| if (id.isEmpty()) |
| id = null; |
| |
| String name = thread.getName(); |
| String core = thread.getCore(); |
| return new MIThreadDMData_7_1(name == null ? "" : name, id, core == null ? null : new String[] { core }); //$NON-NLS-1$ |
| } |
| |
| @DsfServiceEventHandler |
| public void eventDispatched_7_1(IResumedDMEvent e) { |
| if (e instanceof IContainerResumedDMEvent) { |
| // This will happen in all-stop mode |
| fCommandForCoresCache.setContextAvailable(e.getDMContext(), false); |
| } else { |
| // This will happen in non-stop mode |
| // Keep target available for Container commands |
| } |
| } |
| |
| // Something has suspended, core allocation could have changed |
| // during the time it was running. |
| @DsfServiceEventHandler |
| public void eventDispatched_7_1(ISuspendedDMEvent e) { |
| if (e instanceof IContainerSuspendedDMEvent) { |
| // This will happen in all-stop mode |
| fCommandForCoresCache.setContextAvailable(fCommandControl.getContext(), true); |
| } else { |
| // This will happen in non-stop mode |
| } |
| |
| fCommandForCoresCache.reset(); |
| } |
| |
| // Event handler when a thread or threadGroup starts, core allocation |
| // could have changed |
| @DsfServiceEventHandler |
| public void eventDispatched_7_1(IStartedDMEvent e) { |
| fCommandForCoresCache.reset(); |
| } |
| |
| // Event handler when a thread or a threadGroup exits, core allocation |
| // could have changed |
| @DsfServiceEventHandler |
| public void eventDispatched_7_1(IExitedDMEvent e) { |
| fCommandForCoresCache.reset(); |
| } |
| |
| @Override |
| public void flushCache(IDMContext context) { |
| fCommandForCoresCache.reset(context); |
| super.flushCache(context); |
| } |
| } |