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.

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.

The functions that Atmel Studio will try to load is shown below with its function signature.

def should_process_breakpoint(studio_interface, 
                              breakpoint_address, 
                              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_address, 
                             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, 
             reset_address):
	"""
	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 a 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 evaluated expression 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 ToolsTools, 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_address, 
                              breakpoint_id, 
                              obj):
	vals = System.Array[System.Byte]([1, 2, 3, 4, 5, 6, 7, 8, 9])

	studio_interface.WriteMemory(data=vals, adr=0, type="eeprom")

	ret = studio_interface.ReadMemory(adr=0, type="eeprom", count=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_address, 
                              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 in this list contains all known information about a symbol, but the most usefull field is the last element which contains the value of the evaluated symbol.

def should_process_breakpoint(studio_interface, 
                              breakpoint_address, 
                              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.