blob: 36dd1a398b73a5c598c53d41380d2d974ee9cdea [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2011 QNX Software Systems and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* QNX Software Systems - Initial API and implementation
* Marc Khouzam (Ericsson) - Check for a null threadId (Bug 356463)
*******************************************************************************/
package org.eclipse.cdt.dsf.gdb.internal.ui.breakpoints;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.RejectedExecutionException;
import org.eclipse.cdt.debug.ui.CDebugUIPlugin;
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants;
import org.eclipse.cdt.dsf.concurrent.ImmediateDataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.Query;
import org.eclipse.cdt.dsf.datamodel.DMContexts;
import org.eclipse.cdt.dsf.datamodel.IDMContext;
import org.eclipse.cdt.dsf.debug.service.IDsfBreakpointExtension;
import org.eclipse.cdt.dsf.debug.service.IProcesses;
import org.eclipse.cdt.dsf.debug.service.IProcesses.IProcessDMContext;
import org.eclipse.cdt.dsf.debug.service.IProcesses.IThreadDMContext;
import org.eclipse.cdt.dsf.debug.service.IProcesses.IThreadDMData;
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.command.ICommandControlService;
import org.eclipse.cdt.dsf.gdb.internal.ui.GdbUIPlugin;
import org.eclipse.cdt.dsf.gdb.launching.GdbLaunch;
import org.eclipse.cdt.dsf.mi.service.IMIExecutionDMContext;
import org.eclipse.cdt.dsf.mi.service.IMIProcesses;
import org.eclipse.cdt.dsf.service.DsfServicesTracker;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchManager;
import org.eclipse.debug.ui.DebugUITools;
import org.eclipse.debug.ui.IDebugUIConstants;
import org.eclipse.jface.viewers.AbstractTreeViewer;
import org.eclipse.jface.viewers.CheckStateChangedEvent;
import org.eclipse.jface.viewers.CheckboxTreeViewer;
import org.eclipse.jface.viewers.ICheckStateListener;
import org.eclipse.jface.viewers.ITreeContentProvider;
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.Viewer;
import org.eclipse.swt.SWT;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
public class GdbThreadFilterEditor {
public class CheckHandler implements ICheckStateListener {
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.viewers.ICheckStateListener#checkStateChanged(org.eclipse.jface.viewers.CheckStateChangedEvent)
*/
@Override
public void checkStateChanged(CheckStateChangedEvent event) {
Object element = event.getElement();
if (element instanceof IContainerDMContext) {
checkTarget((IContainerDMContext) element, event.getChecked());
} else if (element instanceof IExecutionDMContext) {
checkThread((IExecutionDMContext) element, event.getChecked());
}
}
/**
* Check or uncheck a debug target in the tree viewer. When a debug
* target is checked, attempt to check all of the target's threads by
* default. When a debug target is unchecked, uncheck all its threads.
*/
protected void checkTarget(IContainerDMContext target, boolean checked) {
getThreadViewer().setChecked(target, checked);
getThreadViewer().setGrayed(target, false);
getThreadViewer().expandToLevel(target, AbstractTreeViewer.ALL_LEVELS);
IExecutionDMContext[] threads = syncGetThreads(target);
for (int i = 0; i < threads.length; i++) {
getThreadViewer().setChecked(threads[i], checked);
getThreadViewer().setGrayed(threads[i], false);
}
}
/**
* Check or uncheck a thread. Update the thread's debug target.
*/
protected void checkThread(IExecutionDMContext thread, boolean checked) {
getThreadViewer().setChecked(thread, checked);
IContainerDMContext target = DMContexts.getAncestorOfType(thread, IContainerDMContext.class);
IExecutionDMContext[] threads = syncGetThreads(target);
int checkedNumber = 0;
for (int i = 0; i < threads.length; i++) {
if (getThreadViewer().getChecked(threads[i])) {
++checkedNumber;
}
}
if (checkedNumber == 0) {
getThreadViewer().setChecked(target, false);
getThreadViewer().setGrayed(target, false);
} else if (checkedNumber == threads.length) {
getThreadViewer().setChecked(target, true);
getThreadViewer().setGrayed(target, false);
} else {
getThreadViewer().setGrayChecked(target, true);
}
}
}
public class ThreadFilterContentProvider implements ITreeContentProvider {
public ThreadFilterContentProvider() {
super();
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.viewers.ITreeContentProvider#getChildren(java.lang.Object)
*/
@Override
public Object[] getChildren(Object parent) {
if (parent instanceof IContainerDMContext) {
return syncGetThreads((IContainerDMContext) parent);
}
if (parent instanceof ILaunchManager) {
List<Object> children = new ArrayList<Object>();
ILaunch[] launches = ((ILaunchManager) parent).getLaunches();
IContainerDMContext[] targetArray;
for (int i = 0; i < launches.length; i++) {
if (launches[i] instanceof GdbLaunch) {
targetArray = syncGetContainers((GdbLaunch) launches[i]);
children.addAll(Arrays.asList(targetArray));
}
}
return children.toArray();
}
return new Object[0];
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.viewers.ITreeContentProvider#getParent(java.lang.Object)
*/
@Override
public Object getParent(Object element) {
if (element instanceof IContainerDMContext) {
return DebugPlugin.getDefault().getLaunchManager();
}
if (element instanceof IExecutionDMContext) {
return DMContexts.getAncestorOfType((IExecutionDMContext) element, IContainerDMContext.class);
}
return null;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.viewers.ITreeContentProvider#hasChildren(java.lang.Object)
*/
@Override
public boolean hasChildren(Object element) {
return getChildren(element).length > 0;
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.viewers.IStructuredContentProvider#getElements(java.lang.Object)
*/
@Override
public Object[] getElements(Object inputElement) {
return getChildren(inputElement);
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.viewers.IContentProvider#dispose()
*/
@Override
public void dispose() {
}
/*
* (non-Javadoc)
*
* @see org.eclipse.jface.viewers.IContentProvider#inputChanged(org.eclipse.jface.viewers.Viewer,
* java.lang.Object, java.lang.Object)
*/
@Override
public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {
}
}
public class ThreadFilterLabelProvider extends LabelProvider {
@Override
public Image getImage(Object element) {
if (element instanceof IContainerDMContext) {
return DebugUITools.getImage(IDebugUIConstants.IMG_OBJS_DEBUG_TARGET);
} else {
return DebugUITools.getImage(IDebugUIConstants.IMG_OBJS_THREAD_RUNNING);
}
}
@Override
public String getText(Object element) {
if (element instanceof IContainerDMContext) {
return syncGetContainerLabel((IContainerDMContext)element);
} else {
return syncGetThreadLabel((IExecutionDMContext)element);
}
}
}
private CBreakpointGdbThreadFilterPage fPage;
private CheckboxTreeViewer fThreadViewer;
private ThreadFilterContentProvider fContentProvider;
private CheckHandler fCheckHandler;
public GdbThreadFilterEditor(Composite parent, CBreakpointGdbThreadFilterPage page) {
fPage = page;
fContentProvider = new ThreadFilterContentProvider();
fCheckHandler = new CheckHandler();
createThreadViewer(parent);
}
protected CBreakpointGdbThreadFilterPage getPage() {
return fPage;
}
private void createThreadViewer(Composite parent) {
Label label = new Label(parent, SWT.NONE);
label.setText(Messages.GdbThreadFilterEditor_RestrictToSelected);
label.setFont(parent.getFont());
label.setLayoutData(new GridData());
GridData data = new GridData(GridData.FILL_BOTH);
data.heightHint = 100;
fThreadViewer = new CheckboxTreeViewer(parent, SWT.BORDER);
fThreadViewer.addCheckStateListener(fCheckHandler);
fThreadViewer.getTree().setLayoutData(data);
fThreadViewer.getTree().setFont(parent.getFont());
fThreadViewer.setContentProvider(fContentProvider);
fThreadViewer.setLabelProvider(new ThreadFilterLabelProvider());
fThreadViewer.setInput(DebugPlugin.getDefault().getLaunchManager());
setInitialCheckedState();
}
/**
* Returns the debug targets that appear in the tree
*/
protected IContainerDMContext[] getDebugTargets() {
Object input = fThreadViewer.getInput();
if (!(input instanceof ILaunchManager)) {
return new IContainerDMContext[0];
}
List<Object> targets = new ArrayList<Object>();
ILaunch[] launches = ((ILaunchManager) input).getLaunches();
IContainerDMContext[] targetArray;
for (int i = 0; i < launches.length; i++) {
if (launches[i] instanceof GdbLaunch) {
targetArray = syncGetContainers((GdbLaunch) launches[i]);
targets.addAll(Arrays.asList(targetArray));
}
}
return targets.toArray(new IContainerDMContext[targets.size()]);
}
protected CheckboxTreeViewer getThreadViewer() {
return fThreadViewer;
}
/**
* Sets the initial checked state of the tree viewer. The initial state
* should reflect the current state of the breakpoint. If the breakpoint has
* a thread filter in a given thread, that thread should be checked.
*/
protected void setInitialCheckedState() {
IDsfBreakpointExtension filterExtension = fPage.getFilterExtension();
try {
IContainerDMContext[] targets = filterExtension.getTargetFilters();
for (int i = 0; i < targets.length; i++) {
IExecutionDMContext[] filteredThreads = filterExtension.getThreadFilters(targets[i]);
if (filteredThreads != null) {
for (int j = 0; j < filteredThreads.length; ++j) {
// Mark this thread as selected
fCheckHandler.checkThread(filteredThreads[j], true);
}
} else {
// Mark the entire process as selected
fCheckHandler.checkTarget(targets[i], true);
}
}
} catch (CoreException e) {
CDebugUIPlugin.log(e);
}
}
protected void doStore() {
IDsfBreakpointExtension filterExtension = fPage.getFilterExtension();
IContainerDMContext[] targets = getDebugTargets();
for (int i = 0; i < targets.length; ++i) {
try {
if (getThreadViewer().getChecked(targets[i])) {
if (getThreadViewer().getGrayed(targets[i])) {
IExecutionDMContext[] threads = getTargetThreadFilters(targets[i]);
filterExtension.setThreadFilters(threads);
} else {
filterExtension.setTargetFilter(targets[i]);
}
} else {
filterExtension.removeTargetFilter(targets[i]);
}
DebugPlugin.getDefault().getBreakpointManager().fireBreakpointChanged(fPage.getBreakpoint());
} catch (CoreException e) {
CDebugUIPlugin.log(e);
}
}
}
private IExecutionDMContext[] getTargetThreadFilters(IContainerDMContext target) {
Object[] threads = ((ITreeContentProvider) getThreadViewer().getContentProvider()).getChildren(target);
ArrayList<Object> list = new ArrayList<Object>(threads.length);
for (int i = 0; i < threads.length; ++i) {
if (getThreadViewer().getChecked(threads[i]))
list.add(threads[i]);
}
return list.toArray(new IExecutionDMContext[list.size()]);
}
private IContainerDMContext[] syncGetContainers(final GdbLaunch launch) {
final DsfSession session = launch.getSession();
class ContainerQuery extends Query<IContainerDMContext[]> {
@Override
protected void execute(final DataRequestMonitor<IContainerDMContext[]> rm) {
if (!session.isActive()) {
rm.setStatus(getFailStatus(IDsfStatusConstants.INVALID_STATE, "Launch's session not active.")); //$NON-NLS-1$
rm.done();
return;
}
DsfServicesTracker tracker = new DsfServicesTracker(GdbUIPlugin.getBundleContext(), session.getId());
ICommandControlService commandControl = tracker.getService(ICommandControlService.class);
IMIProcesses procService = tracker.getService(IMIProcesses.class);
if (commandControl != null && procService != null) {
procService.getProcessesBeingDebugged(
commandControl.getContext(),
new DataRequestMonitor<IDMContext[]>(session.getExecutor(), rm) {
@Override
protected void handleSuccess() {
if (getData() instanceof IContainerDMContext[]) {
IContainerDMContext[] containerDmcs = (IContainerDMContext[])getData();
rm.setData(containerDmcs);
} else {
rm.setStatus(getFailStatus(IDsfStatusConstants.INVALID_STATE, "Wrong type of container contexts.")); //$NON-NLS-1$
}
rm.done();
}
});
} else {
rm.setStatus(getFailStatus(IDsfStatusConstants.INVALID_STATE, "GDB Control or Process service not accessible.")); //$NON-NLS-1$
rm.done();
}
tracker.dispose();
}
}
ContainerQuery query = new ContainerQuery();
try {
session.getExecutor().execute(query);
return query.get();
} catch (RejectedExecutionException e) {
} catch (InterruptedException e) {
} catch (ExecutionException e) {
}
return new IContainerDMContext[0];
}
private IExecutionDMContext[] syncGetThreads(final IContainerDMContext container) {
final DsfSession session = DsfSession.getSession(container.getSessionId());
if (session == null) {
return new IExecutionDMContext[0];
}
class ThreadsQuery extends Query<IExecutionDMContext[]> {
@Override
protected void execute(DataRequestMonitor<IExecutionDMContext[]> rm) {
if (!session.isActive()) {
rm.setStatus(getFailStatus(IDsfStatusConstants.INVALID_STATE, "Container's session not active.")); //$NON-NLS-1$
rm.done();
return;
}
DsfServicesTracker tracker = new DsfServicesTracker(GdbUIPlugin.getBundleContext(), session.getId());
IRunControl runControl = tracker.getService(IRunControl.class);
if (runControl != null) {
runControl.getExecutionContexts(container, rm);
} else {
rm.setStatus(getFailStatus(IDsfStatusConstants.INVALID_STATE, "GDB Control not accessible.")); //$NON-NLS-1$
rm.done();
}
tracker.dispose();
}
}
ThreadsQuery query = new ThreadsQuery();
try {
session.getExecutor().execute(query);
return query.get();
} catch (RejectedExecutionException e) {
} catch (InterruptedException e) {
} catch (ExecutionException e) {
}
return new IExecutionDMContext[0];
}
private String syncGetContainerLabel(final IContainerDMContext container) {
final DsfSession session = DsfSession.getSession(container.getSessionId());
if (session == null) {
return "Error reading data"; //$NON-NLS-1$
}
class ContainerLabelQuery extends Query<String> {
@Override
protected void execute(final DataRequestMonitor<String> rm) {
if (!session.isActive()) {
rm.setStatus(getFailStatus(IDsfStatusConstants.INVALID_STATE, "Container's session not active.")); //$NON-NLS-1$
rm.done();
return;
}
DsfServicesTracker tracker = new DsfServicesTracker(GdbUIPlugin.getBundleContext(), session.getId());
IProcesses processService = tracker.getService(IProcesses.class);
IProcessDMContext procDmc = DMContexts.getAncestorOfType(container, IProcessDMContext.class);
if (processService != null && procDmc != null) {
processService.getExecutionData(
procDmc,
new ImmediateDataRequestMonitor<IThreadDMData>(rm) {
@Override
public void handleSuccess() {
final StringBuilder builder = new StringBuilder(getData().getName());
String containerId = getData().getId();
if (containerId != null) {
builder.append(" ["); //$NON-NLS-1$
builder.append(containerId);
builder.append("]"); //$NON-NLS-1$
}
rm.setData(builder.toString());
rm.done();
}
});
} else {
rm.setStatus(getFailStatus(IDsfStatusConstants.INVALID_STATE, "Processes service not accessible.")); //$NON-NLS-1$
rm.done();
}
tracker.dispose();
}
}
ContainerLabelQuery query = new ContainerLabelQuery();
try {
session.getExecutor().execute(query);
return query.get();
} catch (RejectedExecutionException e) {
} catch (InterruptedException e) {
} catch (ExecutionException e) {
}
return "Error reading data"; //$NON-NLS-1$
}
private String syncGetThreadLabel(final IExecutionDMContext thread) {
final DsfSession session = DsfSession.getSession(thread.getSessionId());
if (session == null) {
return "Error reading data"; //$NON-NLS-1$
}
class ThreadLabelQuery extends Query<String> {
@Override
protected void execute(final DataRequestMonitor<String> rm) {
if (!session.isActive()) {
rm.setStatus(getFailStatus(IDsfStatusConstants.INVALID_STATE, "Container's session not active.")); //$NON-NLS-1$
rm.done();
return;
}
DsfServicesTracker tracker = new DsfServicesTracker(GdbUIPlugin.getBundleContext(), session.getId());
IProcesses procService = tracker.getService(IProcesses.class);
if (procService != null) {
IThreadDMContext threadDmc = DMContexts.getAncestorOfType(thread, IThreadDMContext.class);
procService.getExecutionData(threadDmc, new ImmediateDataRequestMonitor<IThreadDMData>(rm) {
@Override
protected void handleSuccess() {
final StringBuilder builder = new StringBuilder(Messages.GdbThreadFilterEditor_Thread);
builder.append(" #"); //$NON-NLS-1$
builder.append(((IMIExecutionDMContext)thread).getThreadId());
String threadName = getData().getName();
if (threadName != null) {
builder.append(" ["); //$NON-NLS-1$
builder.append(threadName);
builder.append("] "); //$NON-NLS-1$
}
String threadId = getData().getId();
if (threadId != null) {
builder.append(threadId);
}
rm.setData(builder.toString());
rm.done();
}
});
} else {
rm.setStatus(getFailStatus(IDsfStatusConstants.INVALID_STATE, "IProcesses service not accessible.")); //$NON-NLS-1$
rm.done();
}
tracker.dispose();
}
}
ThreadLabelQuery query = new ThreadLabelQuery();
try {
session.getExecutor().execute(query);
return query.get();
} catch (RejectedExecutionException e) {
} catch (InterruptedException e) {
} catch (ExecutionException e) {
}
return "Error reading data"; //$NON-NLS-1$
}
private Status getFailStatus(int code, String message) {
return new Status(IStatus.ERROR, GdbUIPlugin.PLUGIN_ID, code, message, null);
}
}