From bf327ec364d279122e22f3ae810fad677311e01f Mon Sep 17 00:00:00 2001 From: Jonah Graham Date: Wed, 10 Jul 2019 00:22:12 -0400 Subject: [PATCH] Bug 542488: Memory view support Change-Id: I8c3343f0ead46ab203d58f77370dd2dadda550ff --- .../META-INF/MANIFEST.MF | 6 +- .../cdt/debug/dap/CDTDebugElement.java | 26 ++ .../cdt/debug/dap/CDTDebugProtocol.java | 261 ++++++++++++++++++ .../eclipse/cdt/debug/dap/DapDebugTarget.java | 98 +++++++ .../cdt/debug/dap/DapLaunchDelegate.java | 16 +- .../debug/dap/ICDTDebugProtocolClient.java | 17 ++ .../debug/dap/ICDTDebugProtocolServer.java | 30 ++ .../eclipse/cdt/debug/dap/MemoryBlock.java | 215 +++++++++++++++ 8 files changed, 667 insertions(+), 2 deletions(-) create mode 100644 debug/org.eclipse.cdt.debug.dap/src/org/eclipse/cdt/debug/dap/CDTDebugElement.java create mode 100644 debug/org.eclipse.cdt.debug.dap/src/org/eclipse/cdt/debug/dap/CDTDebugProtocol.java create mode 100644 debug/org.eclipse.cdt.debug.dap/src/org/eclipse/cdt/debug/dap/DapDebugTarget.java create mode 100644 debug/org.eclipse.cdt.debug.dap/src/org/eclipse/cdt/debug/dap/ICDTDebugProtocolClient.java create mode 100644 debug/org.eclipse.cdt.debug.dap/src/org/eclipse/cdt/debug/dap/ICDTDebugProtocolServer.java create mode 100644 debug/org.eclipse.cdt.debug.dap/src/org/eclipse/cdt/debug/dap/MemoryBlock.java diff --git a/debug/org.eclipse.cdt.debug.dap/META-INF/MANIFEST.MF b/debug/org.eclipse.cdt.debug.dap/META-INF/MANIFEST.MF index 7f36898c2c6..a122b314508 100644 --- a/debug/org.eclipse.cdt.debug.dap/META-INF/MANIFEST.MF +++ b/debug/org.eclipse.cdt.debug.dap/META-INF/MANIFEST.MF @@ -23,7 +23,11 @@ Require-Bundle: org.apache.commons.io, org.eclipse.lsp4e.debug, org.eclipse.debug.core, org.eclipse.debug.ui, - org.eclipse.cdt.debug.core + org.eclipse.cdt.debug.core, + org.eclipse.lsp4j.debug, + org.eclipse.lsp4j.jsonrpc.debug, + com.google.guava, + org.eclipse.xtext.xbase.lib Bundle-Vendor: %Bundle-Vendor Export-Package: org.eclipse.cdt.debug.dap Bundle-Activator: org.eclipse.cdt.debug.dap.Activator diff --git a/debug/org.eclipse.cdt.debug.dap/src/org/eclipse/cdt/debug/dap/CDTDebugElement.java b/debug/org.eclipse.cdt.debug.dap/src/org/eclipse/cdt/debug/dap/CDTDebugElement.java new file mode 100644 index 00000000000..6205e48e88d --- /dev/null +++ b/debug/org.eclipse.cdt.debug.dap/src/org/eclipse/cdt/debug/dap/CDTDebugElement.java @@ -0,0 +1,26 @@ +/******************************************************************************* + * Copyright (c) 2019 Kichwa Coders and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.cdt.debug.dap; + +import org.eclipse.lsp4e.debug.debugmodel.DSPDebugElement; +import org.eclipse.lsp4e.debug.debugmodel.DSPDebugTarget; + +public class CDTDebugElement extends DSPDebugElement { + public CDTDebugElement(DSPDebugTarget target) { + super(target); + } + + @Override + public ICDTDebugProtocolServer getDebugProtocolServer() { + // TODO Auto-generated method stub + return (ICDTDebugProtocolServer) super.getDebugProtocolServer(); + } +} diff --git a/debug/org.eclipse.cdt.debug.dap/src/org/eclipse/cdt/debug/dap/CDTDebugProtocol.java b/debug/org.eclipse.cdt.debug.dap/src/org/eclipse/cdt/debug/dap/CDTDebugProtocol.java new file mode 100644 index 00000000000..1d65e233653 --- /dev/null +++ b/debug/org.eclipse.cdt.debug.dap/src/org/eclipse/cdt/debug/dap/CDTDebugProtocol.java @@ -0,0 +1,261 @@ +/******************************************************************************* + * Copyright (c) 2019 Kichwa Coders and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.cdt.debug.dap; + +import org.eclipse.lsp4j.jsonrpc.validation.NonNull; +import org.eclipse.xtext.xbase.lib.Pure; +import org.eclipse.xtext.xbase.lib.util.ToStringBuilder; + +public class CDTDebugProtocol { + + /** + * https://github.com/eclipse-cdt/cdt-gdb-adapter/blob/5d788cbbc6ace142b0930375fcd931b4241eddbb/src/GDBDebugSession.ts#L73 + * export interface MemoryResponse extends Response { + * body: MemoryContents; + * } + */ + public static class MemoryRequestResponse { + @NonNull + private MemoryContents body; + + @Pure + @NonNull + public MemoryContents getBody() { + return this.body; + } + + public void setBody(@NonNull final MemoryContents address) { + if (address == null) { + throw new IllegalArgumentException("Property must not be null: body"); //$NON-NLS-1$ + } + this.body = address; + } + + @Override + @Pure + public String toString() { + ToStringBuilder b = new ToStringBuilder(this); + b.add("body", this.body); //$NON-NLS-1$ + return b.toString(); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((body == null) ? 0 : body.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + MemoryRequestResponse other = (MemoryRequestResponse) obj; + if (body == null) { + if (other.body != null) + return false; + } else if (!body.equals(other.body)) + return false; + return true; + } + } + + /** + * https://github.com/eclipse-cdt/cdt-gdb-adapter/blob/5d788cbbc6ace142b0930375fcd931b4241eddbb/src/GDBDebugSession.ts#L67 + * export interface MemoryContents { + * /\* Hex-encoded string of bytes. *\/ + * data: string; + * address: string; + * } + */ + public static class MemoryContents { + @NonNull + private String data; + @NonNull + private String address; + + @Pure + @NonNull + public String getData() { + return this.data; + } + + public void setData(@NonNull final String address) { + if (address == null) { + throw new IllegalArgumentException("Property must not be null: data"); //$NON-NLS-1$ + } + this.data = address; + } + + @Pure + @NonNull + public String getAddress() { + return this.address; + } + + public void setAddress(@NonNull final String address) { + if (address == null) { + throw new IllegalArgumentException("Property must not be null: address"); //$NON-NLS-1$ + } + this.address = address; + } + + @Override + @Pure + public String toString() { + ToStringBuilder b = new ToStringBuilder(this); + b.add("data", this.data); //$NON-NLS-1$ + b.add("address", this.address); //$NON-NLS-1$ + return b.toString(); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((address == null) ? 0 : address.hashCode()); + result = prime * result + ((data == null) ? 0 : data.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + MemoryContents other = (MemoryContents) obj; + if (address == null) { + if (other.address != null) + return false; + } else if (!address.equals(other.address)) + return false; + if (data == null) { + if (other.data != null) + return false; + } else if (!data.equals(other.data)) + return false; + return true; + } + } + + /** + * https://github.com/eclipse-cdt/cdt-gdb-adapter/blob/5d788cbbc6ace142b0930375fcd931b4241eddbb/src/GDBDebugSession.ts#L58 + * export interface MemoryRequestArguments { + * address: string; + * length: number; + * offset?: number; + * } + * + */ + public static class MemoryRequestArguments { + @NonNull + private String address; + @NonNull + private Long length; + private Long offset; + + @Pure + @NonNull + public String getAddress() { + return this.address; + } + + public void setAddress(@NonNull final String address) { + if (address == null) { + throw new IllegalArgumentException("Property must not be null: address"); //$NON-NLS-1$ + } + this.address = address; + } + + @Pure + @NonNull + public Long getLength() { + return this.length; + } + + public void setLength(@NonNull final Long length) { + if (length == null) { + throw new IllegalArgumentException("Property must not be null: length"); //$NON-NLS-1$ + } + this.length = length; + } + + @Pure + public Long getOffset() { + return this.offset; + } + + public void setOffset(@NonNull final Long length) { + if (length == null) { + throw new IllegalArgumentException("Property must not be null: offset"); //$NON-NLS-1$ + } + this.offset = length; + } + + @Override + @Pure + public String toString() { + ToStringBuilder b = new ToStringBuilder(this); + b.add("address", this.address); //$NON-NLS-1$ + b.add("length", this.length); //$NON-NLS-1$ + b.add("offset", this.offset); //$NON-NLS-1$ + return b.toString(); + } + + @Override + @Pure + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + ((address == null) ? 0 : address.hashCode()); + result = prime * result + ((length == null) ? 0 : length.hashCode()); + result = prime * result + ((offset == null) ? 0 : offset.hashCode()); + return result; + } + + @Override + @Pure + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + MemoryRequestArguments other = (MemoryRequestArguments) obj; + if (address == null) { + if (other.address != null) + return false; + } else if (!address.equals(other.address)) + return false; + if (length == null) { + if (other.length != null) + return false; + } else if (!length.equals(other.length)) + return false; + if (offset == null) { + if (other.offset != null) + return false; + } else if (!offset.equals(other.offset)) + return false; + return true; + } + + } +} \ No newline at end of file diff --git a/debug/org.eclipse.cdt.debug.dap/src/org/eclipse/cdt/debug/dap/DapDebugTarget.java b/debug/org.eclipse.cdt.debug.dap/src/org/eclipse/cdt/debug/dap/DapDebugTarget.java new file mode 100644 index 00000000000..419e125d3e8 --- /dev/null +++ b/debug/org.eclipse.cdt.debug.dap/src/org/eclipse/cdt/debug/dap/DapDebugTarget.java @@ -0,0 +1,98 @@ +/******************************************************************************* + * Copyright (c) 2019 Kichwa Coders and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.cdt.debug.dap; + +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigInteger; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.function.UnaryOperator; + +import org.eclipse.cdt.debug.dap.CDTDebugProtocol.MemoryContents; +import org.eclipse.cdt.debug.dap.CDTDebugProtocol.MemoryRequestArguments; +import org.eclipse.core.runtime.IStatus; +import org.eclipse.core.runtime.Status; +import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.ILaunch; +import org.eclipse.debug.core.model.IMemoryBlock; +import org.eclipse.debug.core.model.IMemoryBlockExtension; +import org.eclipse.debug.core.model.IMemoryBlockRetrievalExtension; +import org.eclipse.lsp4e.debug.debugmodel.DSPDebugTarget; +import org.eclipse.lsp4j.debug.services.IDebugProtocolServer; +import org.eclipse.lsp4j.jsonrpc.Launcher; +import org.eclipse.lsp4j.jsonrpc.MessageConsumer; +import org.eclipse.lsp4j.jsonrpc.debug.DebugLauncher; + +public class DapDebugTarget extends DSPDebugTarget implements IMemoryBlockRetrievalExtension, ICDTDebugProtocolClient { + + public DapDebugTarget(ILaunch launch, Runnable processCleanup, InputStream in, OutputStream out, + Map dspParameters) { + super(launch, processCleanup, in, out, dspParameters); + } + + @Override + protected Launcher createLauncher(UnaryOperator wrapper, + InputStream in, OutputStream out, ExecutorService threadPool) { + Launcher debugProtocolLauncher = DebugLauncher.createLauncher(this, + ICDTDebugProtocolServer.class, in, out, threadPool, wrapper); + return debugProtocolLauncher; + } + + @Override + public ICDTDebugProtocolServer getDebugProtocolServer() { + return (ICDTDebugProtocolServer) super.getDebugProtocolServer(); + } + + @Override + public boolean supportsStorageRetrieval() { + return true; + } + + @Override + public IMemoryBlock getMemoryBlock(long startAddress, long length) throws DebugException { + throw new DebugException(new Status(IStatus.ERROR, Activator.PLUGIN_ID, DebugException.NOT_SUPPORTED, + "getMemoryBlock() not supported, use getExtendedMemoryBlock()", null)); //$NON-NLS-1$ + } + + @Override + public IMemoryBlockExtension getExtendedMemoryBlock(String expression, Object context) throws DebugException { + BigInteger bigBaseAddress; + /* + * See if the expression is a simple numeric value; if it is, we can + * avoid some costly processing (calling the back-end to resolve the + * expression and obtain an address) + */ + try { + // Now, try to parse the expression. If a NumberFormatException is + // thrown, then it wasn't a simple numerical expression and we give + // up as debug adapter hasn't provided us what was needed + bigBaseAddress = BigInteger.valueOf(Long.decode(expression)); + + } catch (NumberFormatException nfexc) { + MemoryRequestArguments memoryRequestArguments = new MemoryRequestArguments(); + memoryRequestArguments.setAddress(expression); + memoryRequestArguments.setLength(1L); + CompletableFuture memory = getDebugProtocolServer().memory(memoryRequestArguments); + MemoryContents body = complete(memory); + String address = body.getAddress(); + try { + bigBaseAddress = BigInteger.valueOf(Long.decode(address)); + } catch (NumberFormatException e) { + // still no resolvable address + return null; + } + } + return new MemoryBlock(this, expression, bigBaseAddress, context); + } + +} diff --git a/debug/org.eclipse.cdt.debug.dap/src/org/eclipse/cdt/debug/dap/DapLaunchDelegate.java b/debug/org.eclipse.cdt.debug.dap/src/org/eclipse/cdt/debug/dap/DapLaunchDelegate.java index d8ea4d8823d..5000cecd6a9 100644 --- a/debug/org.eclipse.cdt.debug.dap/src/org/eclipse/cdt/debug/dap/DapLaunchDelegate.java +++ b/debug/org.eclipse.cdt.debug.dap/src/org/eclipse/cdt/debug/dap/DapLaunchDelegate.java @@ -11,6 +11,7 @@ package org.eclipse.cdt.debug.dap; import java.io.IOException; +import java.io.InputStream; import java.net.URL; import java.util.Collections; import java.util.HashMap; @@ -24,10 +25,12 @@ import org.eclipse.core.runtime.FileLocator; 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.DebugPlugin; import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.Launch; +import org.eclipse.debug.core.model.IDebugTarget; import org.eclipse.debug.core.model.IPersistableSourceLocator; import org.eclipse.debug.core.sourcelookup.IPersistableSourceLocator2; import org.eclipse.jface.dialogs.ErrorDialog; @@ -82,7 +85,18 @@ public class DapLaunchDelegate extends AbstractCLaunchDelegate2 { builder.setMonitorDebugAdapter(true); builder.setDspParameters(param); - new DSPLaunchDelegate().launch(builder); + DSPLaunchDelegate dspLaunchDelegate = new DSPLaunchDelegate() { + @Override + protected IDebugTarget createDebugTarget(SubMonitor subMonitor, Runnable cleanup, + InputStream inputStream, java.io.OutputStream outputStream, ILaunch launch, + Map dspParameters) throws CoreException { + DapDebugTarget target = new DapDebugTarget(launch, cleanup, inputStream, outputStream, + dspParameters); + target.initialize(subMonitor.split(80)); + return target; + } + }; + dspLaunchDelegate.launch(builder); } catch (IOException e) { IStatus errorStatus = new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), e); Activator.getDefault().getLog().log(errorStatus); diff --git a/debug/org.eclipse.cdt.debug.dap/src/org/eclipse/cdt/debug/dap/ICDTDebugProtocolClient.java b/debug/org.eclipse.cdt.debug.dap/src/org/eclipse/cdt/debug/dap/ICDTDebugProtocolClient.java new file mode 100644 index 00000000000..f2cd5de56a7 --- /dev/null +++ b/debug/org.eclipse.cdt.debug.dap/src/org/eclipse/cdt/debug/dap/ICDTDebugProtocolClient.java @@ -0,0 +1,17 @@ +/******************************************************************************* + * Copyright (c) 2019 Kichwa Coders and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.cdt.debug.dap; + +import org.eclipse.lsp4j.debug.services.IDebugProtocolClient; + +public interface ICDTDebugProtocolClient extends IDebugProtocolClient { + +} diff --git a/debug/org.eclipse.cdt.debug.dap/src/org/eclipse/cdt/debug/dap/ICDTDebugProtocolServer.java b/debug/org.eclipse.cdt.debug.dap/src/org/eclipse/cdt/debug/dap/ICDTDebugProtocolServer.java new file mode 100644 index 00000000000..11c7593cb6a --- /dev/null +++ b/debug/org.eclipse.cdt.debug.dap/src/org/eclipse/cdt/debug/dap/ICDTDebugProtocolServer.java @@ -0,0 +1,30 @@ +/******************************************************************************* + * Copyright (c) 2019 Kichwa Coders and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.cdt.debug.dap; + +import java.util.concurrent.CompletableFuture; + +import org.eclipse.cdt.debug.dap.CDTDebugProtocol.MemoryContents; +import org.eclipse.cdt.debug.dap.CDTDebugProtocol.MemoryRequestArguments; +import org.eclipse.lsp4j.debug.services.IDebugProtocolServer; +import org.eclipse.lsp4j.jsonrpc.services.JsonRequest; + +public interface ICDTDebugProtocolServer extends IDebugProtocolServer { + + /** + * Request a memory block + */ + @JsonRequest(value = "cdt-gdb-adapter/Memory") + default CompletableFuture memory(MemoryRequestArguments args) { + throw new UnsupportedOperationException(); + } + +} diff --git a/debug/org.eclipse.cdt.debug.dap/src/org/eclipse/cdt/debug/dap/MemoryBlock.java b/debug/org.eclipse.cdt.debug.dap/src/org/eclipse/cdt/debug/dap/MemoryBlock.java new file mode 100644 index 00000000000..3a0c22d604e --- /dev/null +++ b/debug/org.eclipse.cdt.debug.dap/src/org/eclipse/cdt/debug/dap/MemoryBlock.java @@ -0,0 +1,215 @@ +/******************************************************************************* + * Copyright (c) 2019 Kichwa Coders and others. + * + * This program and the accompanying materials + * are made available under the terms of the Eclipse Public License 2.0 + * which accompanies this distribution, and is available at + * https://www.eclipse.org/legal/epl-2.0/ + * + * SPDX-License-Identifier: EPL-2.0 + *******************************************************************************/ +package org.eclipse.cdt.debug.dap; + +import java.math.BigInteger; +import java.util.ArrayList; +import java.util.concurrent.CompletableFuture; + +import org.eclipse.cdt.debug.dap.CDTDebugProtocol.MemoryContents; +import org.eclipse.cdt.debug.dap.CDTDebugProtocol.MemoryRequestArguments; +import org.eclipse.debug.core.DebugException; +import org.eclipse.debug.core.model.IMemoryBlockExtension; +import org.eclipse.debug.core.model.IMemoryBlockRetrieval; +import org.eclipse.debug.core.model.MemoryByte; + +public class MemoryBlock extends CDTDebugElement implements IMemoryBlockExtension { + private ArrayList connections = new ArrayList<>(); + + @SuppressWarnings("unused") + private boolean isEnabled; + + @SuppressWarnings("unused") + private Object context; + + private DapDebugTarget debugTarget; + + private BigInteger bigBaseAddress; + + private String expression; + + public MemoryBlock(DapDebugTarget debugTarget, String expression, BigInteger bigBaseAddress, Object context) { + super(debugTarget); + this.debugTarget = debugTarget; + this.expression = expression; + this.bigBaseAddress = bigBaseAddress; + this.context = context; + } + + @Override + public long getStartAddress() { + // Not implemented (obsoleted by IMemoryBlockExtension) + return 0; + } + + @Override + public long getLength() { + // Not implemented (obsoleted by IMemoryBlockExtension) + return 0; + } + + @Override + public byte[] getBytes() throws DebugException { + // Not implemented (obsoleted by IMemoryBlockExtension) + return new byte[0]; + } + + @Override + public boolean supportsValueModification() { + // TODO + return false; + } + + @Override + public void setValue(long offset, byte[] bytes) throws DebugException { + // Not implemented (obsoleted by IMemoryBlockExtension) + } + + @Override + public String getExpression() { + return expression; + } + + @Override + public BigInteger getBigBaseAddress() throws DebugException { + return bigBaseAddress; + } + + @Override + public BigInteger getMemoryBlockStartAddress() throws DebugException { + // Null indicates that memory can be retrieved at addresses lower than the block base address + return null; + } + + @Override + public BigInteger getMemoryBlockEndAddress() throws DebugException { + // Null indicates that memory can be retrieved at addresses higher the block base address + return null; + } + + @Override + public BigInteger getBigLength() throws DebugException { + // -1 indicates that memory block is unbounded + return BigInteger.valueOf(-1); + } + + @Override + public int getAddressSize() throws DebugException { + // TODO Get this from backend + return 8; + } + + @Override + public int getAddressableSize() throws DebugException { + // TODO Get this from backend + return 1; + } + + @Override + public boolean supportBaseAddressModification() throws DebugException { + return false; + } + + @Override + public boolean supportsChangeManagement() { + return false; + } + + @Override + public void setBaseAddress(BigInteger address) throws DebugException { + // Not supported + } + + @Override + public MemoryByte[] getBytesFromOffset(BigInteger offset, long units) throws DebugException { + return getBytesFromAddress(getBigBaseAddress().add(offset), units); + } + + @Override + public MemoryByte[] getBytesFromAddress(BigInteger address, long units) throws DebugException { + MemoryRequestArguments memoryRequestArguments = new MemoryRequestArguments(); + memoryRequestArguments.setAddress("0x" + address.toString(16)); + memoryRequestArguments.setLength(units); + CompletableFuture memory = getDebugProtocolServer().memory(memoryRequestArguments); + MemoryContents body = complete(memory); + String resultAddress = body.getAddress(); + String contents = body.getData(); + BigInteger bigResultAddress; + BigInteger.valueOf(Long.decode(resultAddress)); + try { + bigResultAddress = BigInteger.valueOf(Long.decode(resultAddress)); + } catch (NumberFormatException nfexc) { + // TODO + bigResultAddress = address; + } + int numRequestedBytes = (int) (units * getAddressableSize()); + MemoryByte[] bytes = new MemoryByte[numRequestedBytes]; + int resultOffsetFromRequest = bigResultAddress.subtract(address).intValue(); + + for (int i = 0; i < resultOffsetFromRequest; i++) { + bytes[i] = new MemoryByte((byte) 0, (byte) (0 & ~MemoryByte.READABLE)); + } + for (int i = resultOffsetFromRequest, k = 0; i < resultOffsetFromRequest + (contents.length() / 2) + && i < numRequestedBytes; i++, k += 2) { + byte b = (byte) Integer.parseInt(contents.substring(k, k + 2), 16); + bytes[i] = new MemoryByte(b); + } + for (int i = resultOffsetFromRequest + (contents.length() / 2); i < numRequestedBytes; i++) { + bytes[i] = new MemoryByte((byte) 0, (byte) (0 & ~MemoryByte.READABLE)); + } + return bytes; + } + + @Override + public void setValue(BigInteger offset, byte[] bytes) throws DebugException { + // Not supported + } + + @Override + public void dispose() throws DebugException { + // nothing to do yet + } + + @Override + public IMemoryBlockRetrieval getMemoryBlockRetrieval() { + return debugTarget; + } + + @Override + public void connect(Object client) { + if (!connections.contains(client)) + connections.add(client); + if (connections.size() == 1) + enable(); + } + + @Override + public void disconnect(Object client) { + if (connections.contains(client)) + connections.remove(client); + if (connections.size() == 0) + disable(); + } + + @Override + public Object[] getConnections() { + return connections.toArray(); + } + + private void enable() { + isEnabled = true; + } + + private void disable() { + isEnabled = false; + } + +}