1
0
Fork 0
mirror of https://github.com/eclipse-cdt/cdt synced 2025-04-23 22:52:11 +02:00

Bug 542488: Memory view support

Change-Id: I8c3343f0ead46ab203d58f77370dd2dadda550ff
This commit is contained in:
Jonah Graham 2019-07-10 00:22:12 -04:00
parent d7b7f10255
commit bf327ec364
8 changed files with 667 additions and 2 deletions

View file

@ -23,7 +23,11 @@ Require-Bundle: org.apache.commons.io,
org.eclipse.lsp4e.debug, org.eclipse.lsp4e.debug,
org.eclipse.debug.core, org.eclipse.debug.core,
org.eclipse.debug.ui, 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 Bundle-Vendor: %Bundle-Vendor
Export-Package: org.eclipse.cdt.debug.dap Export-Package: org.eclipse.cdt.debug.dap
Bundle-Activator: org.eclipse.cdt.debug.dap.Activator Bundle-Activator: org.eclipse.cdt.debug.dap.Activator

View file

@ -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();
}
}

View file

@ -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;
}
}
}

View file

@ -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<String, Object> dspParameters) {
super(launch, processCleanup, in, out, dspParameters);
}
@Override
protected Launcher<? extends IDebugProtocolServer> createLauncher(UnaryOperator<MessageConsumer> wrapper,
InputStream in, OutputStream out, ExecutorService threadPool) {
Launcher<ICDTDebugProtocolServer> 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<MemoryContents> 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);
}
}

View file

@ -11,6 +11,7 @@
package org.eclipse.cdt.debug.dap; package org.eclipse.cdt.debug.dap;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.net.URL; import java.net.URL;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; 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.IProgressMonitor;
import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.debug.core.DebugPlugin; import org.eclipse.debug.core.DebugPlugin;
import org.eclipse.debug.core.ILaunch; import org.eclipse.debug.core.ILaunch;
import org.eclipse.debug.core.ILaunchConfiguration; import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.debug.core.Launch; 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.model.IPersistableSourceLocator;
import org.eclipse.debug.core.sourcelookup.IPersistableSourceLocator2; import org.eclipse.debug.core.sourcelookup.IPersistableSourceLocator2;
import org.eclipse.jface.dialogs.ErrorDialog; import org.eclipse.jface.dialogs.ErrorDialog;
@ -82,7 +85,18 @@ public class DapLaunchDelegate extends AbstractCLaunchDelegate2 {
builder.setMonitorDebugAdapter(true); builder.setMonitorDebugAdapter(true);
builder.setDspParameters(param); 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<String, Object> 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) { } catch (IOException e) {
IStatus errorStatus = new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), e); IStatus errorStatus = new Status(IStatus.ERROR, Activator.PLUGIN_ID, e.getMessage(), e);
Activator.getDefault().getLog().log(errorStatus); Activator.getDefault().getLog().log(errorStatus);

View file

@ -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 {
}

View file

@ -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<MemoryContents> memory(MemoryRequestArguments args) {
throw new UnsupportedOperationException();
}
}

View file

@ -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<Object> 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<MemoryContents> 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;
}
}