Skip to main content
aboutsummaryrefslogtreecommitdiffstats
path: root/python
diff options
context:
space:
mode:
authoraleherbau2011-04-06 14:27:32 +0000
committeraleherbau2011-04-06 14:27:32 +0000
commit56b6dfdffcb437472d72ffbf2c55eb2c5541f4f1 (patch)
tree1fc3e74eb29345416af4d7354993109d645bc720 /python
parentd18d3f559a79c345222dffe248d53fef8d1c3931 (diff)
downloadorg.eclipse.tcf-56b6dfdffcb437472d72ffbf2c55eb2c5541f4f1.tar.gz
org.eclipse.tcf-56b6dfdffcb437472d72ffbf2c55eb2c5541f4f1.tar.xz
org.eclipse.tcf-56b6dfdffcb437472d72ffbf2c55eb2c5541f4f1.zip
Implemented Expressions service proxy
Diffstat (limited to 'python')
-rw-r--r--python/src/tcf/channel/Command.py6
-rw-r--r--python/src/tcf/channel/__init__.py3
-rw-r--r--python/src/tcf/services/expressions.py357
-rw-r--r--python/src/tcf/services/remote/ExpressionsProxy.py128
-rw-r--r--python/src/tcf/tests/BasicTests.py18
-rw-r--r--python/src/tcf/util/sync.py5
6 files changed, 512 insertions, 5 deletions
diff --git a/python/src/tcf/channel/Command.py b/python/src/tcf/channel/Command.py
index 58f517c05..c22484db4 100644
--- a/python/src/tcf/channel/Command.py
+++ b/python/src/tcf/channel/Command.py
@@ -9,9 +9,9 @@
# * Wind River Systems - initial API and implementation
# *******************************************************************************
-import json, exceptions, cStringIO
+import exceptions, cStringIO
from tcf import protocol, errors, services
-from tcf.channel import Token, toJSONSequence, fromJSONSequence
+from tcf.channel import Token, toJSONSequence, fromJSONSequence, dumpJSONObject
class Command(object):
"""
@@ -102,7 +102,7 @@ class Command(object):
buf.write(", ")
i += 1
try:
- json.dump(arg, buf)
+ dumpJSONObject(arg, buf)
except exceptions.Exception as x:
buf.write("***")
buf.write(x.message)
diff --git a/python/src/tcf/channel/__init__.py b/python/src/tcf/channel/__init__.py
index a85ed1de6..0228e5150 100644
--- a/python/src/tcf/channel/__init__.py
+++ b/python/src/tcf/channel/__init__.py
@@ -135,6 +135,9 @@ def fromJSONSequence(bytes):
objects.append(None)
return objects
+def dumpJSONObject(object, buf):
+ json.dump(object, buf, separators=(',', ':'), cls=TCFJSONEncoder)
+
def toByteArray(data):
if data is None: return None
t = type(data)
diff --git a/python/src/tcf/services/expressions.py b/python/src/tcf/services/expressions.py
new file mode 100644
index 000000000..46de26002
--- /dev/null
+++ b/python/src/tcf/services/expressions.py
@@ -0,0 +1,357 @@
+# *******************************************************************************
+# * Copyright (c) 2011 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
+# *******************************************************************************
+
+"""
+Expressions service allows TCF client to perform expression evaluation on remote target.
+The service can be used to retrieve or modify values of variables or any data structures in remote target memory.
+"""
+
+import exceptions
+from tcf import services
+
+# Service name.
+NAME = "Expressions"
+
+class Expression(object):
+ """
+ Expression object represent an expression that can be evaluated by remote target.
+ It has a unique ID and contains all information necessary to compute a value.
+ The object data usually includes:
+ 1. process, thread or stack frame ID that should be used to resolve symbol names
+ 2. a script that can compute a value, like "x.y + z"
+ """
+ def __init__(self, props):
+ self._props = props or {}
+
+ def __str__(self):
+ return "[Expression Context %s]" % self._props
+
+ def getID(self):
+ """
+ Get context ID.
+ @return context ID.
+ """
+ return self._props.get(PROP_ID)
+
+ def getParentID(self):
+ """
+ Get parent context ID.
+ @return parent context ID.
+ """
+ return self._props.get(PROP_PARENT_ID)
+
+ def getLanguage(self):
+ """
+ Get expression script language ID.
+ @return language ID.
+ """
+ return self._props.get(PROP_LANGUAGE)
+
+ def getExpression(self):
+ """
+ Return expression string - the script part of the context.
+ @return expression script string
+ """
+ return self._props.get(PROP_EXPRESSION)
+
+ def getSymbolID(self):
+ """
+ Return symbol ID if the expression represents a symbol (e.g. local variable).
+ @return symbol ID
+ """
+ return self._props.get(PROP_SYMBOL_ID)
+
+ def getBits(self):
+ """
+ Get size of expression value in bits.
+ Can be 0 if value size is even number of bytes, use getSize() in such case.
+ @return size in bits.
+ """
+ return self._props.get(PROP_BITS, 0)
+
+ def getSize(self):
+ """
+ Get size in bytes. The size can include extra (unused) bits.
+ This is "static" or "declared" size - as determined by expression type.
+ @return size in bytes.
+ """
+ return self._props.get(PROP_SIZE, 0)
+
+ def getTypeID(self):
+ """
+ Get expression type ID. Symbols service can be used to get type properties.
+ This is "static" or "declared" type ID, actual type of a value can be different -
+ if expression language supports dynamic typing.
+ @return type ID.
+ """
+ return self._props.get(PROP_TYPE)
+
+ def canAssign(self):
+ """
+ Check if the expression can be assigned a new value.
+ @return true if can assign.
+ """
+ return self._props.get(PROP_CAN_ASSIGN)
+
+ def getProperties(self):
+ """
+ Get complete map of context properties.
+ @return map of context properties.
+ """
+ return self._props
+
+# Expression context property names.
+PROP_ID = "ID"
+PROP_PARENT_ID = "ParentID"
+PROP_SYMBOL_ID = "SymbolID"
+PROP_LANGUAGE = "Language"
+PROP_EXPRESSION = "Expression"
+PROP_BITS = "Bits"
+PROP_SIZE = "Size"
+PROP_TYPE = "Type"
+PROP_CAN_ASSIGN = "CanAssign"
+
+class Value(object):
+ """
+ Value represents result of expression evaluation.
+ Note that same expression can be evaluated multiple times with different results.
+ """
+ def __init__(self, value, props):
+ self._value = value
+ self._props = props or {}
+
+ def __str__(self):
+ return "[Expression Value %s %s]" % (self._value, self._props)
+
+ def getTypeClass(self):
+ """
+ Get value type class.
+ @see symbols.TypeClass
+ @return type class
+ """
+ return self._props.get(VAL_CLASS, 0)
+
+ def getTypeID(self):
+ """
+ Get value type ID. Symbols service can be used to get type properties.
+ @return type ID.
+ """
+ return self._props.get(VAL_TYPE)
+
+ def isBigEndian(self):
+ """
+ Check endianness of the values.
+ Big-endian means decreasing numeric significance with increasing byte number.
+ @return true if big-endian.
+ """
+ return self._props.get(VAL_BIG_ENDIAN)
+
+ def getValue(self):
+ """
+ Get value as array of bytes.
+ @return value as array of bytes.
+ """
+ return self._value
+
+ def getProperties(self):
+ """
+ Get complete map of value properties.
+ @return map of value properties.
+ """
+ return self._props
+
+# Expression value property names.
+VAL_CLASS = "Class"
+VAL_TYPE = "Type"
+VAL_BIG_ENDIAN = "BigEndian"
+
+class ExpressionsService(services.Service):
+ def getName(self):
+ return NAME
+
+ def getContext(self, id, done):
+ """
+ Retrieve expression context info for given context ID.
+ @see Expression
+
+ @param id - context ID.
+ @param done - call back interface called when operation is completed.
+ @return - pending command handle.
+ """
+ raise exceptions.NotImplementedError("Abstract method")
+
+ def getChildren(self, parent_context_id, done):
+ """
+ Retrieve children IDs for given parent ID.
+ Meaning of the operation depends on parent kind:
+ 1. expression with type of a struct, union, or class - fields
+ 2. expression with type of an enumeration - enumerators
+ 3. expression with type of an array - array elements
+ 4. stack frame - function arguments and local variables
+ 5. thread - top stack frame function arguments and local variables
+ 6. process - global variables
+
+ Children list *does not* include IDs of expressions that were created by clients
+ using "create" command.
+
+ @param parent_context_id - parent context ID.
+ @param done - call back interface called when operation is completed.
+ @return - pending command handle.
+ """
+ raise exceptions.NotImplementedError("Abstract method")
+
+ def create(self, parent_id, language, expression, done):
+ """
+ Create an expression context.
+ The context should be disposed after use.
+ @param parent_id - a context ID that can be used to resolve symbol names.
+ @param language - language of expression script, None means default language
+ @param expression - expression script
+ @param done - call back interface called when operation is completed.
+ @return - pending command handle.
+ """
+ raise exceptions.NotImplementedError("Abstract method")
+
+ def dispose(self, id, done):
+ """
+ Dispose an expression context that was created by create()
+ @param id - the expression context ID
+ @param done - call back interface called when operation is completed.
+ @return - pending command handle.
+ """
+ raise exceptions.NotImplementedError("Abstract method")
+
+ def evaluate(self, id, done):
+ """
+ Evaluate value of an expression context.
+ @param id - the expression context ID
+ @param done - call back interface called when operation is completed.
+ @return - pending command handle.
+ """
+ raise exceptions.NotImplementedError("Abstract method")
+
+ def assign(self, id, value, done):
+ """
+ Assign a value to memory location determined by an expression.
+ @param id - expression ID.
+ @param value - value as an array of bytes.
+ @param done - call back interface called when operation is completed.
+ @return - pending command handle.
+ """
+ raise exceptions.NotImplementedError("Abstract method")
+
+ def addListener(self, listener):
+ """
+ Add expressions service event listener.
+ @param listener - event listener implementation.
+ """
+ raise exceptions.NotImplementedError("Abstract method")
+
+ def removeListener(self, listener):
+ """
+ Remove expressions service event listener.
+ @param listener - event listener implementation.
+ """
+ raise exceptions.NotImplementedError("Abstract method")
+
+class DoneGetContext(object):
+ """
+ Client call back interface for getContext().
+ """
+ def doneGetContext(self, token, error, context):
+ """
+ Called when context data retrieval is done.
+ @param token - command handle
+ @param error - error description if operation failed, None if succeeded.
+ @param context - context properties.
+ """
+ pass
+
+class DoneGetChildren(object):
+ """
+ Client call back interface for getChildren().
+ """
+ def doneGetChildren(self, token, error, context_ids):
+ """
+ Called when context list retrieval is done.
+ @param token - command handle
+ @param error - error description if operation failed, None if succeeded.
+ @param context_ids - array of available context IDs.
+ """
+ pass
+
+
+class DoneCreate(object):
+ """
+ Client call back interface for create().
+ """
+ def doneCreate(self, token, error, context):
+ """
+ Called when context create context command is done.
+ @param token - command handle
+ @param error - error description if operation failed, None if succeeded.
+ @param context - context properties.
+ """
+ pass
+
+class DoneDispose(object):
+ """
+ Client call back interface for dispose().
+ """
+ def doneDispose(self, token, error):
+ """
+ Called when context dispose command is done.
+ @param token - command handle
+ @param error - error description if operation failed, None if succeeded.
+ """
+ pass
+
+class DoneEvaluate(object):
+ """
+ Client call back interface for evaluate().
+ """
+ def doneEvaluate(self, token, error, value):
+ """
+ Called when context dispose command is done.
+ @param token - command handle
+ @param error - error description if operation failed, None if succeeded.
+ @param value - expression evaluation result
+ """
+ pass
+
+class DoneAssign(object):
+ """
+ Client call back interface for assign().
+ """
+ def doneAssign(self, token, error):
+ """
+ Called when assign command is done.
+ @param token - command handle
+ @param error - error description if operation failed, None if succeeded.
+ """
+ pass
+
+class ExpressionsListener(object):
+ """
+ Registers event listener is notified when registers context hierarchy
+ changes, and when a register is modified by the service commands.
+ """
+ def valueChanged(self, id):
+ """
+ Called when expression value was changed and clients
+ need to update themselves. Clients, at least, should invalidate
+ corresponding cached expression data.
+ Not every change is notified - it is not possible,
+ only those, which are not caused by normal execution of the debuggee.
+ At least, changes caused by "assign" command should be notified.
+ @param id - expression context ID.
+ """
+ pass
diff --git a/python/src/tcf/services/remote/ExpressionsProxy.py b/python/src/tcf/services/remote/ExpressionsProxy.py
new file mode 100644
index 000000000..601adab02
--- /dev/null
+++ b/python/src/tcf/services/remote/ExpressionsProxy.py
@@ -0,0 +1,128 @@
+# *******************************************************************************
+# * Copyright (c) 2011 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
+# *******************************************************************************
+
+import exceptions
+from tcf import channel
+from tcf.services import expressions
+from tcf.channel.Command import Command
+
+class ExpressionsProxy(expressions.ExpressionsService):
+ def __init__(self, channel):
+ self.channel = channel
+
+ def assign(self, id, value, done):
+ service = self
+ value = bytearray(value)
+ class AssignCommand(Command):
+ def __init__(self):
+ super(AssignCommand, self).__init__(service.channel, service, "assign", (id, value))
+ def done(self, error, args):
+ if not error:
+ assert len(args) == 1
+ error = self.toError(args[0])
+ done.doneAssign(self.token, error)
+ return AssignCommand().token
+
+ def create(self, parent_id, language, expression, done):
+ service = self
+ class CreateCommand(Command):
+ def __init__(self):
+ super(CreateCommand, self).__init__(service.channel, service, "create", (parent_id, language, expression))
+ def done(self, error, args):
+ ctx = None
+ if not error:
+ assert len(args) == 2
+ error = self.toError(args[0])
+ ctx = expressions.Expression(args[1])
+ done.doneCreate(self.token, error, ctx)
+ return CreateCommand().token
+
+ def dispose(self, id, done):
+ service = self
+ class DisposeCommand(Command):
+ def __init__(self):
+ super(DisposeCommand, self).__init__(service.channel, service, "dispose", (id,))
+ def done(self, error, args):
+ if not error:
+ assert len(args) == 1
+ error = self.toError(args[0])
+ done.doneDispose(self.token, error)
+ return DisposeCommand().token
+
+ def evaluate(self, id, done):
+ service = self
+ class EvalCommand(Command):
+ def __init__(self):
+ super(EvalCommand, self).__init__(service.channel, service, "evaluate", (id,))
+ def done(self, error, args):
+ value = None
+ if not error:
+ assert len(args) == 3
+ value = channel.toByteArray(args[0])
+ error = self.toError(args[1])
+ props = args[2]
+ done.doneEvaluate(self.token, error, expressions.Value(value, props))
+ return EvalCommand().token
+
+ def getChildren(self, parent_context_id, done):
+ service = self
+ class GetChildrenCommand(Command):
+ def __init__(self):
+ super(GetChildrenCommand, self).__init__(service.channel, service, "getChildren", (parent_context_id,))
+ def done(self, error, args):
+ contexts = None
+ if not error:
+ assert len(args) == 2
+ error = self.toError(args[0])
+ contexts = args[1]
+ done.doneGetChildren(self.token, error, contexts)
+ return GetChildrenCommand().token
+
+ def getContext(self, id, done):
+ service = self
+ class GetContextCommand(Command):
+ def __init__(self):
+ super(GetContextCommand, self).__init__(service.channel, service, "getContext", (id,))
+ def done(self, error, args):
+ ctx = None
+ if not error:
+ assert len(args) == 2
+ error = self.toError(args[0])
+ if args[1]: ctx = expressions.Expression(service, args[1])
+ done.doneGetContext(self.token, error, ctx)
+ return GetContextCommand().token
+
+ def addListener(self, listener):
+ l = ChannelEventListener(self, listener)
+ self.channel.addEventListener(self, l)
+ self.listeners[listener] = l
+
+ def removeListener(self, listener):
+ l = self.listeners.get(listener)
+ if l:
+ del self.listeners[listener]
+ self.channel.removeEventListener(self, l)
+
+
+class ChannelEventListener(channel.EventListener):
+ def __init__(self, service, listener):
+ self.service = service
+ self.listener = listener
+ def event(self, name, data):
+ try:
+ args = channel.fromJSONSequence(data)
+ if name == "valueChanged":
+ assert len(args) == 1
+ self.listener.valueChanged(args[0])
+ else:
+ raise IOError("Expressions service: unknown event: " + name);
+ except exceptions.Exception as x:
+ self.service.channel.terminate(x)
diff --git a/python/src/tcf/tests/BasicTests.py b/python/src/tcf/tests/BasicTests.py
index 692f35df1..acddf6a9f 100644
--- a/python/src/tcf/tests/BasicTests.py
+++ b/python/src/tcf/tests/BasicTests.py
@@ -27,7 +27,11 @@ _suspended = []
def test():
protocol.startEventQueue()
- c = tcf.connect("TCP:127.0.0.1:1534")
+ try:
+ c = tcf.connect("TCP:127.0.0.1:1534")
+ except exceptions.Exception as e:
+ protocol.log(e)
+ sys.exit()
assert c.state == channel.STATE_OPEN
if __TRACE: protocol.invokeLater(c.addTraceListener, TraceListener())
def r2():
@@ -40,6 +44,7 @@ def test():
testBreakpoints(c)
testSymbols(c)
testRegisters(c)
+ testExpressions(c)
testSyncCommands(c)
testEvents(c)
testDataCache(c)
@@ -290,6 +295,17 @@ def testRegisters(c):
protocol.invokeLater(regTest, ctx_id)
lock.wait(5000)
+def testExpressions(c):
+ if not _suspended: return
+ from tcf.services import expressions
+ ctl = sync.CommandControl(c)
+ exprs = ctl.Expressions
+ e = exprs.create(_suspended[0], None, "1+2*(3-4/2)").getE()
+ id = e.get(expressions.PROP_ID)
+ val, cls = exprs.evaluate(id).getE()
+ print e.get(expressions.PROP_EXPRESSION), "=", val
+ exprs.dispose(id)
+
def testSyncCommands(c):
# simplified command execution
ctl = sync.CommandControl(c)
diff --git a/python/src/tcf/util/sync.py b/python/src/tcf/util/sync.py
index e37420974..3d8184602 100644
--- a/python/src/tcf/util/sync.py
+++ b/python/src/tcf/util/sync.py
@@ -82,10 +82,13 @@ class CommandControl(object):
def done(self, error, args):
resultArgs = None
if not error and args:
- # error result is usually in args[0]
+ # error result is usually in args[0], but there are exceptions
if service == "StackTrace" and command == "getContext":
error = self.toError(args[1])
resultArgs = (args[0],)
+ elif service == "Expressions" and command == "evaluate":
+ error = self.toError(args[1])
+ resultArgs = (args[0],args[2])
elif service == "Diagnostics" and command.startswith("echo"):
resultArgs = (args[0],)
else:

Back to the top