/*******************************************************************************
* Copyright (c) 2007, 2015 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
*
* Contributors:
* Ericsson - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.tests.dsf.breakpoints;
import java.util.Collections;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;
import org.eclipse.cdt.core.IAddress;
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.Immutable;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.datamodel.AbstractDMContext;
import org.eclipse.cdt.dsf.datamodel.DMContexts;
import org.eclipse.cdt.dsf.datamodel.IDMContext;
import org.eclipse.cdt.dsf.debug.service.IBreakpoints;
import org.eclipse.cdt.dsf.service.AbstractDsfService;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.cdt.tests.dsf.DsfTestPlugin;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.model.IBreakpoint;
import org.osgi.framework.BundleContext;
/**
* Initial breakpoint service implementation.
* Implements the IBreakpoints interface.
*/
public class DsfTestBreakpoints extends AbstractDsfService implements IBreakpoints
{
public static final String ATTR_DEBUGGER_PREFIX = DsfTestBreakpoint.DSF_TEST_BREAKPOINT_MODEL_ID + ".debugger.";
public static final String ATTR_ENABLED = ATTR_DEBUGGER_PREFIX + "enabled";
public static final String ATTR_TRANSLATED = ATTR_DEBUGGER_PREFIX + "enabled";
public static final String ATTR_SUB_ID = ATTR_DEBUGGER_PREFIX + "subId";
@Immutable
public static class BreakpointsTargetDMContext extends AbstractDMContext implements IBreakpointsTargetDMContext {
private static int fIdCounter = 1;
public final Integer fId = fIdCounter++;
BreakpointsTargetDMContext (String sessionId) {
super(sessionId, DMContexts.EMPTY_CONTEXTS_ARRAY);
}
@Override
public boolean equals(Object obj) {
return baseEquals(obj) && (fId.equals(((BreakpointsTargetDMContext) obj).fId));
}
@Override
public int hashCode() {
return baseHashCode() + fId.hashCode() ;
}
@Override
public String toString() {
return "breakpointsTarget(" + fId + ")"; //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$
}
}
/**
* Context representing a PDA line breakpoint. In PDA debugger, since there is only
* one file being debugged at a time, a breakpoint is uniquely identified using the
* line number only.
*/
@Immutable
public static class BreakpointDMContext extends AbstractDMContext implements IBreakpointDMContext {
public final Integer fId;
public final Integer fSubId;
public BreakpointDMContext(String sessionId, BreakpointsTargetDMContext commandControlCtx, Integer id, Integer subId) {
super(sessionId, new IDMContext[] { commandControlCtx });
fId = id;
fSubId = subId;
}
@Override
public boolean equals(Object obj) {
return baseEquals(obj) &&
(fId.equals(((BreakpointDMContext) obj).fId)) &&
(fSubId.equals(((BreakpointDMContext) obj).fSubId));
}
@Override
public int hashCode() {
return baseHashCode() + fId.hashCode() + fSubId.hashCode();
}
@Override
public String toString() {
return baseToString() + ".breakpoint(" + fId + "-" + fSubId + ")"; //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$//$NON-NLS-4$
}
}
@Immutable
public static class BreakpointDMData implements IBreakpointDMData {
public final Map<String, Object> fAttributes;
public BreakpointDMData(Map<String, Object> attributes) {
fAttributes = Collections.unmodifiableMap( new HashMap<String, Object>(attributes) );
}
@Override
public IAddress[] getAddresses() { return null; }
@Override
public String getBreakpointType() { return null; }
@Override
public String getFileName() { return null; }
@Override
public int getLineNumber() { return 0; }
@Override
public String getFunctionName() { return null; }
@Override
public String getCondition() { return null; }
@Override
public int getIgnoreCount() { return 0; }
@Override
public String getExpression() { return null; }
@Override
public boolean isEnabled() { return (Boolean)getAttributes().get(ATTR_ENABLED); }
public Map<String, Object> getAttributes() { return fAttributes; }
}
public static class BreakpointsChangedEvent implements IBreakpointsChangedEvent {
public final BreakpointDMContext fBreakpoint;
public final IBreakpointDMContext [] fBreakpointArray;
BreakpointsChangedEvent (BreakpointDMContext bp) {
fBreakpoint = bp;
fBreakpointArray = new IBreakpointDMContext[] { bp };
}
@Override
public IBreakpointsTargetDMContext getDMContext() {
return DMContexts.getAncestorOfType(fBreakpoint, IBreakpointsTargetDMContext.class);
}
@Override
public IBreakpointDMContext[] getBreakpoints() {
return fBreakpointArray;
}
}
public static class BreakpointsAddedEvent extends BreakpointsChangedEvent implements IBreakpointsAddedEvent {
BreakpointsAddedEvent(BreakpointDMContext bp) {
super(bp);
}
}
public static class BreakpointsRemovedEvent extends BreakpointsChangedEvent implements IBreakpointsRemovedEvent {
BreakpointsRemovedEvent(BreakpointDMContext bp) {
super(bp);
}
}
public static class BreakpointsUpdatedEvent extends BreakpointsChangedEvent implements IBreakpointsUpdatedEvent {
BreakpointsUpdatedEvent(BreakpointDMContext bp) {
super(bp);
}
}
// Breakpoints currently installed
private Map<BreakpointDMContext, BreakpointDMData> fBreakpoints = new HashMap<BreakpointDMContext, BreakpointDMData>();
/**
* The service constructor
*
* @param session The debugging session this service belongs to.
*/
public DsfTestBreakpoints(DsfSession session) {
super(session);
}
@Override
public void initialize(final RequestMonitor rm) {
super.initialize(new RequestMonitor(getExecutor(), rm) {
@Override
protected void handleSuccess() {
doInitialize(rm);
}
});
}
private void doInitialize(final RequestMonitor rm) {
// Register this service
register(new String[] { IBreakpoints.class.getName(), DsfTestBreakpoints.class.getName() },
new Hashtable<String, String>());
rm.done();
}
@Override
public void shutdown(final RequestMonitor rm) {
unregister();
rm.done();
}
@Override
protected BundleContext getBundleContext() {
return DsfTestPlugin.getBundleContext();
}
@Override
public void getBreakpoints(final IBreakpointsTargetDMContext context, final DataRequestMonitor<IBreakpointDMContext[]> rm) {
// Validate the context
// TODO: check the target context
// if (!fCommandControl.getContext().equals(context)) {
// DsfTestPlugin.failRequest(rm, INVALID_HANDLE, "Invalid breakpoints target context");
// return;
// }
rm.setData(fBreakpoints.keySet().toArray(new IBreakpointDMContext[fBreakpoints.size()]));
rm.done();
}
@Override
public void getBreakpointDMData(IBreakpointDMContext dmc, DataRequestMonitor<IBreakpointDMData> rm) {
DsfTestPlugin.failRequest(rm, NOT_SUPPORTED, "Retrieving breakpoint data is not supported");
}
@Override
public void insertBreakpoint(IBreakpointsTargetDMContext context, Map<String, Object> attributes,
DataRequestMonitor<IBreakpointDMContext> rm)
{
Boolean enabled = (Boolean)attributes.get(IBreakpoint.ENABLED);
if (enabled != null && !enabled.booleanValue()) {
// If the breakpoint is disabled, just fail the request.
DsfTestPlugin.failRequest(rm, REQUEST_FAILED, "Breakpoint is disabled");
} else {
BreakpointsTargetDMContext targetCtx = DMContexts.getAncestorOfType(context, BreakpointsTargetDMContext.class);
if (targetCtx != null) {
doInsertBreakpoint(targetCtx, attributes, rm);
} else {
DsfTestPlugin.failRequest(rm, INVALID_HANDLE, "Unknown breakpoint type");
}
}
}
private void doInsertBreakpoint(BreakpointsTargetDMContext targetCtx, final Map<String, Object> attributes, final DataRequestMonitor<IBreakpointDMContext> rm)
{
// Retrieve the id
Integer id = (Integer)attributes.get(DsfTestBreakpoint.ATTR_ID);
if (id == null) {
DsfTestPlugin.failRequest(rm, REQUEST_FAILED, "No ID specified");
return;
}
Integer subId = (Integer)attributes.get(ATTR_SUB_ID);
if (subId == null) {
DsfTestPlugin.failRequest(rm, REQUEST_FAILED, "No Sub ID specified");
return;
}
// Create a new breakpoint context object and check that it's not
// installed already. PDA can only track a single breakpoint at a
// given line, attempting to set the second breakpoint should fail.
final BreakpointDMContext breakpointCtx =
new BreakpointDMContext(getSession().getId(), targetCtx, id, subId);
if (fBreakpoints.containsKey(breakpointCtx)) {
DsfTestPlugin.failRequest(rm, REQUEST_FAILED, "Breakpoint already set");
return;
}
// Add the new breakpoint context to the list of known breakpoints.
// Adding it here, before the set command is completed will prevent
// a possibility of a second breakpoint being installed in the same
// location while this breakpoint is being processed. It will also
// allow the breakpoint to be removed or updated even while it is
// still being processed here.
fBreakpoints.put(breakpointCtx, new BreakpointDMData(attributes));
rm.setData(breakpointCtx);
getSession().dispatchEvent(new BreakpointsAddedEvent(breakpointCtx), getProperties());
}
@Override
public void removeBreakpoint(IBreakpointDMContext bpCtx, RequestMonitor rm) {
if (!fBreakpoints.containsKey(bpCtx)) {
DsfTestPlugin.failRequest(rm, REQUEST_FAILED, "Breakpoint already removed");
return;
}
if (bpCtx instanceof BreakpointDMContext) {
if ( fBreakpoints.remove(bpCtx) == null ) {
DsfTestPlugin.failRequest(rm, INVALID_STATE, "Breakpoint does not exist");
} else {
getSession().dispatchEvent(new BreakpointsRemovedEvent((BreakpointDMContext)bpCtx), getProperties());
}
rm.done();
} else {
rm.setStatus(new Status(IStatus.ERROR, DsfTestPlugin.PLUGIN_ID, INTERNAL_ERROR, "Invalid breakpoint type", null ));
rm.done();
}
}
@Override
public void updateBreakpoint(final IBreakpointDMContext bpCtx, Map<String, Object> attributes, final RequestMonitor rm) {
if (!fBreakpoints.containsKey(bpCtx)) {
DsfTestPlugin.failRequest(rm, REQUEST_FAILED, "Breakpoint not installed");
return;
}
for (String attribute : attributes.keySet()) {
if (!DsfTestBreakpoint.ATTR_UPDATABLE.equals(attribute)) {
DsfTestPlugin.failRequest(rm, REQUEST_FAILED, "Attribute cannot be updated");
return;
}
}
if (bpCtx instanceof BreakpointDMContext) {
Map<String, Object> newAttrs = new HashMap<String, Object>(fBreakpoints.get(bpCtx).getAttributes());
newAttrs.putAll(attributes);
fBreakpoints.put((BreakpointDMContext)bpCtx, new BreakpointDMData(newAttrs));
getSession().dispatchEvent(new BreakpointsRemovedEvent((BreakpointDMContext)bpCtx), getProperties());
} else {
DsfTestPlugin.failRequest(rm, INVALID_HANDLE, "Invalid breakpoint type");
}
}
}