blob: 5c0dffa7c04b3fa17c7228914f4d5625dc724b84 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2010 Nokia 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:
* Nokia - Initial API and implementation
*******************************************************************************/
package org.eclipse.cdt.debug.edc.internal.services.dsf;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.cdt.core.IAddress;
import org.eclipse.cdt.debug.edc.internal.EDCDebugger;
import org.eclipse.cdt.debug.edc.internal.EDCTrace;
import org.eclipse.cdt.debug.edc.services.AbstractEDCService;
import org.eclipse.cdt.debug.edc.services.IDSFServiceUsingTCF;
import org.eclipse.cdt.debug.edc.services.IEDCDMContext;
import org.eclipse.cdt.debug.edc.services.IEDCExecutionDMC;
import org.eclipse.cdt.debug.edc.services.IEDCMemory;
import org.eclipse.cdt.debug.edc.snapshot.IAlbum;
import org.eclipse.cdt.debug.edc.snapshot.ISnapshotContributor;
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.IDsfStatusConstants;
import org.eclipse.cdt.dsf.concurrent.ImmediateExecutor;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.datamodel.AbstractDMEvent;
import org.eclipse.cdt.dsf.datamodel.DMContexts;
import org.eclipse.cdt.dsf.datamodel.IDMContext;
import org.eclipse.cdt.dsf.debug.service.ICachingService;
import org.eclipse.cdt.dsf.debug.service.IExpressions;
import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionChangedDMEvent;
import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMAddress;
import org.eclipse.cdt.dsf.debug.service.IExpressions.IExpressionDMContext;
import org.eclipse.cdt.dsf.debug.service.IMemory;
import org.eclipse.cdt.dsf.debug.service.IRunControl.IResumedDMEvent;
import org.eclipse.cdt.dsf.debug.service.IRunControl.ISuspendedDMEvent;
import org.eclipse.cdt.dsf.debug.service.IRunControl.StateChangeReason;
import org.eclipse.cdt.dsf.service.DsfServiceEventHandler;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.cdt.utils.Addr64;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.debug.core.model.MemoryByte;
import org.eclipse.tm.tcf.protocol.IService;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
public class Memory extends AbstractEDCService implements IEDCMemory, ICachingService, ISnapshotContributor,
IDSFServiceUsingTCF {
private org.eclipse.tm.tcf.services.IMemory tcfMemoryService;
private Map<String, MemoryCache> memoryCaches;
private long tcfTimeout;
private class MemoryChangedEvent extends AbstractDMEvent<IMemoryDMContext> implements IMemoryChangedEvent {
IAddress[] addresses;
public MemoryChangedEvent(IMemoryDMContext context, IAddress[] addresses) {
super(context);
this.addresses = addresses;
}
public IAddress[] getAddresses() {
return addresses;
}
}
public Memory(DsfSession session) {
super(session, new String[] { IEDCMemory.class.getName(), IMemory.class.getName(), Memory.class.getName(),
ISnapshotContributor.class.getName() });
setTCFTimeout(15 * 1000); // Fifteen seconds
}
private MemoryCache getMemoryCache(IMemoryDMContext memoryDMC) {
assert memoryDMC instanceof IEDCDMContext;
MemoryCache cache = memoryCaches.get(((IEDCDMContext) memoryDMC).getID());
if (cache == null) {
cache = new MemoryCache(getTargetEnvironmentService().getMemoryCacheMinimumBlockSize());
memoryCaches.put(((IEDCDMContext) memoryDMC).getID(), cache);
}
return cache;
}
@Override
protected void doInitialize(RequestMonitor requestMonitor) {
super.doInitialize(requestMonitor);
// create memory cache
memoryCaches = new HashMap<String, MemoryCache>();
getSession().addServiceEventListener(this, null);
}
public MemoryByte[] getMemory(final IMemoryDMContext context, final IAddress address, final long offset,
final int word_size, final int count) throws CoreException {
if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { address.toHexAddressString(), offset, word_size, count })); }
// Validate the context
if (context == null) {
throw new CoreException(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, INTERNAL_ERROR,
"Unknown context type", null)); //$NON-NLS-1$);
}
// Validate the word size
if (word_size < 1) {
throw new CoreException(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, NOT_SUPPORTED,
"Word size not supported (< 1)", null)); //$NON-NLS-1$
}
// Validate the byte count
if (count < 0) {
throw new CoreException(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR,
"Invalid word count (< 0)", null)); //$NON-NLS-1$
}
// everything OK
// NOTE: We normalize word_size and count to read 1-byte words for this implementation
MemoryByte[] memoryBytes = getMemoryCache(context).getMemory(tcfMemoryService, context, address.add(offset), 1, count * word_size,
getTCFTimeout());
// hide breakpoints inserted in the memory by debugger
Breakpoints bpService = getService(Breakpoints.class);
bpService.removeBreakpointFromMemoryBuffer(address.add(offset), memoryBytes);
if (RunControl.timeStepping())
System.out.println("Time since stepping start: " +
((System.currentTimeMillis() - RunControl.getSteppingStartTime()) / 1000.0));
if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
return memoryBytes;
}
public IStatus getMemory(IEDCExecutionDMC context, IAddress address,
ArrayList<MemoryByte> memBuffer, int count, int word_size) {
try {
MemoryByte[] memArray = getMemory(context, address, 0, count, word_size);
memBuffer.addAll(Arrays.asList(memArray));
} catch (CoreException e) {
return new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, INTERNAL_ERROR,
"Error reading memory from: " + address.toHexAddressString(), null);
}
return Status.OK_STATUS;
}
public void flushCache(IDMContext context) {
if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { context })); }
if (isSnapshot())
return;
if (context == null) {
// reset every cache in each context
for (String key : memoryCaches.keySet()) {
memoryCaches.get(key).reset();
}
} else {
IMemoryDMContext memoryDMC = DMContexts.getAncestorOfType(context, IMemoryDMContext.class);
if (memoryCaches.containsKey(((IEDCDMContext) memoryDMC).getID())) {
// We do not want to use the call to getMemoryCache() here.
// This is because:
// 1- if there is not an entry already , we do not want to
// automatically
// create one, just to call reset() on it.
// 2- if memoryDMC == null, we do not want to create a cache
// entry for which the key is 'null'
memoryCaches.get(((IEDCDMContext) memoryDMC).getID()).reset();
}
}
if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
}
public IStatus setMemory(IMemoryDMContext context, IAddress address, int word_size, int count, byte[] buffer) {
if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { address.toHexAddressString(), count })); }
final IStatus[] ret = new IStatus[] { Status.OK_STATUS };
setMemory(context, address, 0, word_size, count, buffer, new RequestMonitor(ImmediateExecutor
.getInstance(), null) {
@Override
protected void handleFailure() {
ret[0] = getStatus();
}
});
if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceExit(null, EDCTrace.fixArg(ret[0])); }
return ret[0];
}
public void tcfServiceReady(IService service) {
if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { service })); }
tcfMemoryService = (org.eclipse.tm.tcf.services.IMemory) service;
if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
}
@DsfServiceEventHandler
public void eventDispatched(ISuspendedDMEvent e) {
if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { e.getClass() })); }
flushCache(e.getDMContext());
if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
}
@DsfServiceEventHandler
public void eventDispatched(IResumedDMEvent e) {
if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { e.getClass() })); }
if (e.getReason() != StateChangeReason.STEP) {
flushCache(e.getDMContext());
}
if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
}
@DsfServiceEventHandler
public void eventDispatched(IExpressionChangedDMEvent e) {
if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { e.getClass() })); }
// Get the context and expression service handle
final IExpressionDMContext context = e.getDMContext();
IExpressions expressionService = getService(IExpressions.class);
// Get the variable information and update the corresponding memory
// locations
if (expressionService != null) {
expressionService.getExpressionAddressData(context, new DataRequestMonitor<IExpressionDMAddress>(
getExecutor(), null) {
@Override
protected void handleSuccess() {
// Figure out which memory area was modified
IExpressionDMAddress expression = getData();
final int count = expression.getSize();
Object expAddress = expression.getAddress();
final Addr64 address;
if (expAddress instanceof Addr64)
address = (Addr64) expAddress;
else if (expAddress instanceof IAddress)
address = new Addr64(((IAddress) expAddress).getValue());
else
return; // not a valid memory address
final IMemoryDMContext memoryDMC = DMContexts.getAncestorOfType(context, IMemoryDMContext.class);
boolean modified = getMemoryCache(memoryDMC).refreshMemory(tcfMemoryService, memoryDMC, address, 0,
1, count, new RequestMonitor(getExecutor(), null), getTCFTimeout());
if (modified) {
// we've modified cache - send an event
IAddress[] addresses = new IAddress[count];
for (int i = 0; i < count; i++) {
addresses[i] = address.add(i);
}
getSession().dispatchEvent(new MemoryChangedEvent(memoryDMC, addresses), getProperties());
}
}
});
}
if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
}
private static final String MEMORY_CONTEXT = "memory_context";
private static final String MEMORY = "memory";
private static final String CONTEXT_ID = "context_id";
public void loadSnapshot(Element element) throws Exception {
memoryCaches = new HashMap<String, MemoryCache>();
NodeList contextElements = element.getElementsByTagName(MEMORY_CONTEXT);
int numContexts = contextElements.getLength();
for (int i = 0; i < numContexts; i++) {
Element contextElement = (Element) contextElements.item(i);
String contextID = contextElement.getAttribute(CONTEXT_ID);
MemoryCache cache = new MemoryCache(getTargetEnvironmentService().getMemoryCacheMinimumBlockSize());
cache.loadSnapshot(contextElement);
memoryCaches.put(contextID, cache);
}
}
/* (non-Javadoc)
* @see org.eclipse.cdt.debug.edc.snapshot.ISnapshotContributor#takeShapshot(org.eclipse.cdt.debug.edc.snapshot.IAlbum, org.w3c.dom.Document, org.eclipse.core.runtime.IProgressMonitor)
*/
public Element takeSnapshot(IAlbum album, Document document, IProgressMonitor monitor) {
Element memoryElement = document.createElement(MEMORY);
SubMonitor progress = SubMonitor.convert(monitor, memoryCaches.keySet().size() * 1000);
progress.subTask("Memory");
for (String key : memoryCaches.keySet()) {
MemoryCache cache = memoryCaches.get(key);
Element memoryCacheElement = document.createElement(MEMORY_CONTEXT);
memoryCacheElement.setAttribute(CONTEXT_ID, key);
memoryCacheElement.appendChild(cache.takeSnapshot(album, document, progress.newChild(1000)));
memoryElement.appendChild(memoryCacheElement);
}
return memoryElement;
}
public void setTCFTimeout(long msecs) {
tcfTimeout = msecs;
}
public long getTCFTimeout() {
return tcfTimeout;
}
// Implementation of org.eclipse.cdt.dsf.debug.service.IMemory
/* (non-Javadoc)
* @see org.eclipse.cdt.dsf.debug.service.IMemory#fillMemory(org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryDMContext, org.eclipse.cdt.core.IAddress, long, int, int, byte[], org.eclipse.cdt.dsf.concurrent.RequestMonitor)
*/
public void fillMemory(final IMemoryDMContext context, final IAddress address, final long offset, final int word_size, final int count,
final byte[] pattern, final RequestMonitor rm) {
asyncExec(new Runnable() {
public void run() {
if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { address.toHexAddressString(), offset, word_size, count })); }
// Validate the context
if (context == null) {
rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, INTERNAL_ERROR,
"Unknown context type", null)); //$NON-NLS-1$;
rm.done();
return;
}
// Validate the word size
if (word_size < 1) {
rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, NOT_SUPPORTED,
"Word size not supported (< 1)", null)); //$NON-NLS-1$
rm.done();
return;
}
// Validate the repeat count
if (count < 0) {
rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR,
"Invalid repeat count (< 0)", null)); //$NON-NLS-1$
rm.done();
return;
}
// Validate the pattern
if (pattern.length < 1) {
rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR,
"Empty pattern", null)); //$NON-NLS-1$
rm.done();
return;
}
// Create an aggregate buffer so we can write in 1 shot
final int length = pattern.length;
final byte[] buffer = new byte[count * length];
for (int i = 0; i < count; i++) {
System.arraycopy(pattern, 0, buffer, i * length, length);
}
// All is clear: go for it
// NOTE: We normalize word_size and count to read 1-byte words for this implementation
try {
getMemoryCache(context).setMemory(tcfMemoryService, context, address, offset, 1, count * length * word_size,
buffer, getTCFTimeout());
} catch (CoreException e) {
EDCDebugger.getMessageLogger().log(e.getStatus());
rm.setStatus(e.getStatus());
}
rm.done();
if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
}
}, rm);
}
/* (non-Javadoc)
* @see org.eclipse.cdt.dsf.debug.service.IMemory#getMemory(org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryDMContext, org.eclipse.cdt.core.IAddress, long, int, int, org.eclipse.cdt.dsf.concurrent.DataRequestMonitor)
*/
public void getMemory(final IMemoryDMContext context, final IAddress address, final long offset,
final int word_size, final int count, final DataRequestMonitor<MemoryByte[]> drm) {
asyncExec(new Runnable() {
public void run() {
if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { address.toHexAddressString(), offset, word_size, count })); }
// NOTE: We normalize word_size and count to read 1-byte words for this implementation
try {
MemoryByte[] memoryBytes = getMemory(context, address, offset, word_size, count);
drm.setData(memoryBytes);
} catch (CoreException e) {
EDCDebugger.getMessageLogger().log(e.getStatus());
drm.setStatus(e.getStatus());
}
drm.done();
if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
}
}, drm);
}
/* (non-Javadoc)
* @see org.eclipse.cdt.dsf.debug.service.IMemory#setMemory(org.eclipse.cdt.dsf.debug.service.IMemory.IMemoryDMContext, org.eclipse.cdt.core.IAddress, long, int, int, byte[], org.eclipse.cdt.dsf.concurrent.RequestMonitor)
*/
public void setMemory(final IMemoryDMContext context, final IAddress address, final long offset,
final int word_size, final int count, final byte[] buffer, final RequestMonitor rm) {
asyncExec(new Runnable() {
public void run() {
if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceEntry(null, EDCTrace.fixArgs(new Object[] { address.toHexAddressString(), offset, word_size, count })); }
// Validate the context
if (context == null) {
rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, INTERNAL_ERROR,
"Unknown context type", null)); //$NON-NLS-1$);
rm.done();
return;
}
// Validate the word size
// NOTE: We only accept 1 byte words for this implementation
if (word_size != 1) {
rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, NOT_SUPPORTED,
"Word size not supported (!= 1)", null)); //$NON-NLS-1$
rm.done();
return;
}
// Validate the byte count
if (count < 0) {
rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR,
"Invalid word count (< 0)", null)); //$NON-NLS-1$
rm.done();
return;
}
// Validate the buffer size
if (buffer.length < count) {
rm.setStatus(new Status(IStatus.ERROR, EDCDebugger.PLUGIN_ID, IDsfStatusConstants.INTERNAL_ERROR,
"Buffer too short", null)); //$NON-NLS-1$
rm.done();
return;
}
// everything ok
// NOTE: We normalize word_size and count to read 1-byte words for this implementation
try {
getMemoryCache(context).setMemory(tcfMemoryService, context, address, offset, word_size, count, buffer, getTCFTimeout());
if (rm.isSuccess()) {
// we've modified memory - send an event
IAddress[] addresses = new IAddress[count];
for (int i = 0; i < count; i++) {
addresses[i] = address.add(offset + i);
}
getSession().dispatchEvent(new MemoryChangedEvent(context, addresses), getProperties());
}
} catch (CoreException e) {
EDCDebugger.getMessageLogger().log(e.getStatus());
rm.setStatus(e.getStatus());
}
rm.done();
if (EDCTrace.MEMORY_TRACE_ON) { EDCTrace.getTrace().traceExit(null); }
}
}, rm);
}
}