Jython is a Java implementation of the Python scripting language. It provides extensive support for data types, conditional execution, loops and organisation of code into functions, classes and modules, as well as access to the standard Jython libraries. Jython is an ideal choice for larger or more complex scripts.
These are important concepts that are required in order to write a debugger Jython script.
The debugger module provides a Debugger class for initial access to the DS-5 Debugger, with further classes, known as services, to access registers and memory, . Here is an example showing the full set of module imports that are typically placed at the top of the jython script:
from arm_ds.debugger_v1 import Debugger from arm_ds.debugger_v1 import DebugException
- Execution Contexts
Most operations on DS-5 Debugger Jython interfaces require an execution context. The execution context represents the state of the target system. Separate execution contexts exist for each process, thread, or processor that is accessible in the debugger. You can obtain an execution context from the Debugger class instance, for example:
# Obtain the first execution context debugger = Debugger() ec = debugger.getExecutionContext(0)
You can access processor registers, coprocessor registers and peripheral registers using the debugger Jython interface. To access a register you must know its name. The name can be obtained from the Registers view in the graphical debugger. The RegisterService enables you to read and write register values, for a given execution context, for example:
# Print the Program Counter (PC) from execution context ec value = ec.getRegisterService().getValue('PC') print 'The PC is %s' %value
You can access memory using the debugger Jython interface. You must specify an address and the number of bytes to access. The address and size can be an absolute numeric value or a string containing an expression to be evaluated within the specified execution context. Here is an example:
# Print 16 bytes at address 0x0 from execution context ec print ec.getMemoryService().read(0x0, 16)
- DS Commands
The debugger jython interface enables you to execute arbitrary DS-5 commands. This is useful when the required functionality is not directly provided in the Jython interface. You must specify the execution context, the command and any arguments that you want to execute. The return value includes the textual output from the command and any errors. Here is an example:
# Execute the DS-5 command 'print $ENTRYPOINT' and print the result print ec.executeDSCommand('print $ENTRYPOINT')
- Error Handling
The methods on the debugger Jython interfaces throw DebugException whenever an error occurs. You can catch exceptions to handle errors in order to provide more information. Here is an example:
# Catch a DebugException and print the error message try: ec.getRegisterService().getValue('ThisRegisterDoesNotExist') except DebugException, de: print "Caught DebugException: %s" % (de.getMessage())
.py file extension must be used to
identify this type of script.
# Filename: myScript.py import sys from arm_ds.debugger_v1 import Debugger from arm_ds.debugger_v1 import DebugException # Debugger object for accessing the debugger debugger = Debugger() # Initialisation commands ec = debugger.getExecutionContext(0) ec.getExecutionService().stop() ec.getExecutionService().waitForStop() # in case the execution context reference is out of date ec = debugger.getExecutionContext(0) # load image if provided in script arguments if len(sys.argv) == 2: image = sys.argv ec.getImageService().loadImage(image) ec.getExecutionService().setExecutionAddressToEntryPoint() ec.getImageService().loadSymbols(image) # we can use all the DS commands available print "Entry point: ", print ec.executeDSCommand("print $ENTRYPOINT") # Sample output: # Entry point: $8 = 32768 else: pass # assuming image and symbols are loaded # sets a temporary breakpoint at main and resumes ec.getExecutionService().resumeTo("main") # this method is non-blocking try: ec.getExecutionService().waitForStop(500) # wait for 500ms except DebugException, e: if e.getErrorCode() == "JYI31": # code of "Wait for stop timed out" message print "Waiting timed out!" sys.exit() else: raise # re-raise the exception if it is a different error ec = debugger.getExecutionContext(0) def getRegisterValue(executionContext, name): """Get register value and return string with unsigned hex and signed integer, possibly string "error" if there was a problem reading the register. """ try: value = executionContext.getRegisterService().getValue(name) # the returned value behaves like a numeric type, # and even can be accessed like an array of bytes, e.g. 'print value[:]' return "%s (%d)" % (str(value), int(value)) except DebugException, e: return "error" # print Core registers on all execution contexts for i in range(debugger.getExecutionContextCount()): ec = debugger.getExecutionContext(i) # filter register names starting with "Core::" coreRegisterNames = filter(lambda name: name.startswith("Core::"), ec.getRegisterService().getRegisterNames()) # using Jython list comprehension get values of all these registers registerInfo = ["%s = %s" % (name, getRegisterValue(ec, name)) for name in coreRegisterNames] registers = ", ".join(registerInfo[:3]) # only first three print "Identifier: %s, Registers: %s" % (ec.getIdentifier(), registers) # Output: # Identifier: 1, Registers: Core::R0 = 0x00000010 (16), Core::R1 = 0x00000000 (0), Core::R2 = 0x0000A4A4 (42148) # ...