/******************************************************************************* * Copyright (c) 2010, 2015 Wind River Systems, Inc. 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: * Wind River Systems - initial API and implementation *******************************************************************************/ package org.eclipse.tcf.internal.cdt.ui; import java.util.HashMap; import java.util.LinkedList; import java.util.Map; import org.eclipse.cdt.core.IAddress; import org.eclipse.cdt.debug.core.model.IMoveToAddress; import org.eclipse.cdt.debug.core.model.IMoveToLine; import org.eclipse.cdt.debug.core.model.IResumeAtAddress; import org.eclipse.cdt.debug.core.model.IResumeAtLine; import org.eclipse.cdt.debug.core.model.IRunToAddress; import org.eclipse.cdt.debug.core.model.IRunToLine; import org.eclipse.cdt.debug.internal.core.sourcelookup.CSourceLookupDirector; import org.eclipse.core.resources.IFile; import org.eclipse.core.runtime.IAdaptable; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Status; import org.eclipse.debug.core.DebugException; import org.eclipse.debug.core.model.ISourceLocator; import org.eclipse.debug.core.model.ISuspendResume; import org.eclipse.tcf.internal.debug.actions.TCFAction; import org.eclipse.tcf.internal.debug.model.TCFContextState; import org.eclipse.tcf.internal.debug.ui.model.TCFChildren; import org.eclipse.tcf.internal.debug.ui.model.TCFDebugTask; import org.eclipse.tcf.internal.debug.ui.model.TCFNode; import org.eclipse.tcf.internal.debug.ui.model.TCFNodeExecContext; import org.eclipse.tcf.internal.debug.ui.model.TCFNodeRegister; import org.eclipse.tcf.protocol.IChannel; import org.eclipse.tcf.protocol.IToken; import org.eclipse.tcf.protocol.JSON; import org.eclipse.tcf.services.IBreakpoints; import org.eclipse.tcf.services.ILineNumbers; import org.eclipse.tcf.services.ILineNumbers.CodeArea; import org.eclipse.tcf.services.IMemory; import org.eclipse.tcf.services.IRegisters; import org.eclipse.tcf.services.IRegisters.RegistersContext; import org.eclipse.tcf.services.IRunControl; import org.eclipse.tcf.services.IRunControl.RunControlContext; import org.eclipse.tcf.services.IRunControl.RunControlListener; import org.eclipse.tcf.util.TCFDataCache; import org.eclipse.tcf.util.TCFTask; /** * A {@link ISuspendResume} adapter for TCF execution contexts enabling special * run control actions run-to-line, move-to-line and resume-at-line. */ @SuppressWarnings("restriction") public class TCFSuspendResumeAdapter implements ISuspendResume, IRunToLine, IRunToAddress, IMoveToLine, IMoveToAddress, IResumeAtLine, IResumeAtAddress, IAdaptable { private static class Location { String fFile; int fLine; Number fAddress; Location(String file, int line) { fFile = file; fLine = line; } Location(Number address) { fAddress = address; } } private final TCFNodeExecContext fExecCtx; public TCFSuspendResumeAdapter(TCFNodeExecContext execCtx) { fExecCtx = execCtx; } @SuppressWarnings("rawtypes") public Object getAdapter(Class adapter) { if (adapter.isInstance(this)) { return this; } return null; } public boolean canResume() { return isSuspended(); } public boolean canSuspend() { return !isSuspended(); } public boolean isSuspended() { try { Boolean result = new TCFTask<Boolean>() { public void run() { if (fExecCtx.isDisposed()) { done(Boolean.FALSE); return; } TCFDataCache<TCFContextState> state = fExecCtx.getState(); if (!state.validate(this)) { return; } if (state.getError() == null && state.getData() != null) { done(state.getData().is_suspended); return; } done(Boolean.FALSE); } }.get(); return result != null ? result : false; } catch (Exception e) { // ignored } return false; } public void resume() throws DebugException { new TCFDebugTask<Object>() { public void run() { if (fExecCtx.isDisposed()) { // ignore silently done(null); return; } TCFDataCache<IRunControl.RunControlContext> cache = fExecCtx.getRunContext(); if (!cache.validate(this)) { return; } IRunControl.RunControlContext runCtx = cache.getData(); runCtx.resume(IRunControl.RM_RESUME, 1, new IRunControl.DoneCommand() { public void doneCommand(IToken token, Exception error) { if (error != null) { error(error); } else { done(null); } } }); } }.getD(); } public void suspend() throws DebugException { new TCFDebugTask<Object>() { public void run() { if (fExecCtx.isDisposed()) { // ignore silently done(null); return; } TCFDataCache<IRunControl.RunControlContext> cache = fExecCtx.getRunContext(); if (!cache.validate(this)) { return; } IRunControl.RunControlContext runCtx = cache.getData(); if (runCtx.canSuspend()) { runCtx.suspend(new IRunControl.DoneCommand() { public void doneCommand(IToken token, Exception error) { if (error != null) { error(error); } else { done(null); } } }); } } }.getD(); } public boolean canResumeAtAddress(IAddress address) { return true; } public void resumeAtAddress(IAddress address) throws DebugException { moveToLocation(new Location(address.getValue()), true); } public boolean canResumeAtLine(IFile file, int lineNumber) { return true; } public void resumeAtLine(IFile file, int lineNumber) throws DebugException { IPath location = file.getLocation(); if (location == null) { throw new DebugException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Cannot resume at line: Not a local file.")); } resumeAtLine(location.toOSString(), lineNumber); } public boolean canResumeAtLine(String fileName, int lineNumber) { return true; } public void resumeAtLine(String fileName, int lineNumber) throws DebugException { String debuggerPath = mapToDebuggerPath(fileName); moveToLocation(new Location(debuggerPath, lineNumber), true); } public boolean canMoveToAddress(IAddress address) { return true; } public void moveToAddress(IAddress address) throws DebugException { moveToLocation(new Location(address.getValue()), false); } public boolean canMoveToLine(String fileName, int lineNumber) { return true; } public void moveToLine(String fileName, int lineNumber) throws DebugException { String debuggerPath = mapToDebuggerPath(fileName); moveToLocation(new Location(debuggerPath, lineNumber), false); } public boolean canRunToAddress(IAddress address) { return canResume(); } public void runToAddress(IAddress address, boolean skipBreakpoints) throws DebugException { runToLocation(new Location(address.getValue()), skipBreakpoints); } public boolean canRunToLine(IFile file, int lineNumber) { return canResume(); } public void runToLine(IFile file, int lineNumber, boolean skipBreakpoints) throws DebugException { IPath location = file.getLocation(); if (location == null) { throw new DebugException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Cannot run to line: Not a local file.")); } runToLine(location.toOSString(), lineNumber, skipBreakpoints); } public boolean canRunToLine(String fileName, int lineNumber) { return canResume(); } public void runToLine(String fileName, final int lineNumber, final boolean skipBreakpoints) throws DebugException { String debuggerPath = mapToDebuggerPath(fileName); runToLocation(new Location(debuggerPath, lineNumber), skipBreakpoints); } private String mapToDebuggerPath(String fileName) { ISourceLocator locator = fExecCtx.getModel().getLaunch().getSourceLocator(); if (locator instanceof CSourceLookupDirector) { IPath compilationPath = ((CSourceLookupDirector) locator).getCompilationPath(fileName); if (compilationPath != null) { return compilationPath.toString(); } } // fallback: use basename return new Path(fileName).lastSegment(); } private void runToLocation(final Location location, final boolean skipBreakpoints) throws DebugException { new TCFDebugTask<Object>() { public void run() { if (fExecCtx.isDisposed()) { // ignore silently done(null); return; } final IChannel channel = fExecCtx.getChannel(); final IBreakpoints breakpoints = channel.getRemoteService(IBreakpoints.class); if (breakpoints == null) { error("Cannot set breakpoint."); return; } final String contextId = fExecCtx.getID(); Map<String, Object> properties = new HashMap<String, Object>(); if (location.fFile != null) { properties.put(IBreakpoints.PROP_FILE, location.fFile); properties.put(IBreakpoints.PROP_LINE, location.fLine); } else { properties.put(IBreakpoints.PROP_LOCATION, location.fAddress.toString()); } // properties.put(IBreakpoints.PROP_CONTEXTIDS, new String[] { contextId }); properties.put(IBreakpoints.PROP_ENABLED, Boolean.TRUE); // properties.put(IBreakpoints.PROP_TEMPORARY, Boolean.TRUE); final String breakpointId = TCFAction.STEP_BREAKPOINT_PREFIX + contextId; properties.put(IBreakpoints.PROP_ID, breakpointId); breakpoints.add(properties, new IBreakpoints.DoneCommand() { public void doneCommand(IToken token, Exception error) { if (error != null) { error(error); return; } final Runnable removeBreakpoint = new Runnable() { public void run() { breakpoints.remove(new String[] { breakpointId }, new IBreakpoints.DoneCommand() { public void doneCommand(IToken token, Exception error) { // ignore errors? } }); } }; Runnable resume = new Runnable() { public void run() { final IRunControl runControl = channel.getRemoteService(IRunControl.class); if (runControl == null) { error("Cannot resume."); removeBreakpoint.run(); return; } final TCFDataCache<IRunControl.RunControlContext> cache = fExecCtx.getRunContext(); if (!cache.validate(this)) { return; } if (cache.getError() != null) { error(cache.getError()); removeBreakpoint.run(); return; } runControl.addListener(new RunControlListener() { private void finished() { runControl.removeListener(this); removeBreakpoint.run(); } public void contextSuspended(String context, String pc, String reason, Map<String, Object> params) { if (contextId.equals(context)) { finished(); } } public void contextResumed(String context) { } public void contextRemoved(String[] context_ids) { for (String context : context_ids) { if (contextId.equals(context)) { finished(); return; } } } public void contextException(String context, String msg) { } public void contextChanged(RunControlContext[] contexts) { } public void contextAdded(RunControlContext[] contexts) { } public void containerSuspended(String context, String pc, String reason, Map<String, Object> params, String[] suspended_ids) { for (String context2 : suspended_ids) { if (contextId.equals(context2)) { finished(); return; } } } public void containerResumed(String[] context_ids) { } }); IRunControl.RunControlContext runCtx = cache.getData(); runCtx.resume(IRunControl.RM_RESUME, 1, new IRunControl.DoneCommand() { public void doneCommand(IToken token, Exception error) { if (error != null) { error(error); } else { done(null); } } }); } }; resume.run(); } }); } }.getD(); } private void moveToLocation(final Location location, final boolean resume) throws DebugException { new TCFDebugTask<Object>() { private RegistersContext fPCReg; public void run() { if (fExecCtx.isDisposed()) { // ignore silently done(null); return; } LinkedList<TCFChildren> queue = new LinkedList<TCFChildren>(); queue.add(fExecCtx.getRegisters()); while (fPCReg == null && !queue.isEmpty()) { TCFChildren regNodesCache = queue.removeFirst(); if (!regNodesCache.validate(this)) return; Map<String,TCFNode> regNodes = regNodesCache.getData(); if (regNodes == null || regNodes.size() == 0) { if (regNodesCache.getError() != null) error(regNodesCache.getError()); else error("Cannot retrive registers info"); return; } for (TCFNode node : regNodes.values()) { TCFNodeRegister regNode = (TCFNodeRegister)node; TCFDataCache<IRegisters.RegistersContext> regCtxCache = regNode.getContext(); if (!regCtxCache.validate(this)) return; IRegisters.RegistersContext context = regCtxCache.getData(); if (context != null && IRegisters.ROLE_PC.equals(context.getRole())) { fPCReg = context; break; } queue.add(regNode.getChildren()); } } if (fPCReg == null) { error("Cannot determine PC register."); return; } if (location.fAddress == null) { final IChannel channel = fExecCtx.getChannel(); final ILineNumbers lineNumbers = channel.getRemoteService(ILineNumbers.class); if (lineNumbers == null) { error("No line numbers service."); return; } TCFDataCache<TCFNodeExecContext> memNodeCache = fExecCtx.getMemoryNode(); if (!memNodeCache.validate(this)) return; TCFNodeExecContext memNode = memNodeCache.getData(); if (memNode == null) { if (memNodeCache.getError() != null) error(memNodeCache.getError()); else error("Cannot determine memory context."); return; } TCFDataCache<IMemory.MemoryContext> memCtxCache = memNode.getMemoryContext(); if (!memCtxCache.validate(this)) return; final IMemory.MemoryContext memCtx = memCtxCache.getData(); if (memCtx == null) { if (memNodeCache.getError() != null) error(memNodeCache.getError()); else error("Cannot determine memory context."); return; } lineNumbers.mapToMemory(memCtx.getID(), location.fFile, location.fLine, 0, new ILineNumbers.DoneMapToMemory() { public void doneMapToMemory(IToken token, Exception error, CodeArea[] areas) { if (error != null) { error(error); return; } if (areas == null || areas.length == 0) { error("Cannot map source location to address."); return; } doneGetLocationAddress(areas[0].start_address); } }); } else { doneGetLocationAddress(location.fAddress); } } private void doneGetLocationAddress(Number address) { if (address == null) { error("Cannot map source location to address."); return; } byte[] value = addressToByteArray(address, fPCReg.getSize(), fPCReg.isBigEndian()); fPCReg.set(value, new IRegisters.DoneSet() { public void doneSet(IToken token, Exception error) { if (error != null) { error(error); return; } fExecCtx.getModel().setDebugViewSelection(fExecCtx, "Move"); if (resume) { final TCFDataCache<IRunControl.RunControlContext> cache = fExecCtx.getRunContext(); final IChannel channel = fExecCtx.getChannel(); Runnable resume = new Runnable() { public void run() { final IRunControl runControl = channel.getRemoteService(IRunControl.class); if (runControl == null) { error("Cannot resume."); return; } if (!cache.validate(this)) { return; } if (cache.getError() != null) { error(cache.getError()); return; } IRunControl.RunControlContext runCtx = cache.getData(); runCtx.resume(IRunControl.RM_RESUME, 1, new IRunControl.DoneCommand() { public void doneCommand(IToken token, Exception error) { if (error != null) { error(error); } else { done(null); } } }); } }; if (cache.validate(resume)) { resume.run(); } } else { done(null); } } }); } private byte[] addressToByteArray(Number address, int size, boolean bigEndian) { byte[] bytes = new byte[size]; byte[] addrBytes = JSON.toBigInteger(address).toByteArray(); for (int i=0; i < bytes.length; ++i) { byte b = 0; if (i < addrBytes.length) { b = addrBytes[addrBytes.length - i - 1]; } bytes[bigEndian ? size -i - 1 : i] = b; } return bytes; } }.getD(); } }