Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--python/src/tcf/services/linenumbers.py133
-rw-r--r--python/src/tcf/services/processes.py341
-rw-r--r--python/src/tcf/services/remote/LineNumbersProxy.py65
-rw-r--r--python/src/tcf/services/remote/ProcessesProxy.py207
-rw-r--r--python/src/tcf/services/symbols.py8
-rw-r--r--python/src/tcf/tests/BasicTests.py34
6 files changed, 788 insertions, 0 deletions
diff --git a/python/src/tcf/services/linenumbers.py b/python/src/tcf/services/linenumbers.py
new file mode 100644
index 000000000..c7c4674f2
--- /dev/null
+++ b/python/src/tcf/services/linenumbers.py
@@ -0,0 +1,133 @@
+# *******************************************************************************
+# * 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 self distribution, and is available at
+# * http://www.eclipse.org/legal/epl-v10.html
+# *
+# * Contributors:
+# * Wind River Systems - initial API and implementation
+# *******************************************************************************
+
+"""
+Line numbers service associates locations in the source files with the corresponding
+machine instruction addresses in the executable object.
+"""
+
+import exceptions
+from tcf import services
+
+NAME = "LineNumbers"
+
+class CodeArea(object):
+ """
+ A CodeArea represents a continues area in source text mapped to
+ continues range of code addresses.
+ Line and columns are counted starting from 1.
+ File name can be a relative path, in this case the client should
+ use the CodeArea directory name as origin for the path.
+ File and directory names are valid on a host where code was compiled.
+ It is client responsibility to map names to local host file system.
+ """
+ def __init__(self, directory, file, start_line, start_column,
+ end_line, end_column, start_address, end_address, isa,
+ is_statement, basic_block, prologue_end, epilogue_begin):
+ self.directory = directory
+ self.file = file
+ self.start_line = start_line
+ self.start_column = start_column
+ self.end_line = end_line
+ self.end_column = end_column
+ self.start_address = start_address
+ self.end_address = end_address
+ self.isa = isa
+ self.is_statement = is_statement
+ self.basic_block = basic_block
+ self.prologue_end = prologue_end
+ self.epilogue_begin = epilogue_begin
+
+ def __eq__(self, o):
+ if self == o: return True
+ if not isinstance(o, CodeArea): return False
+ if self.start_line != o.start_line: return False
+ if self.start_column != o.start_column: return False
+ if self.end_line != o.end_line: return False
+ if self.end_column != o.end_column: return False
+ if self.isa != o.isa: return False
+ if self.is_statement != o.is_statement: return False
+ if self.basic_block != o.basic_block: return False
+ if self.prologue_end != o.prologue_end: return False
+ if self.epilogue_begin != o.epilogue_begin: return False
+ if self.start_address != o.start_address: return False
+ if self.end_address != o.end_address: return False
+ if self.file != o.file: return False
+ if self.directory != o.directory: return False
+ return True
+
+ def __hash__(self):
+ h = 0
+ if file: h += hash(file)
+ return h + self.start_line + self.start_column + self.end_line + self.end_column
+
+ def __str__(self):
+ import cStringIO
+ bf = cStringIO.StringIO()
+ bf.write('[')
+ if self.directory:
+ bf.write(self.directory)
+ bf.write(':')
+ if self.file:
+ bf.write(self.file)
+ bf.write(':')
+ bf.write(str(self.start_line))
+ if self.start_column:
+ bf.write('.')
+ bf.write(self.start_column)
+ bf.write("..")
+ bf.write(self.end_line)
+ if self.end_column:
+ bf.write('.')
+ bf.write(self.end_column)
+ bf.write(" -> ")
+ if self.start_address:
+ bf.write("0x")
+ bf.write(hex(self.start_address))
+ else:
+ bf.write('0')
+ bf.write("..")
+ if self.end_address:
+ bf.write("0x")
+ bf.write(hex(self.end_address))
+ else:
+ bf.write('0')
+ if self.isa:
+ bf.write(",isa ")
+ bf.write(self.isa)
+ if self.is_statement:
+ bf.write(",statement")
+ if self.basic_block:
+ bf.write(",basic block")
+ if self.prologue_end:
+ bf.write(",prologue end")
+ if self.epilogue_begin:
+ bf.write(",epilogue begin")
+ bf.write(']')
+ return bf.getvalue()
+
+class LineNumbersService(services.Service):
+ def getName(self):
+ return NAME
+
+ def mapToSource(self, context_id, start_address, end_address, done):
+ raise exceptions.NotImplementedError("Abstract method")
+
+ def mapToMemory(self, context_id, file, line, column, done):
+ raise exceptions.NotImplementedError("Abstract method")
+
+class DoneMapToSource(object):
+ def doneMapToSource(self, token, error, areas):
+ pass
+
+class DoneMapToMemory(object):
+ def doneMapToMemory(self, token, error, areas):
+ pass
diff --git a/python/src/tcf/services/processes.py b/python/src/tcf/services/processes.py
new file mode 100644
index 000000000..e2e1a8a6a
--- /dev/null
+++ b/python/src/tcf/services/processes.py
@@ -0,0 +1,341 @@
+# *******************************************************************************
+# * 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
+# *******************************************************************************
+
+"""
+IProcesses service provides access to the target OS's process
+information, allows to start and terminate a process, and allows
+to attach and detach a process for debugging. Debug services,
+like IMemory and IRunControl, require a process to be attached
+before they can access it.
+
+If a process is started by this service, its standard input/output streams are
+available for client to read/write using Streams service. Stream type of such
+streams is set to "Processes".
+"""
+
+import exceptions
+from tcf import services
+
+NAME = "Processes"
+
+# Context property names.
+
+# The TCF context ID
+PROP_ID = "ID"
+
+# The TCF parent context ID
+PROP_PARENT_ID = "ParentID"
+
+# Is the context attached
+PROP_ATTACHED = "Attached"
+
+# Can terminate the context
+PROP_CAN_TERMINATE = "CanTerminate"
+
+# Process name. Client UI can show this name to a user
+PROP_NAME = "Name"
+
+# Process standard input stream ID
+PROP_STDIN_ID = "StdInID"
+
+# Process standard output stream ID
+PROP_STDOUT_ID = "StdOutID"
+
+# Process standard error stream ID
+PROP_STDERR_ID = "StdErrID"
+
+
+# Signal property names used by "getSignalList" command.
+
+# Number, bit position in the signal mask
+SIG_INDEX = "Index"
+
+#String, signal name, for example "SIGHUP"
+SIG_NAME = "Name"
+
+# Number, signal code, as defined by OS
+SIG_CODE = "Code"
+
+# String, human readable description of the signal
+SIG_DESCRIPTION = "Description"
+
+
+class ProcessesService(services.Service):
+ def getName(self):
+ return NAME
+
+ def getContext(self, id, done):
+ """
+ Retrieve context info for given context ID.
+ A context corresponds to an execution thread, process, address space, etc.
+ Context IDs are valid across TCF services, so it is allowed to issue
+ 'IProcesses.getContext' command with a context that was obtained,
+ for example, from Memory service.
+ However, 'Processes.getContext' is supposed to return only process specific data,
+ If the ID is not a process ID, 'IProcesses.getContext' may not return any
+ useful information
+
+ @param id - context ID.
+ @param done - call back interface called when operation is completed.
+ """
+ raise exceptions.NotImplementedError("Abstract method")
+
+ def getChildren(self, parent_context_id, attached_only, done):
+ """
+ Retrieve children of given context.
+
+ @param parent_context_id - parent context ID. Can be None -
+ to retrieve top level of the hierarchy, or one of context IDs retrieved
+ by previous getContext or getChildren commands.
+ @param attached_only - if True return only attached process IDs.
+ @param done - call back interface called when operation is completed.
+ """
+ raise exceptions.NotImplementedError("Abstract method")
+
+ def getSignalList(self, context_id, done):
+ """
+ Get list of signals that can be send to the process.
+ @param context_id - process context ID or None.
+ @param done - call back interface called when operation is completed.
+ @return pending command handle, can be used to cancel the command.
+ """
+ raise exceptions.NotImplementedError("Abstract method")
+
+ def getSignalMask(self, context_id, done):
+ """
+ Get process or thread signal mask.
+ Bits in the mask control how signals should be handled by debug agent.
+ When new context is created it inherits the mask from its parent.
+ If context is not attached the command will return an error.
+ @param done - call back interface called when operation is completed.
+ @return pending command handle, can be used to cancel the command.
+ """
+ raise exceptions.NotImplementedError("Abstract method")
+
+ def setSignalMask(self, context_id, dont_stop, dont_pass, done):
+ """
+ Set process or thread signal mask.
+ Bits in the mask control how signals should be handled by debug agent.
+ If context is not attached the command will return an error.
+ @param dont_stop - bit-set of signals that should not suspend execution of the context.
+ By default, debugger suspends a context before it receives a signal.
+ @param dont_pass - bit-set of signals that should not be delivered to the context.
+ @param done - call back interface called when operation is completed.
+ @return pending command handle, can be used to cancel the command.
+ """
+ raise exceptions.NotImplementedError("Abstract method")
+
+ def signal(self, context_id, signal, done):
+ """
+ Send a signal to a process or thread.
+ @param context_id - context ID.
+ @param signal - signal code.
+ @param done - call back interface called when operation is completed.
+ @return pending command handle, can be used to cancel the command.
+ """
+ raise exceptions.NotImplementedError("Abstract method")
+
+ def getEnvironment(self, done):
+ """
+ Get default set of environment variables used to start a new process.
+ @param done - call back interface called when operation is completed.
+ @return pending command handle, can be used to cancel the command.
+ """
+ raise exceptions.NotImplementedError("Abstract method")
+
+ def start(self, directory, file, command_line, environment, attach, done):
+ """
+ Start a new process on remote machine.
+ @param directory - initial value of working directory for the process.
+ @param file - process image file.
+ @param command_line - command line arguments for the process.
+ Note: the service does NOT add image file name as first argument for the process.
+ If a client wants first parameter to be the file name, it should add it itself.
+ @param environment - map of environment variables for the process,
+ if None then default set of environment variables will be used.
+ @param attach - if True debugger should be attached to the process.
+ @param done - call back interface called when operation is completed.
+ @return pending command handle, can be used to cancel the command.
+ """
+ raise exceptions.NotImplementedError("Abstract method")
+
+ def addListener(self, listener):
+ """
+ Add processes service event listener.
+ @param listener - event listener implementation.
+ """
+ raise exceptions.NotImplementedError("Abstract method")
+
+ def removeListener(self, listener):
+ """
+ Remove processes service event listener.
+ @param listener - event listener implementation.
+ """
+ raise exceptions.NotImplementedError("Abstract method")
+
+
+class ProcessContext(object):
+ def __init__(self, props):
+ self._props = props or {}
+
+ def __str__(self):
+ return "[Processes Context %s]" % self._props
+
+ def getProperties(self):
+ """
+ Get context properties. See PROP_* definitions for property names.
+ Context properties are read only, clients should not try to modify them.
+ @return Map of context properties.
+ """
+ return self._props
+
+ def getID(self):
+ """
+ Retrieve context ID.
+ Same as getProperties().get('ID')
+ """
+ return self._props.get(PROP_ID)
+
+ def getParentID(self):
+ """
+ Retrieve parent context ID.
+ Same as getProperties().get('ParentID')
+ """
+ return self._props.get(PROP_PARENT_ID)
+
+ def getName(self):
+ """
+ Retrieve human readable context name.
+ Same as getProperties().get('Name')
+ """
+ return self._props.get(PROP_NAME)
+
+ def isAttached(self):
+ """
+ Utility method to read context property PROP_ATTACHED.
+ Services like IRunControl, IMemory, IBreakpoints work only with attached processes.
+ @return value of PROP_ATTACHED.
+ """
+ return self._props.get(PROP_ATTACHED)
+
+ def canTerminate(self):
+ """
+ Utility method to read context property PROP_CAN_TERMINATE.
+ @return value of PROP_CAN_TERMINATE.
+ """
+ return self._props.get(PROP_CAN_TERMINATE)
+
+ def attach(self, done):
+ """
+ Attach debugger to a process.
+ Services like IRunControl, IMemory, IBreakpoints work only with attached processes.
+ @param done - call back interface called when operation is completed.
+ @return pending command handle, can be used to cancel the command.
+ """
+ raise exceptions.NotImplementedError("Abstract method")
+
+ def detach(self, done):
+ """
+ Detach debugger from a process.
+ Process execution will continue without debugger supervision.
+ @param done - call back interface called when operation is completed.
+ @return pending command handle, can be used to cancel the command.
+ """
+ raise exceptions.NotImplementedError("Abstract method")
+
+ def terminate(self, done):
+ """
+ Terminate a process.
+ @param done - call back interface called when operation is completed.
+ @return pending command handle, can be used to cancel the command.
+ """
+ raise exceptions.NotImplementedError("Abstract method")
+
+class DoneCommand(object):
+ """
+ Call-back interface to be called when command is complete.
+ """
+ def doneCommand(self, token, error):
+ pass
+
+class DoneGetContext(object):
+ """
+ Client call back interface for getContext().
+ """
+ def doneGetContext(self, token, error, context):
+ """
+ Called when context data retrieval is done.
+ @param error - error description if operation failed, None if succeeded.
+ @param context - context data.
+ """
+ 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 error - error description if operation failed, None if succeeded.
+ @param context_ids - array of available context IDs.
+ """
+ pass
+
+class DoneGetSignalList(object):
+ """
+ Call-back interface to be called when "getSignalList" command is complete.
+ """
+ def doneGetSignalList(self, token, error, list):
+ pass
+
+class DoneGetSignalMask(object):
+ """
+ Call-back interface to be called when "getSignalMask" command is complete.
+ """
+ def doneGetSignalMask(self, token, error, dont_stop, dont_pass, pending):
+ """
+ @param token - command handle.
+ @param dont_stop - bit-set of signals that should suspend execution of the context.
+ @param dont_pass - bit-set of signals that should not be delivered to the context.
+ @param pending - bit-set of signals that are generated but not delivered yet.
+ Note: "pending" is meaningful only if the context is suspended.
+ """
+ pass
+
+class DoneGetEnvironment(object):
+ """
+ Call-back interface to be called when "getEnvironment" command is complete.
+ """
+ def doneGetEnvironment(self, token, error, environment):
+ pass
+
+class DoneStart(object):
+ """
+ Call-back interface to be called when "start" command is complete.
+ """
+ def doneStart(self, token, error, process):
+ pass
+
+class ProcessesListener(object):
+ """
+ Process event listener is notified when a process exits.
+ Event are reported only for processes that were started by 'start' command.
+ """
+
+ def exited(self, process_id, exit_code):
+ """
+ Called when a process exits.
+ @param process_id - process context ID
+ @param exit_code - if >= 0 - the process exit code,
+ if < 0 - process was terminated by a signal, the signal code = -exit_code.
+ """
+ pass
diff --git a/python/src/tcf/services/remote/LineNumbersProxy.py b/python/src/tcf/services/remote/LineNumbersProxy.py
new file mode 100644
index 000000000..84c4aa9ca
--- /dev/null
+++ b/python/src/tcf/services/remote/LineNumbersProxy.py
@@ -0,0 +1,65 @@
+# *******************************************************************************
+# * 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
+# *******************************************************************************
+
+from tcf.services import linenumbers
+from tcf.channel.Command import Command
+
+class LineNumbersProxy(linenumbers.LineNumbersService):
+
+ def __init__(self, channel):
+ self.channel = channel
+
+ def mapToSource(self, context_id, start_address, end_address, done):
+ service = self
+ class MapCommand(Command):
+ def __init__(self):
+ super(MapCommand, self).__init__(service.channel, service,
+ "mapToSource", (context_id, start_address, end_address))
+ def done(self, error, args):
+ arr = None
+ if not error:
+ assert len(args) == 2
+ error = self.toError(args[0])
+ arr = _toCodeAreaArray(args[1])
+ done.doneMapToSource(self.token, error, arr)
+ return MapCommand().token
+
+ def mapToMemory(self, context_id, file, line, column, done):
+ service = self
+ class MapCommand(Command):
+ def __init__(self):
+ super(MapCommand, self).__init__(service.channel, service,
+ "mapToMemory", (context_id, file, line, column))
+ def done(self, error, args):
+ arr = None
+ if not error:
+ assert len(args) == 2
+ error = self.toError(args[0])
+ arr = _toCodeAreaArray(args[1])
+ done.doneMapToMemory(self.token, error, arr)
+ return MapCommand().token
+
+def _toCodeAreaArray(o):
+ if not o: return None
+ arr = []
+ directory = None
+ file = None
+ for area in o:
+ directory = area.get("Dir", directory)
+ file = area.get("File", file)
+ arr.append(linenumbers.CodeArea(directory, file,
+ area.get("SLine", 0), area.get("SCol", 0),
+ area.get("ELine", 0), area.get("ECol", 0),
+ area.get("SAddr"), area.get("EAddr"),
+ area.get("ISA", 0),
+ area.get("IsStmt"), area.get("BasicBlock"),
+ area.get("PrologueEnd"), area.get("EpilogueBegin")))
+ return arr
diff --git a/python/src/tcf/services/remote/ProcessesProxy.py b/python/src/tcf/services/remote/ProcessesProxy.py
new file mode 100644
index 000000000..c816465cd
--- /dev/null
+++ b/python/src/tcf/services/remote/ProcessesProxy.py
@@ -0,0 +1,207 @@
+# *******************************************************************************
+# * 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 processes
+from tcf.channel.Command import Command
+
+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 == "exited":
+ assert len(args) == 2
+ self.listener.exited(args[0], args[1])
+ else:
+ raise IOError("Processes service: unknown event: " + name);
+ except exceptions.Exception as x:
+ self.service.channel.terminate(x)
+
+class ProcessesProxy(processes.ProcessesService):
+ def __init__(self, channel):
+ self.channel = channel
+ self.listeners = {}
+
+ 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)
+
+ def getChildren(self, parent_context_id, attached_only, done):
+ service = self
+ class GetChildrenCommand(Command):
+ def __init__(self):
+ super(GetChildrenCommand, self).__init__(service.channel, service,
+ "getChildren", (parent_context_id, attached_only))
+ 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, context_id, done):
+ service = self
+ class GetContextCommand(Command):
+ def __init__(self):
+ super(GetContextCommand, self).__init__(service.channel, service, "getContext", (context_id,))
+ def done(self, error, args):
+ ctx = None
+ if not error:
+ assert len(args) == 2
+ error = self.toError(args[0])
+ if args[1]: ctx = ProcessContext(service, args[1])
+ done.doneGetContext(self.token, error, ctx)
+ return GetContextCommand().token
+
+ def getEnvironment(self, done):
+ service = self
+ class GetEnvCommand(Command):
+ def __init__(self):
+ super(GetEnvCommand, self).__init__(service.channel, service, "getEnvironment", None)
+ def done(self, error, args):
+ env = None
+ if not error:
+ assert len(args) == 2
+ error = self.toError(args[0])
+ env = _toEnvMap(args[1])
+ done.doneGetEnvironment(self.token, error, env)
+ return GetEnvCommand().token
+
+ def start(self, directory, file, command_line, environment, attach, done):
+ service = self
+ env = _toEnvStringArray(environment)
+ class StartCommand(Command):
+ def __init__(self):
+ super(StartCommand, self).__init__(service.channel, service,
+ "start", (directory, file, command_line, env, attach))
+ def done(self, error, args):
+ ctx = None
+ if not error:
+ assert len(args) == 2
+ error = self.toError(args[0])
+ if args[1]: ctx = ProcessContext(service, args[1])
+ done.doneStart(self.token, error, ctx)
+ return StartCommand().token
+
+ def getSignalList(self, context_id, done):
+ service = self
+ class GetSignalsCommand(Command):
+ def __init__(self):
+ super(GetSignalsCommand, self).__init__(service.channel, service,
+ "getSignalList", (context_id,))
+ def done(self, error, args):
+ list = None
+ if not error:
+ assert len(args) == 2
+ error = self.toError(args[0])
+ list = args[1]
+ done.doneGetSignalList(self.token, error, list)
+ return GetSignalsCommand().token
+
+ def getSignalMask(self, context_id, done):
+ service = self
+ class GetSignalMaskCommand(Command):
+ def __init__(self):
+ super(GetSignalMaskCommand, self).__init__(service.channel, service,
+ "getSignalMask", (context_id,))
+ def done(self, error, args):
+ dont_stop = 0
+ dont_pass = 0
+ pending = 0
+ if not error:
+ assert len(args) == 4
+ error = self.toError(args[0])
+ dont_stop, dont_pass, pending = args[1:3]
+ done.doneGetSignalMask(self.token, error, dont_stop, dont_pass, pending)
+ return GetSignalMaskCommand().token
+
+ def setSignalMask(self, context_id, dont_stop, dont_pass, done):
+ service = self
+ class SetSignalMaskCommand(Command):
+ def __init__(self):
+ super(SetSignalMaskCommand, self).__init__(service.channel, service,
+ "setSignalMask", (context_id, dont_stop, dont_pass))
+ def done(self, error, args):
+ if not error:
+ assert len(args) == 1
+ error = self.toError(args[0])
+ done.doneCommand(self.token, error)
+ return SetSignalMaskCommand().token
+
+ def signal(self, context_id, signal, done):
+ service = self
+ class SignalCommand(Command):
+ def __init__(self):
+ super(SignalCommand, self).__init__(service.channel, service,
+ "signal", (context_id, signal))
+ def done(self, error, args):
+ if not error:
+ assert len(args) == 1
+ error = self.toError(args[0])
+ done.doneCommand(self.token, error)
+ return SignalCommand().token
+
+def _toEnvStringArray(map):
+ arr = []
+ if not map: return arr
+ for name, value in map.items():
+ arr.append("%s=%s" % (name, value))
+ return arr
+
+def _toEnvMap(arr):
+ map = {}
+ if not arr: return map
+ for str in arr:
+ i = str.find('=')
+ if i >= 0: map[str[:i]] = str[i + 1:]
+ else: map[str] = ""
+ return map
+
+class ProcessContext(processes.ProcessContext):
+ def __init__(self, service, props):
+ super(ProcessContext, self).__init__(props)
+ self.service = service
+
+ def attach(self, done):
+ return self._command("attach", done)
+
+ def detach(self, done):
+ return self._command("detach", done)
+
+ def terminate(self, done):
+ return self._command("terminate", done)
+
+ def _command(self, command, done):
+ service = self.service
+ id = self.getID()
+ class _Command(Command):
+ def __init__(self):
+ super(_Command, self).__init__(service.channel, service,
+ command, (id,))
+ def done(self, error, args):
+ if not error:
+ assert len(args) == 1
+ error = self.toError(args[0])
+ done.doneCommand(self.token, error)
+ return _Command().token
diff --git a/python/src/tcf/services/symbols.py b/python/src/tcf/services/symbols.py
index ccf77de22..2f03f7402 100644
--- a/python/src/tcf/services/symbols.py
+++ b/python/src/tcf/services/symbols.py
@@ -53,6 +53,7 @@ PROP_OFFSET = "Offset"
PROP_ADDRESS = "Address"
PROP_VALUE = "Value"
PROP_BIG_ENDIAN = "BigEndian"
+PROP_REGISTER = "Register"
#
# Symbol context properties update policies.
@@ -215,6 +216,13 @@ class Symbol(object):
"""
return self._props.get(PROP_BIG_ENDIAN, False)
+ def getRegisterID(self):
+ """
+ Return register ID if the symbol represents a register variable.
+ @return register ID or null.
+ """
+ return self._props.get(PROP_REGISTER)
+
def getProperties(self):
"""
Get complete map of context properties.
diff --git a/python/src/tcf/tests/BasicTests.py b/python/src/tcf/tests/BasicTests.py
index acddf6a9f..24bb022cb 100644
--- a/python/src/tcf/tests/BasicTests.py
+++ b/python/src/tcf/tests/BasicTests.py
@@ -45,9 +45,11 @@ def test():
testSymbols(c)
testRegisters(c)
testExpressions(c)
+ testLineNumbers(c)
testSyncCommands(c)
testEvents(c)
testDataCache(c)
+ testProcesses(c)
except exceptions.Exception as e:
protocol.log(e)
@@ -237,6 +239,7 @@ def testSymbols(c):
protocol.invokeLater(symTest, ctx_id)
def testRegisters(c):
+ if not _suspended: return
from tcf.services import registers
lock = threading.Condition()
def regTest(ctx_id):
@@ -306,6 +309,22 @@ def testExpressions(c):
print e.get(expressions.PROP_EXPRESSION), "=", val
exprs.dispose(id)
+def testLineNumbers(c):
+ if not _suspended: return
+ from tcf.services import linenumbers
+ from tcf.services import stacktrace
+ ctl = sync.CommandControl(c)
+ stack = ctl.StackTrace
+ lineNumbers = ctl.LineNumbers
+ for ctx_id in _suspended:
+ bt = stack.getChildren(ctx_id).get()
+ if bt:
+ bt = stack.getContext(bt).get()
+ for frame in bt:
+ addr = frame.get(stacktrace.PROP_INSTRUCTION_ADDRESS)
+ area = lineNumbers.mapToSource(ctx_id, addr, addr+1).get()
+ print "Frame %d - CodeArea: %s" % (frame.get(stacktrace.PROP_LEVEL), area)
+
def testSyncCommands(c):
# simplified command execution
ctl = sync.CommandControl(c)
@@ -378,5 +397,20 @@ def testDataCache(c):
print "ContextsCache is valid:", contextsCache.getData()
protocol.invokeLater(contextsCache.validate, done)
+def testProcesses(c):
+ from tcf.services import processes
+ def processTest():
+ proc = c.getRemoteService(processes.NAME)
+ if not proc:
+ return
+ class DoneGetChildren(processes.DoneGetChildren):
+ def doneGetChildren(self, token, error, context_ids):
+ if error:
+ protocol.log("Error from Processes.GetChildren", error)
+ else:
+ print "Processes:", context_ids
+ proc.getChildren(None, DoneGetChildren())
+ protocol.invokeLater(processTest)
+
if __name__ == '__main__':
test()

Back to the top