11 Low-level Debugging with Simics 11.2 Symbolic Debugging
Simics User's Guide  /  III Low-Level Debugging  /  11 Low-level Debugging with Simics  / 

11.1 Breakpoints

Simics can set breakpoints on code and data. Unlike most debuggers, Simics breakpoints are not limited by what the hardware can support; for example, there is no restriction on the number of read/write breakpoints (also known as watchpoints).

In Simics you can set breakpoints on, for instance:

Simics is fully deterministic, and breakpoints in Simics are fully non-intrusive. This makes it possible to narrow down the location of difficult bugs by re-running the exact same run as many times as you need.

But there is often no need to run the bug multiple times, since breakpoints work even when the simulation is running backwards. For example, to find the code responsible for writing garbage to a pointer, run forward until your program crashes, then set a write breakpoint on the (now clobbered) pointer, and run backward; the breakpoint will trigger at the point in time when the pointer was last written to.

Simics has a breakpoint manager which stores information about all breakpoints, of all types. It has commands for listing breakpoints, enabling, deleting and other common operations. Breakpoint creation is specific to the different types, and is described in the following chapters. The breakpoint manager is the object bp.

In general, breakpoint creation commands return an ID, which can be used to later delete the breakpoint, or obtain information about it. The most important generic commands are

11.1.1 Memory Breakpoints

A memory breakpoint stops the simulation whenever a memory location in a specified address interval is accessed. The address interval can be of arbitrary length and the type of memory access can be specified as any combination of read, write, and execute.

The easiest way to set memory breakpoints is to use the bp.memory.break command:

simics> output-radix 16
simics> bp.memory.break p:0x10000
Breakpoint 1 set on address 0x10000 in 'board.mb.cpu0.mem[0][0]' with Line break
access mode 'x'

Prefix the address with p: or v: to get a physical or virtual address, respectively. As you can see in the following example, Simics defaults to interpreting a breakpoint address as virtual if you do not specify otherwise:

simics> board.mb.cpu0.core[0][0]->current_virtual_context = "board.cell_context"
simics> bp.memory.break v:0x4711
[board.mb.cpu0.core[0][0] info] VMP not engaged. Reason: virtual breakpoint.
Breakpoint 2 set on address 0x4711 in 'board.cell_context' with Line break
access mode 'x'
simics> bp.memory.break p:0x4711
Breakpoint 3 set on address 0x4711 in 'board.mb.cpu0.mem[0][0]' with Line break
access mode 'x'
simics> bp.memory.break 0x4711
Breakpoint 4 set on address 0x4711 in 'board.cell_context' with Line break
access mode 'x'
Note: overlaps with breakpoint 2

This way of setting breakpoints will attach them to the memory space (physical address) or context (virtual address) connected to the current processor. If this is not exactly what you want, read on.

Note: The current processor can be set with the pselect command:

simics> pselect board.mb.cpu0.core[0][0]

Without an argument, pselect prints the current processor:

simics> pselect
"board.mb.cpu0.core[0][0]"

Also useful is the cpu object alias, which returns the current processor and can be used to expand child objects, commands and aliases on the current processor.

simics> cpu # type cpu<tab>
cpu->  cpu-pages-dump  cpu-switch-time  cpu.  cpu.vtime.

Physical memory breakpoints are handled by memory space objects. A memory space represents a physical address space; they sit between the processor and the actual hardware devices, for example RAM, that can be accessed with read and write instructions. Breakpoints are created with the memory space's break command:

simics> board.mb.cpu0.core[0][0]->physical_memory
"board.mb.cpu0.mem[0][0]"
simics> board.mb.cpu0.mem[0][0].bp-break-memory address = 0x10000 length = 16 -w
Breakpoint 1 set on address 0x10000 in 'board.mb.cpu0.mem[0][0]', Line break
length 16 with access mode 'w'

Virtual memory breakpoints are handled by context objects. A context represents a virtual address space; you can learn more about them in chapter 11.2. Essentially, they provide a level of indirection between processors and virtual memory breakpoints; a processor has a current context, which in turn has virtual breakpoints:

simics> board.mb.cpu0.core[0][0]->current_context
"board.cell_context"
simics> board.cell_context.bp-break-memory 0x1ff00
Breakpoint 2 set on address 0x1ff00 in 'board.cell_context' with Line break
access mode 'x'

Note that by default, all simulated processors in a cell share one context (celln_context). If you want a virtual breakpoint to apply only to a subset of the processors, create a new context just for them:

simics> new-context foo
simics> board.mb.cpu0.core[0][0].set-context foo
simics> foo.bp-break-memory 0xffffffffbfc008b8

When creating execution breakpoints, it is possible to specify filter rules to only trigger when instructions match certain syntactical criteria. This feature is mainly useful with breakpoints covering large areas of memory. The available parameters to the break command are prefix (to match the start of an instruction), substr (to match a particular substring), and pattern (to match the bit pattern of the instruction).

For example, to stop when an instruction with the name add is executed in a memory range from 0x10000 to 0x12000, use the following commands:

simics> bp.memory.break 0x10000 0x2000 -x prefix = "add"
Breakpoint 1 set on address 0x10000 in 'board.cell_context', Line break
length 8192 with access mode 'x'

Simics will now break on the first add instruction encountered (or the last, if the simulation runs backward). For more information, see the Simics Reference Manual or use the help bp.memory.break command.

11.1.2 Temporal Breakpoints

Unlike an ordinary debugger, Simics can handle temporal breakpoints, i.e., breakpoints in time. Since the concept of time is based on steps and cycles, to be precise a temporal breakpoint refers to a specific step or a cycle count as measured by a given processor, but one can also specify breakpoints in seconds of virtual time:

simics> bp.cycle.break cpu0 100
simics> bp.step.break cpu0 100
simics> bp.time.break cpu0 1

In the example above, the breakpoints are specified relative to the current time. It is also possible to give temporal breakpoints in absolute time (where 0 refers to the time when the original configuration was set up in Simics).

simics> board.mb.cpu0.core[0][0].bp-break-cycle -absolute 100
simics> board.mb.cpu0.core[0][0].bp-break-step -absolute 100
simics> board.mb.cpu0.core[0][0].bp-break-time -absolute 1

The commands bp.cycle.break, bp.step.break and bp.time.break, can be given without providing the CPU ad argument. This will set a breakpoint for the current processor.

11.1.3 Control Register Breakpoints

A control register breakpoint triggers when the selected control register is accessed. The access type is determined by the break command parameters used.

To break on a control register write, use bp.control_register.break. For example, to break when the register cr0 is written to:

simics> bp.control_register.break name = cr0

To break on a control register read, use bp.control_register.break -r. For example, to break when the register cr0 is read:

simics> bp.control_register.break name = cr0 -r

A list of available control registers can be obtained by tab-completing the name argument. See the documentation for bp.control_register.break in the Simics Reference Manual for more information.

11.1.4 I/O Breakpoints

An I/O breakpoint is always connected to a specific device object. The breakpoint is triggered when that device is accessed. The breakpoint is set using the break-io command, which take the device name as a parameter. For example, to break on accesses to a device called dma, we would use the following syntax:

simics> break-io device = board.mb.sb.dma

A list of devices can be obtained by tab-completing the device argument.

11.1.5 Text Output Breakpoints

Many simulated machines have a text console—a terminal window hooked up to a serial port on the target machine, so that you can type commands to the target and get replies. In particular x86 systems also often have a graphics console—a window acting as a monitor to the machine, hooked up to a graphics device on the target machine, and usually also to keyboard and mouse devices.

A text console can halt the simulation on the occurrence of a given character sequence in the output; this is called a text output breakpoint. This is also possible on a graphics console when it is running in text mode graphics, assuming that the graphics device implements this.

To set a breakpoint, use the command bp.console_string.break console string, which returns a breakpoint id. Simics will stop when string appears in the output.

Note: To find out if a specific simulated machine uses these consoles, look for objects of class textcon or graphcon in the list provided by list-objects once the configuration is loaded.

11.1.6 Graphics Breakpoints

If your target machine has a graphical display (as opposed to just a text console), you can set graphical breakpoints on it. A graphical breakpoint is a (small or large) bitmap image and a pair of coordinates; when the pixels at those coordinates on the simulated display exactly match the breakpoint image, the simulation will halt.

Graphics breakpoints can be created from the console GUI window, but the following commands can also be used to save and set breakpoints for a graphics console:

gfx-console.save-break-xy filename left top right bottom
Let the user specify a rectangular area inside the graphics console using the top left and bottom right corners coordinates. The selected area will be saved as a binary graphical breakpoint file.
gfx-console.break-gfx filename
Activate a previously saved breakpoint and return a breakpoint id. When a graphical breakpoint is reached, Simics halts execution and returns to the command prompt.
gfx-console.unbreak-gfx id
Delete the breakpoint associated with id.

11.1.7 Magic Instructions and Magic Breakpoints

For each simulated processor architecture, a special nop (no-operation) instruction has been chosen to be a magic instruction for the simulator. When the simulator executes such an instruction, it triggers a Core_Magic_Instruction hap and calls all the callbacks functions registered on this hap.

Since magic instructions are just no-operation instructions on hardware, you can run code containing magic instructions on hardware as well as in the simulator, but you will not get any of the extra behavior Simics implements for the magic instruction.

If the architecture makes it possible, a magic instruction parameter is encoded as an immediate value in the magic instruction. When the hap is triggered, this value is passed as an argument to the hap handlers. This provides the user with a rudimentary way of passing information from the simulated system to the hap handler.

Magic instructions have to be compiled into the binary files that are executed on the target. The file magic-instruction.h in [simics]/src/include/simics/ defines a MAGIC(n) macro that can be used to place magic instructions in your program, where n is the magic instruction parameter value to encode. Some parameter values are reserved for internal use; see figure 28 and figure 29.

A complete list of magic instructions and the range of the parameter n is provided in figure 28.

Note: The declaration of the macros are heavily dependent on the compiler used, so you may get an error message telling you that your compiler is not supported. In that case, you will have to write the inline assembly corresponding to the magic instruction you want to use. The GCC compiler should always be supported.
Note: The magic instruction macro is directly usable only from C and C++; if your program is written in another language, you will have to call a C function that uses the macro, or an assembly function that includes the magic instruction. (If the language supports inline assembly, that can of course be used as well.) For example, in Java it would be necessary to use the JNI interface. Check your compiler and language documentation for details.
Note: (x86 specific) The magic instruction parameter is passed through the eax register on x86. The eax register contents is preserved when the magic instruction is emulated by Simics. Thus the eax register value can be used just after the magic instruction to extract the magic parameter value from it. The values of the ebx, ecx and edx registers are undefined after the magic instruction. Earlier Simics versions had different definitions of magic instructions for x86. The old magic instruction can still be used on x86 if the processor is not running in VMP mode.
Note: (PowerPC specific) There are two different encodings of the rlwimi-based magic instruction on PowerPC. On 64-bit models, the new encoding is always used; it is also the one generated by the MAGIC() and MAGIC_BREAKPOINT() macros in magic-instruction.h when compiling 64-bit PowerPC code. The old encoding is used on 32-bit models when the old_rlwimi_magic attribute is set. When compiling 32-bit PowerPC code, the macros will use the old encoding unless the preprocessor symbol SIM_NEW_RLWIMI_MAGIC has been defined.

It is recommended that the new encoding is used with 32-bit PowerPC models and code by setting the appropriate attribute and preprocessor symbol.

Target Magic instruction Conditions on n
ARCmov 0, n 1 ≤ n < 64
ARMorr rn, rn, rn 0 ≤ n ≤ 14
ARMv8orr xn, xn, xn 0 ≤ n ≤ 31
ARM Thumb-2orr.w rn, rn, rn 0 ≤ n ≤ 12
H8300brn n −128 ≤ n ≤ 127
M680x0dbt dx,y 0 ≤ n < 0x3ffff
x=n[17:15], y=n[14:0] * 2
MIPSli %zero, n 0 ≤ n < 0x10000
Nios IIor rN, rN, rN, 0 ≤ N < 32
PowerPCrlwimi x,x,0,y,z 0 ≤ n < 8192 new encoding
x=n[12:8], y=n[7:4], z=n[3:0]|16
PowerPCrlwimi x,x,0,y,z 0 ≤ n < 32768 old encoding
x=n[14:10], y=n[9:5], z=n[4:0]
RISC-Vsrai zero, zero, n 0 ≤ n ≤ 31
SHmov rn, rn 0 ≤ n < 16
SPARCsethi n, %g0 1 ≤ n < 0x400000
x86 cpuid 0 ≤ n < 0x10000
with eax = 0x4711 + n * 216
Figure 28. Magic instructions for different Simics Targets
Reserved Magic Numbers
 0
12
Figure 29. Reserved magic numbers, for internal use only.

Here is a simple example of how to use magic instructions:

#include "magic-instruction.h"

int main(int argc, char **argv)
{
        initialize();
        MAGIC(1);                     // tell the simulator to start
                                      // the cache simulation
        do_something_important();
        MAGIC(2);                     // tell the simulator to stop
                                      // the cache simulation
        clean_up();
}

This code needs to be coupled with a callback registered on the magic instruction hap to handle what happens when the simulator encounters a magic instruction with the arguments 1 or 2 (in this example, to start and stop the cache simulation).

Simics implements a special handling of magic instructions called magic breakpoints. A magic breakpoint occurs if magic breakpoints are enabled and if the parameter n of a magic instruction matches a special condition. When a magic breakpoint is triggered, the simulation stops and returns to the prompt.

Magic breakpoints can be enabled with the command bp.magic.break . The condition on n for a magic instruction to be recognized as a magic breakpoint is the following:

n == 0 || (n & 0x3f0000) == 0x40000

Note that the value 0 is included for architectures where no immediate can be specified. The file magic-instruction.h defines a macro called MAGIC_BREAKPOINT that places a magic instruction with a correct parameter value in your program.

11 Low-level Debugging with Simics 11.2 Symbolic Debugging