# Debug Scripting

The debug scripting interface is function based and depends on certain, named functions to be defined in a Python® file. The function will then be called when the corresponding event is occurring inside Atmel Studio.

Attention:

Error checking is kept at a minimum for the functions exported into the Python environment so that the time used on initialization during normal sessions are kept low. This means that there are many ways to crash Atmel Studio through this interface.

To load a Python file, place a file named debughooks.py in the Debug folder of your project, next to the ELF file, or one folder up where the project file is. It is also possible to place this file inside the Atmel Studio installation directory to make the script load for all projects.

Note: The Python file is loaded and compiled when a project is launched, so changes to the Python file during a debug session will not be active until the next debug session is started. The Python file is running in an IronPython context, with full access to .NET and a Python 2.7 runtime. See http://ironpython.net/documentation/dotnet/ for more information of the runtime.
The functions that Atmel Studio will try to load are shown below with their function signature.
def should_process_breakpoint(studio_interface,
breakpoint_id,
obj):
"""
Called to determine if a breakpoint should cause Atmel Studio to enter debug mode.

If this function returns False, Atmel Studio will not break at the breakpoint.
"""

return True

def has_processed_breakpoint(studio_interface,
breakpoint_id,
obj):
"""
This function is called if Atmel Studio is breaking at a breakpoint.
The GUI is now in halted mode.
"""

pass

def on_reset(studio_interface,
"""
This function is called when the target is reset. The address
where the reset went to is 'reset_address'.
"""

pass

def on_eval_expr(studio_interface,
expression):
"""
This function is called for each expression that is evaluated in Atmel Studio.

This includes the watch window and other windows that show data from the target.
Pass the 'expression' string through to evaluate it, or return another expression
to be evaluated to override the expression. This override is not visible in the
Atmel Studio GUI.
"""

return expression


Note: Atmel Studio expects all these functions to be available if the script has been found and is loaded correctly. If, for instance, the should_process_breakpoint is undefined, breakpoints might start to misbehave as the return value of an undefined function is in itself undefined.

In the code shown above, the main interface back into the Atmel Studio is the studio_interface object. This object contains some functions to show messages and do target interaction.

The Print function in the studio_interface object is used to show text in the output window inside Atmel Studio. The function takes two arguments; the string to print and the name of the tab in the output window. The example below prints all the evaluated expressions to the Expressions tab.
def on_eval_expr(studio_interface, expression):
studio_interface.Print("Evaluating {}".format(expression), "Expressions")
return expression
Note: The severity level of text sent through Print is set to INFO, which means that the output may be masked by Atmel Studio. To lower the threshold, go to Tools > Tools, select Status Management, and set the Display Threshold to INFO.

The ExecStmt function in the studio_interface object is used to execute statements in the debugger. This can, for instance, be used to set variables. See MSDN Debugger.ExecuteStatement Method for more information.

The WriteMemory and ReadMemory  are symmetric functions for reading and writing memory on the target. It is important to use a System.Array[System.Byte] object to pass the data between the script and Atmel Studio.

import System

def should_process_breakpoint(studio_interface,
breakpoint_id,
obj):
vals = System.Array[System.Byte]([1, 2, 3, 4, 5, 6, 7, 8, 9])

studio_interface.Print("ret == vals => {!r}".format(ret == vals), "Python")
return True

The CalcNumericValue is a shorthand for the CalcValue call. It will return the numeric value of the symbol or the provided default value if the function fails to retrieve the value of the symbol.
def should_process_breakpoint(studio_interface,
breakpoint_id,
obj):
a = studio_interface.CalcNumericValue("a", 0)
if a == 0:
studio_interface.Print("a was 0 or default", "Value scripts")
else:
studio_interface.Print("a = {}".format(a), "Value scripts")
return True

The CalcValue function is used to retrieve information about a symbol in the scope where the target code is running. The return value of this call is a list of information containing the address of the symbol, symbol information, and value. The objects sent to this list contains all known information about a symbol, but the most useful field is the last element which contains the value of the evaluated symbol.
def should_process_breakpoint(studio_interface,
breakpoint_id,
obj):
a = studio_interface.CalcValue("a")
# a now contains all information about the variable a.
# It is a list with the following members:
# a = [
#   <Atmel.VsIde.AvrStudio.Services.TargetService.TCF.Services.ValueInfo>,
#   <Atmel.VsIde.AvrStudio.Services.TargetService.TCF.Services.SymbolInfo>,
#   <Atmel.VsIde.AvrStudio.Services.TargetService.TCF.Services.ExpressionInfo>,
#   '1' ] <-- This is the value of the symbol as a string, here it had the value 1

studio_interface.Print("Value of a = {}".format(a[3]), "Value Scripts")
return True

Note: The different objects returned by the CalcValue call contains objects that are either internal or documented in the Atmel Studio SDK. Use the python dir() command to look at the fields that are exported.