/******************************************************************************* * Copyright (c) 2012, 2016 Mentor Graphics 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: * Mentor Graphics - Initial API and implementation *******************************************************************************/ package org.eclipse.cdt.dsf.gdb.service; import java.util.Hashtable; import java.util.Map; import org.eclipse.cdt.dsf.concurrent.ConfinedToDsfExecutor; import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor; import org.eclipse.cdt.dsf.concurrent.ImmediateRequestMonitor; import org.eclipse.cdt.dsf.concurrent.RequestMonitor; import org.eclipse.cdt.dsf.datamodel.IDMContext; import org.eclipse.cdt.dsf.debug.service.IBreakpoints; import org.eclipse.cdt.dsf.debug.service.IBreakpointsExtension; import org.eclipse.cdt.dsf.debug.service.command.ICommandControl; import org.eclipse.cdt.dsf.debug.service.command.IEventListener; import org.eclipse.cdt.dsf.gdb.internal.GdbPlugin; import org.eclipse.cdt.dsf.mi.service.IMICommandControl; import org.eclipse.cdt.dsf.mi.service.MIBreakpointDMData; import org.eclipse.cdt.dsf.mi.service.MIBreakpoints; import org.eclipse.cdt.dsf.mi.service.MIBreakpointsSynchronizer; import org.eclipse.cdt.dsf.mi.service.command.output.MIBreakpoint; import org.eclipse.cdt.dsf.mi.service.command.output.MIConst; import org.eclipse.cdt.dsf.mi.service.command.output.MINotifyAsyncOutput; import org.eclipse.cdt.dsf.mi.service.command.output.MIOOBRecord; import org.eclipse.cdt.dsf.mi.service.command.output.MIOutput; import org.eclipse.cdt.dsf.mi.service.command.output.MIResult; import org.eclipse.cdt.dsf.mi.service.command.output.MITuple; import org.eclipse.cdt.dsf.mi.service.command.output.MIValue; import org.eclipse.cdt.dsf.service.DsfSession; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Status; /** * Breakpoints service for GDB 7.4. * Using breakpoint notifications introduced in 7.4 supports synchronization between the breakpoints * set from the GDB console and the Breakpoints view as well as the tracepoints reported form trace files. * * @since 4.2 */ public class GDBBreakpoints_7_4 extends GDBBreakpoints_7_2 implements IEventListener { // Breakpoint notifications private static final String BREAKPOINT_PREFIX = "breakpoint-"; //$NON-NLS-1$ private static final String BREAKPOINT_CREATED = BREAKPOINT_PREFIX + "created"; //$NON-NLS-1$ private static final String BREAKPOINT_MODIFIED = BREAKPOINT_PREFIX + "modified"; //$NON-NLS-1$ private static final String BREAKPOINT_DELETED = BREAKPOINT_PREFIX + "deleted"; //$NON-NLS-1$ private IMICommandControl fConnection; public GDBBreakpoints_7_4(DsfSession session) { super(session); } @Override public void initialize(final RequestMonitor rm) { super.initialize(new ImmediateRequestMonitor(rm) { @Override protected void handleSuccess() { doInitialize(rm); } }); } private void doInitialize(final RequestMonitor rm) { fConnection = getServicesTracker().getService(IMICommandControl.class); if (fConnection == null) { rm.setStatus(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, "Service is not available")); //$NON-NLS-1$ rm.done(); return; } fConnection.addEventListener(this); // Register this service register(new String[] { IBreakpoints.class.getName(), IBreakpointsExtension.class.getName(), MIBreakpoints.class.getName(), GDBBreakpoints_7_0.class.getName(), GDBBreakpoints_7_2.class.getName(), GDBBreakpoints_7_4.class.getName() }, new Hashtable<String, String>()); rm.done(); } @Override public void shutdown(RequestMonitor requestMonitor) { ICommandControl control = getCommandControl(); if (control != null) { control.removeEventListener(this); } unregister(); super.shutdown(requestMonitor); } @Override public void eventReceived(Object output) { if (output instanceof MIOutput) { MIBreakpointsSynchronizer bs = getServicesTracker().getService(MIBreakpointsSynchronizer.class); if (bs != null) { MIOOBRecord[] records = ((MIOutput)output).getMIOOBRecords(); for(MIOOBRecord r : records) { if (r instanceof MINotifyAsyncOutput) { MINotifyAsyncOutput notifyOutput = (MINotifyAsyncOutput)r; String asyncClass = notifyOutput.getAsyncClass(); if (BREAKPOINT_CREATED.equals(asyncClass)) { MIBreakpoint bpt = getMIBreakpointFromOutput(notifyOutput); if (bpt != null) bs.targetBreakpointCreated(bpt); } else if (BREAKPOINT_DELETED.equals(asyncClass)) { String id = getMIBreakpointIdFromOutput(notifyOutput); if (!id.isEmpty()) bs.targetBreakpointDeleted(id); } else if (BREAKPOINT_MODIFIED.equals(asyncClass)) { MIBreakpoint bpt = getMIBreakpointFromOutput(notifyOutput); if (bpt != null) bs.targetBreakpointModified(bpt); } } } } } } private IMICommandControl getCommandControl() { return fConnection; } private MIBreakpoint getMIBreakpointFromOutput(MINotifyAsyncOutput notifyOutput) { MIBreakpoint bpt = null; MIResult[] results = notifyOutput.getMIResults(); for(int i = 0; i < results.length; i++) { String var = results[i].getVariable(); MIValue val = results[i].getMIValue(); if (var.equals("bkpt")) { //$NON-NLS-1$ if (val instanceof MITuple) { bpt = new MIBreakpoint((MITuple)val); } } } return bpt; } private String getMIBreakpointIdFromOutput(MINotifyAsyncOutput notifyOutput) { MIResult[] results = notifyOutput.getMIResults(); for(int i = 0; i < results.length; i++) { String var = results[i].getVariable(); MIValue val = results[i].getMIValue(); if (var.equals("id") && val instanceof MIConst) { //$NON-NLS-1$ try { return ((MIConst)val).getCString().trim(); } catch(NumberFormatException e) { GdbPlugin.log(new Status(IStatus.ERROR, GdbPlugin.PLUGIN_ID, "Invalid breakpoint id")); //$NON-NLS-1$ } } } return ""; //$NON-NLS-1$ } @Override protected void addBreakpoint( final IBreakpointsTargetDMContext context, final Map<String, Object> attributes, final DataRequestMonitor<IBreakpointDMContext> finalRm) { final MIBreakpointsSynchronizer bs = getServicesTracker().getService(MIBreakpointsSynchronizer.class); if (bs != null) { // Skip the breakpoints set from the console or from outside of Eclipse // because they are already installed on the target. bs.getTargetBreakpoint( context, attributes, new DataRequestMonitor<MIBreakpoint>(getExecutor(), finalRm) { @Override @ConfinedToDsfExecutor( "fExecutor" ) protected void handleSuccess() { MIBreakpoint miBpt = getData(); if (miBpt != null) { bs.removeCreatedTargetBreakpoint(context, miBpt); MIBreakpointDMData newBreakpoint = new MIBreakpointDMData(miBpt); getBreakpointMap(context).put(newBreakpoint.getNumber(), newBreakpoint); IBreakpointDMContext dmc = new MIBreakpointDMContext(GDBBreakpoints_7_4.this, new IDMContext[] { context }, newBreakpoint.getNumber()); finalRm.setData(dmc); getSession().dispatchEvent(new BreakpointAddedEvent(dmc), getProperties()); finalRm.done(); } else { GDBBreakpoints_7_4.super.addBreakpoint(context, attributes, finalRm); } } }); } else { super.addBreakpoint(context, attributes, finalRm); } } @Override protected void addTracepoint( final IBreakpointsTargetDMContext context, final Map<String, Object> attributes, final DataRequestMonitor<IBreakpointDMContext> drm) { final MIBreakpointsSynchronizer bs = getServicesTracker().getService(MIBreakpointsSynchronizer.class); if (bs != null) { // Skip the breakpoints set from the console or from outside of Eclipse // because they are already installed on the target. bs.getTargetBreakpoint( context, attributes, new DataRequestMonitor<MIBreakpoint>(getExecutor(), drm) { @Override @ConfinedToDsfExecutor( "fExecutor" ) protected void handleSuccess() { MIBreakpoint miBpt = getData(); if (miBpt != null) { bs.removeCreatedTargetBreakpoint(context, miBpt); MIBreakpointDMData newBreakpoint = new MIBreakpointDMData(miBpt); getBreakpointMap(context).put(newBreakpoint.getNumber(), newBreakpoint); IBreakpointDMContext dmc = new MIBreakpointDMContext(GDBBreakpoints_7_4.this, new IDMContext[] { context }, newBreakpoint.getNumber()); drm.setData(dmc); getSession().dispatchEvent(new BreakpointAddedEvent(dmc), getProperties()); drm.done(); } else { GDBBreakpoints_7_4.super.addTracepoint(context, attributes, drm); } } }); } else { super.addTracepoint(context, attributes, drm); } } @Override protected void addWatchpoint( final IBreakpointsTargetDMContext context, final Map<String, Object> attributes, final DataRequestMonitor<IBreakpointDMContext> drm) { final MIBreakpointsSynchronizer bs = getServicesTracker().getService(MIBreakpointsSynchronizer.class); if (bs != null) { // Skip the breakpoints set from the console or from outside of Eclipse // because they are already installed on the target. bs.getTargetBreakpoint( context, attributes, new DataRequestMonitor<MIBreakpoint>(getExecutor(), drm) { @Override @ConfinedToDsfExecutor( "fExecutor" ) protected void handleSuccess() { MIBreakpoint miBpt = getData(); if (miBpt != null) { bs.removeCreatedTargetBreakpoint(context, miBpt); MIBreakpointDMData newBreakpoint = new MIBreakpointDMData(miBpt); getBreakpointMap(context).put(newBreakpoint.getNumber(), newBreakpoint); IBreakpointDMContext dmc = new MIBreakpointDMContext(GDBBreakpoints_7_4.this, new IDMContext[] { context }, newBreakpoint.getNumber()); drm.setData(dmc); getSession().dispatchEvent(new BreakpointAddedEvent(dmc), getProperties()); drm.done(); } else { GDBBreakpoints_7_4.super.addWatchpoint(context, attributes, drm); } } }); } else { super.addWatchpoint(context, attributes, drm); } } @Override protected void deleteBreakpointFromTarget(IBreakpointsTargetDMContext context, String reference, RequestMonitor finalRm) { MIBreakpointsSynchronizer bs = getServicesTracker().getService(MIBreakpointsSynchronizer.class); if (bs != null) { // Do nothing if the breakpoint is deleted from the console. if (bs.isTargetBreakpointDeleted(context, reference, true)) { finalRm.done(); return; } } super.deleteBreakpointFromTarget(context, reference, finalRm); } @Override public String adjustDebuggerPath(String originalPath) { // No adjustment is required return originalPath; } }