Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--docs/TCF Service - Registers.html115
-rw-r--r--plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/launch/TCFSelfTest.java41
-rw-r--r--plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/internal/tcf/services/local/LocatorService.java95
-rw-r--r--plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/internal/tcf/services/remote/RegistersProxy.java46
-rw-r--r--plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/protocol/JSON.java67
-rw-r--r--plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/services/IRegisters.java37
6 files changed, 343 insertions, 58 deletions
diff --git a/docs/TCF Service - Registers.html b/docs/TCF Service - Registers.html
index 1638c2306..22d84ffe2 100644
--- a/docs/TCF Service - Registers.html
+++ b/docs/TCF Service - Registers.html
@@ -15,6 +15,8 @@
<li><a href='#CmdGetChildren'>Get Children</a>
<li><a href='#CmdSetRegister'>Set Register</a>
<li><a href='#CmdGetRegister'>Get Register</a>
+ <li><a href='#CmdSetMultiple'>Set Multiple Registers</a>
+ <li><a href='#CmdGetMultiple'>Get Multiple Registers</a>
</ul>
<li><a href='#Events'>Events</a>
<li><a href='#API'>API</a>
@@ -27,6 +29,14 @@ and event parameters are encoded as zero terminated <a href='TCF Specification.h
<p>The service uses standard format for error reports,
see <a href='TCF Services.html#ErrorFormat'>Error Report Format</a>.</p>
+
+In addition to commands that can set/get individual register context values, the service defines commands to set/get values at
+multiple locations. This allows:
+<ol>
+ <li> to get/set multiple register contexts in one command
+ <li> to specify offset and size for get/set on large register groups
+ <li> to get/set trunkated register values, e.g. only the low 32 bits of a 64-bit register
+</ol>
<h2><a name='Cmds'>Commands</a></h2>
@@ -187,6 +197,64 @@ R <i>&lt;token&gt;</i> <i>&lt;error report&gt;</i> <i>&lt;string: value&gt
<p>Error report provides integer error code and a short, human readable explanation
of error. Value is BASE64 encoded byte array of binary data. Array size should match the size of the register.</p>
+<h3><a name='CmdSetMultiple'>Set Multiple Registers</a></h3>
+
+<pre><b><font face="Courier New" size=2 color=#333399>
+C &lt;token&gt; Registers setm <i>&lt;array of locations&gt;</i> <i>&lt;string: value&gt;</i>
+
+<i>&lt;array of locations&gt;</i>
+ <font face=Wingdings></font> [ <i>&lt;location list&gt;</i> ]
+
+<i>&lt;location list&gt;</i>
+ <font face=Wingdings></font> <i>&lt;location&gt;</i>
+ <font face=Wingdings></font> <i>&lt;location list&gt;</i> , <i>&lt;location&gt;</i>
+
+<i>&lt;location&gt;</i>
+ <font face=Wingdings></font> [ <i>&lt;string: register context ID&gt;</i> , <i>&lt;int: offset in bytes&gt;</i> , <i>&lt;int: size in bytes&gt;</i> ]
+</font></b></pre>
+
+<p>Writes value into given list of locations in registers. Each location is represented by 3-element array that consists of
+context ID, offset in the context in bytes and value size in bytes.
+Value is BASE64 encoded byte array of binary data. Byte array size should match the sum of location sizes.</p>
+
+<p>Result message:</p>
+
+<pre><b><font face="Courier New" size=2 color=#333399>
+R <i>&lt;token&gt;</i> <i>&lt;error report&gt;</i>
+</font></b></pre>
+
+<p>Error report provides integer error code and a short, human readable explanation
+of error.</p>
+
+<h3><a name='CmdGetMultiple'>Get Multiple Registers</a></h3>
+
+<pre><b><font face="Courier New" size=2 color=#333399>
+C &lt;token&gt; Registers getm <i>&lt;string: context ID&gt;</i>
+
+<i>&lt;array of locations&gt;</i>
+ <font face=Wingdings></font> [ <i>&lt;location list&gt;</i> ]
+
+<i>&lt;location list&gt;</i>
+ <font face=Wingdings></font> <i>&lt;location&gt;</i>
+ <font face=Wingdings></font> <i>&lt;location list&gt;</i> , <i>&lt;location&gt;</i>
+
+<i>&lt;location&gt;</i>
+ <font face=Wingdings></font> [ <i>&lt;string: register context ID&gt;</i> , <i>&lt;int: offset in bytes&gt;</i> , <i>&lt;int: size in bytes&gt;</i> ]
+</font></b></pre>
+
+<p>Reads register values from given list of locations in registers. Each location is represented by 3-element array that consists of
+context ID, offset in the context in bytes and value size in bytes.
+</p>
+
+<p>Result message:</p>
+
+<pre><b><font face="Courier New" size=2 color=#333399>
+R <i>&lt;token&gt;</i> <i>&lt;error report&gt;</i> <i>&lt;string: value&gt;</i>
+</font></b></pre>
+
+<p>Error report provides integer error code and a short, human readable explanation
+of error. Value is BASE64 encoded byte array of binary data. Byte array size should match the sum of location sizes.</p>
+
<h2><a name='Events'>Events</a></h2>
<p>Registers service broadcasts notification events when registers contexts are changed, and when
@@ -231,16 +299,6 @@ E Registers registerChanged <i>&lt;string: context ID&gt;</i>
PROP_VALUES = "Values";
<font color=#3F5FBF>/**
- * Standard known formats for register data.
- */</font>
- <font color=#7F0055>static final</font> String
- FORMAT_BINARY = "Binary",
- FORMAT_OCTAL = "Octal",
- FORMAT_DECIMAL = "Decimal",
- FORMAT_HEX = "Hex",
- FORMAT_NATURAL = "Natural";
-
- <font color=#3F5FBF>/**
* Retrieve context info for given context ID.
*
* <font color=#7F9FBF>@param</font> id context ID.
@@ -452,6 +510,43 @@ E Registers registerChanged <i>&lt;string: context ID&gt;</i>
}
<font color=#3F5FBF>/**
+ * Read values of multiple locations in registers.
+ * <font color=#7F9FBF>@param</font> locs - array of data locations.
+ * <font color=#7F9FBF>@param</font> done - call back object.
+ * <font color=#7F9FBF>@return</font> - pending command handle.
+ */</font>
+ IToken getm(Location[] locs, DoneGet done);
+
+ <font color=#3F5FBF>/**
+ * Set values of multiple locations in registers.
+ * <font color=#7F9FBF>@param</font> locs - array of data locations.
+ * <font color=#7F9FBF>@param</font> value - value to write into the context.
+ * <font color=#7F9FBF>@param</font> done - call back object.
+ * <font color=#7F9FBF>@return</font> - pending command handle.
+ */</font>
+ IToken setm(Location[] locs, byte[] value, DoneSet done);
+
+ <font color=#3F5FBF>/**
+ * Class Location represents value location in register context
+ */</font>
+ <font color=#7F0055>final class</font> Location {
+ <font color=#3F5FBF>/** Register context ID */</font>
+ <font color=#7F0055>public final</font> String id;
+
+ <font color=#3F5FBF>/** offset in the context, in bytes */</font>
+ <font color=#7F0055>public final int</font> offs;
+
+ <font color=#3F5FBF>/** value size in byte */</font>
+ <font color=#7F0055>public final int</font> size;
+
+ <font color=#7F0055>public</font> Location(String id, <font color=#7F0055>int</font> offs, <font color=#7F0055>int</font> size) {
+ <font color=#7F0055>this</font>.id = id;
+ <font color=#7F0055>this</font>.offs = offs;
+ <font color=#7F0055>this</font>.size = size;
+ }
+ }
+
+ <font color=#3F5FBF>/**
* 'get' command call back interface.
*/</font>
<font color=#7F0055>interface</font> DoneGet {
diff --git a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/launch/TCFSelfTest.java b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/launch/TCFSelfTest.java
index 6056358e7..d3942284b 100644
--- a/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/launch/TCFSelfTest.java
+++ b/plugins/org.eclipse.tm.tcf.debug.ui/src/org/eclipse/tm/internal/tcf/debug/ui/launch/TCFSelfTest.java
@@ -20,6 +20,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
+import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.Set;
@@ -951,6 +952,46 @@ class TCFSelfTest {
}
}));
}
+ if (!reg_map.isEmpty()) {
+ Random rnd = new Random();
+ List<IRegisters.Location> locs = new ArrayList<IRegisters.Location>();
+ String[] ids = reg_map.keySet().toArray(new String[reg_map.size()]);
+ for (int i = 0; i < rnd.nextInt(32); i++) {
+ String id = ids[rnd.nextInt(ids.length)];
+ IRegisters.RegistersContext ctx = reg_map.get(id);
+ if (!ctx.isReadable()) continue;
+ if (ctx.isReadOnce()) continue;
+ int offs = rnd.nextInt(ctx.getSize());
+ int size = rnd.nextInt(ctx.getSize() - offs) + 1;
+ locs.add(new IRegisters.Location(id, offs, size));
+ }
+ final IRegisters.Location[] loc_arr = locs.toArray(new IRegisters.Location[locs.size()]);
+ cmds.add(rg.getm(loc_arr, new IRegisters.DoneGet() {
+ public void doneGet(IToken token, Exception error, byte[] value) {
+ cmds.remove(token);
+ if (suspended.get(sc.id) != sc) return;
+ if (error != null) {
+ for (IToken t : cmds) t.cancel();
+ exit(error);
+ return;
+ }
+ cmds.add(rg.setm(loc_arr, value, new IRegisters.DoneSet() {
+ public void doneSet(IToken token, Exception error) {
+ cmds.remove(token);
+ if (suspended.get(sc.id) != sc) return;
+ if (error != null) {
+ for (IToken t : cmds) t.cancel();
+ exit(error);
+ return;
+ }
+ if (cmds.isEmpty()) {
+ resume(sc);
+ }
+ }
+ }));
+ }
+ }));
+ }
if (cmds.isEmpty()) {
resume(sc);
}
diff --git a/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/internal/tcf/services/local/LocatorService.java b/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/internal/tcf/services/local/LocatorService.java
index ace101013..dda4db59e 100644
--- a/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/internal/tcf/services/local/LocatorService.java
+++ b/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/internal/tcf/services/local/LocatorService.java
@@ -10,6 +10,7 @@
*******************************************************************************/
package org.eclipse.tm.internal.tcf.services.local;
+import java.lang.reflect.Method;
import java.net.DatagramPacket;
import java.net.DatagramSocket;
import java.net.InetAddress;
@@ -18,6 +19,7 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
import org.eclipse.tm.internal.tcf.core.LocalPeer;
@@ -174,52 +176,61 @@ public class LocatorService implements ILocator {
* socket.send(new DatagramPacket(buf, buf.length, ia.getBroadcast(), 1534));
* }
*/
- Enumeration<InetAddress> n = f.getInetAddresses();
- while (n.hasMoreElements()) {
- InetAddress ina = n.nextElement();
- byte[] adr = ina.getAddress();
- if (adr.length != 4) {
- // TODO: Support IPv6
- // System.out.println("Dont support IPv6: " + ina);
- continue;
- }
- /* Since we don't know actual broadcast address,
- * lets try different combinations.
- * Hopefully one of them will work.
- */
- int h = adr[0] & 0xff;
- if (h >= 1 && h <= 127 && h != 38) {
- adr[3] = (byte)255;
- socket.send(new DatagramPacket(buf, buf.length,
- InetAddress.getByAddress(null, adr), 1534));
- adr[2] = (byte)255;
- socket.send(new DatagramPacket(buf, buf.length,
- InetAddress.getByAddress(null, adr), 1534));
- adr[1] = (byte)255;
- }
- else if (h >= 128 && h <= 191) {
- adr[3] = (byte)255;
- socket.send(new DatagramPacket(buf, buf.length,
- InetAddress.getByAddress(null, adr), 1534));
- adr[2] = (byte)255;
+ try {
+ Method m0 = f.getClass().getMethod("getInterfaceAddresses");
+ for (Object ia : (List<?>)m0.invoke(f)) {
+ Method m1 = ia.getClass().getMethod("getBroadcast");
+ socket.send(new DatagramPacket(buf, buf.length, (InetAddress)m1.invoke(ia), 1534));
}
- else {
- adr[3] = (byte)(adr[3] | 0x0f);
- socket.send(new DatagramPacket(buf, buf.length,
- InetAddress.getByAddress(null, adr), 1534));
- adr[3] = (byte)(adr[3] | 0x01f);
- socket.send(new DatagramPacket(buf, buf.length,
- InetAddress.getByAddress(null, adr), 1534));
- adr[3] = (byte)(adr[3] | 0x03f);
- socket.send(new DatagramPacket(buf, buf.length,
- InetAddress.getByAddress(null, adr), 1534));
- adr[3] = (byte)(adr[3] | 0x07f);
+ }
+ catch (Exception x) {
+ Enumeration<InetAddress> n = f.getInetAddresses();
+ while (n.hasMoreElements()) {
+ InetAddress ina = n.nextElement();
+ byte[] adr = ina.getAddress();
+ if (adr.length != 4) {
+ // TODO: Support IPv6
+ // System.out.println("Don't support IPv6: " + ina);
+ continue;
+ }
+ /* Since we don't know actual broadcast address,
+ * lets try different combinations.
+ * Hopefully one of them will work.
+ */
+ int h = adr[0] & 0xff;
+ if (h >= 1 && h <= 127 && h != 38) {
+ adr[3] = (byte)255;
+ socket.send(new DatagramPacket(buf, buf.length,
+ InetAddress.getByAddress(null, adr), 1534));
+ adr[2] = (byte)255;
+ socket.send(new DatagramPacket(buf, buf.length,
+ InetAddress.getByAddress(null, adr), 1534));
+ adr[1] = (byte)255;
+ }
+ else if (h >= 128 && h <= 191) {
+ adr[3] = (byte)255;
+ socket.send(new DatagramPacket(buf, buf.length,
+ InetAddress.getByAddress(null, adr), 1534));
+ adr[2] = (byte)255;
+ }
+ else {
+ adr[3] = (byte)(adr[3] | 0x0f);
+ socket.send(new DatagramPacket(buf, buf.length,
+ InetAddress.getByAddress(null, adr), 1534));
+ adr[3] = (byte)(adr[3] | 0x01f);
+ socket.send(new DatagramPacket(buf, buf.length,
+ InetAddress.getByAddress(null, adr), 1534));
+ adr[3] = (byte)(adr[3] | 0x03f);
+ socket.send(new DatagramPacket(buf, buf.length,
+ InetAddress.getByAddress(null, adr), 1534));
+ adr[3] = (byte)(adr[3] | 0x07f);
+ socket.send(new DatagramPacket(buf, buf.length,
+ InetAddress.getByAddress(null, adr), 1534));
+ adr[3] = (byte)255;
+ }
socket.send(new DatagramPacket(buf, buf.length,
InetAddress.getByAddress(null, adr), 1534));
- adr[3] = (byte)255;
}
- socket.send(new DatagramPacket(buf, buf.length,
- InetAddress.getByAddress(null, adr), 1534));
}
}
}
diff --git a/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/internal/tcf/services/remote/RegistersProxy.java b/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/internal/tcf/services/remote/RegistersProxy.java
index 42424c063..5c64aae33 100644
--- a/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/internal/tcf/services/remote/RegistersProxy.java
+++ b/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/internal/tcf/services/remote/RegistersProxy.java
@@ -208,6 +208,37 @@ public class RegistersProxy implements IRegisters {
}.token;
}
+ public IToken getm(Location[] locs, final DoneGet done) {
+ return new Command(channel, this, "getm", new Object[]{ locs }) {
+ @SuppressWarnings("unchecked")
+ @Override
+ public void done(Exception error, Object[] args) {
+ byte[] val = null;
+ if (error == null) {
+ assert args.length == 3;
+ error = toError(args[0], args[1]);
+ String str = (String)args[2];
+ if (str != null) val = Base64.toByteArray(str.toCharArray());
+ }
+ done.doneGet(token, error, val);
+ }
+ }.token;
+ }
+
+ public IToken setm(Location[] locs, byte[] value, final DoneSet done) {
+ return new Command(channel, this, "setm",
+ new Object[]{ locs, Base64.toBase64(value, 0, value.length) }) {
+ @Override
+ public void done(Exception error, Object[] args) {
+ if (error == null) {
+ assert args.length == 2;
+ error = toError(args[0], args[1]);
+ }
+ done.doneSet(token, error);
+ }
+ }.token;
+ }
+
public void addListener(final RegistersListener listener) {
IChannel.IEventListener l = new IChannel.IEventListener() {
@@ -294,4 +325,19 @@ public class RegistersProxy implements IRegisters {
}
return arr;
}
+
+ static {
+ JSON.addObjectWriter(Location.class, new JSON.ObjectWriter() {
+ public void write(Object o) throws IOException {
+ Location l = (Location)o;
+ JSON.write('[');
+ JSON.writeObject(l.id);
+ JSON.write(',');
+ JSON.writeUInt(l.offs);
+ JSON.write(',');
+ JSON.writeUInt(l.size);
+ JSON.write(']');
+ }
+ });
+ }
}
diff --git a/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/protocol/JSON.java b/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/protocol/JSON.java
index e5dd10c61..f5000fed5 100644
--- a/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/protocol/JSON.java
+++ b/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/protocol/JSON.java
@@ -30,10 +30,29 @@ import org.eclipse.tm.internal.tcf.core.ReadOnlyMap;
/**
* JSON is TCF preferred marshaling. This class implements generation and parsing of JSON strings.
- * The code is optimized for speed since it is a time-critical part of the framework.
+ * The code is optimized for speed since it is a time-critical part of the framework.
+ *
+ * Reading of JSON produces data structure that consists of objects of these classes:
+ * Boolean, Number, String, Collection, Map.
+ *
+ * Writing of JSON is supported for:
+ * Boolean, Number, String, char[], byte[], Object[], Collection, Map
+ *
+ * Clients can enable writing support for objects of a other classes by
+ * registering ObjectWriter interface implementation.
*/
public class JSON {
+ /**
+ * Clients implement ObjectWriter interface when they want to enable marshaling of
+ * object classes that are not directly supported by JSON library.
+ */
+ public interface ObjectWriter {
+ void write(Object o) throws IOException;
+ }
+
+ private static final Map<Class<?>,ObjectWriter> object_writers = new HashMap<Class<?>,ObjectWriter>();
+
private static char[] tmp_buf = new char[0x1000];
private static byte[] tmp_bbf = new byte[0x1000];
private static int tmp_buf_pos;
@@ -48,8 +67,22 @@ public class JSON {
private static final char[] err_buf = new char[100];
private static int err_buf_pos;
private static int err_buf_cnt;
+
+ /**
+ * Add a handler for converting objects of a particular class into JSON.
+ * @param cls - a class
+ * @param writer - ObjectWriter implementation that provides generation of JSON for a given class.
+ */
+ public static void addObjectWriter(Class<?> cls, ObjectWriter writer) {
+ object_writers.put(cls, writer);
+ }
- private static void write(char ch) {
+ /**
+ * Write a character into JSON output buffer.
+ * Clients should not call this method directly, except from ObjectWriter implementation.
+ * @param ch
+ */
+ public static void write(char ch) {
if (tmp_buf_pos >= tmp_buf.length) {
char[] tmp = new char[tmp_buf.length * 2];
System.arraycopy(tmp_buf, 0, tmp, 0, tmp_buf_pos);
@@ -58,7 +91,13 @@ public class JSON {
tmp_buf[tmp_buf_pos++] = ch;
}
- private static void write(String s) {
+ /**
+ * Write a string into JSON output buffer.
+ * The string is written "as-is". Call writeObject() to convert a String into JSON string.
+ * Clients should not call this method directly, except from ObjectWriter implementation.
+ * @param s - a string
+ */
+ public static void write(String s) {
int l = s.length();
for (int i = 0; i < l; i++) {
char ch = s.charAt(i);
@@ -67,7 +106,12 @@ public class JSON {
}
}
- private static void writeUInt(int n) {
+ /**
+ * Write a non-negative integer number into JSON output buffer.
+ * Clients should not call this method directly, except from ObjectWriter implementation.
+ * @param n - a number
+ */
+ public static void writeUInt(int n) {
assert n >= 0;
if (n >= 10) writeUInt(n / 10);
write((char)('0' + n % 10));
@@ -320,8 +364,13 @@ public class JSON {
return l.toArray();
}
+ /**
+ * Write an object into JSON output buffer.
+ * Clients should not call this method directly, except from ObjectWriter implementation.
+ * @param o - an object to write
+ */
@SuppressWarnings("unchecked")
- private static void writeObject(Object o) throws IOException {
+ public static void writeObject(Object o) throws IOException {
if (o == null) {
write("null");
}
@@ -419,7 +468,13 @@ public class JSON {
write('}');
}
else {
- throw new IOException("JSON: unsupported object type");
+ ObjectWriter writer = object_writers.get(o.getClass());
+ if (writer != null) {
+ writer.write(o);
+ }
+ else {
+ throw new IOException("JSON: unsupported object type");
+ }
}
}
diff --git a/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/services/IRegisters.java b/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/services/IRegisters.java
index 9fb1de145..73625f295 100644
--- a/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/services/IRegisters.java
+++ b/plugins/org.eclipse.tm.tcf/src/org/eclipse/tm/tcf/services/IRegisters.java
@@ -258,6 +258,43 @@ public interface IRegisters extends IService {
}
/**
+ * Read values of multiple locations in registers.
+ * @param locs - array of data locations.
+ * @param done - call back object.
+ * @return - pending command handle.
+ */
+ IToken getm(Location[] locs, DoneGet done);
+
+ /**
+ * Set values of multiple locations in registers.
+ * @param locs - array of data locations.
+ * @param value - value to write into the context.
+ * @param done - call back object.
+ * @return - pending command handle.
+ */
+ IToken setm(Location[] locs, byte[] value, DoneSet done);
+
+ /**
+ * Class Location represents value location in register context
+ */
+ final class Location {
+ /** Register context ID */
+ public final String id;
+
+ /** offset in the context, in bytes */
+ public final int offs;
+
+ /** value size in byte */
+ public final int size;
+
+ public Location(String id, int offs, int size) {
+ this.id = id;
+ this.offs = offs;
+ this.size = size;
+ }
+ }
+
+ /**
* 'get' command call back interface.
*/
interface DoneGet {

Back to the top