Previous - Up - Next

12.1   Breakpoints

Like an ordinary debugger, Simics can run user binaries, allowing the user to set breakpoints, inspect state, single step, etc. Some difficult bugs are easier to find using various esoteric breakpoint types. In Simics you can set breakpoints on:

Simics is fully deterministic, allowing you to narrow down the location of difficult bugs. If your session has interactive input, you can record it using the recorder and replay when you need to reproduce the same execution. If Hindsight is available, you can of course freely go forward and backward in time until you found the location of the problem.

12.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 the memory access can be specified as any combination of read, write, and execute.

Physical memory breakpoints refer to addresses within a memory space, so the breakpoint itself is always connected to a specific memory space object in Simics. If this object is known by name (as phys_mem0 in the following example), the breakpoint can be set with the break command:

    simics> phys_mem0.break address = 0x10000 length = 16 -w
    Breakpoint 1 set on address 0x10000, length 16 with access mode 'w'

Virtual memory breakpoints are handled by context objects:

    simics> primary_context.break 0x1ff00
    Breakpoint 1 set on address 0x1ff00 with access mode 'x'

Note that by default, all simulated processors in one Simics process share one 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> cpu1.set-context foo
    simics> cpu7.set-context foo
    simics> foo.break 0xffffffffbfc008b8

The break command can also be used without explicitly specifying an address space or context object. Instead you can prefix the address with p: for a physical address, or v: for a virtual address. The breakpoint will refer respectively to the memory space (physical address) or context (virtual address) connected to the current front-end processor (as specified with the pselect command). Note that, unless you have created a new context for the current processor, the breakpoint will apply to all processors.

As you can see in the following example, Simics interprets a breakpoint address as virtual unless p: is explicitly specified:

    simics> break v:0x4711
    Breakpoint 2 set on address 0x4711 with access mode 'x'
    simics> break p:0x4711
    Breakpoint 3 set on address 0x4711 with access mode 'x'
    simics> break 0x4711
    Breakpoint 4 set on address 0x4711 with access mode 'x'
    Note: overlaps with breakpoint 2

Execution breakpoints can be modified with filter rules to only trigger when instructions match certain syntactical criteria. This feature is mainly useful with breakpoints covering large areas of memory. The commands available are set-prefix (to match the start of an instruction), set-substr (to match a particular substring), and set-pattern (to match the bit pattern of the instruction). The commands work by modifying an existing breakpoint, so you first have to set an execution breakpoint and then modify it to match only particular expressions.

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> break 0x10000 0x2000 -x
    Breakpoint 1 set on address 0x10000, length 8192 with access mode 'x'
    simics> set-prefix 1 "add"

Simics will stop when the first add instruction is encountered. For more information, see the Simics Reference Manual or use the help break command.

12.1.2   Temporal Breakpoints

Unlike an ordinary debugger, Simics can handle temporal breakpoints, i.e., breakpoints in time. As the concept of time is based on steps and cycles, a temporal breakpoint refers to a specific step or a cycle count for a given processor object:

    simics> cpu0.cycle-break 100
    simics> cpu0.step-break 100

In the example above, the breakpoints are specified relative to the current time. It is also possible to set temporal breakpoints in absolute time (where 0 refers to the time when the original configuration was set up in Simics). When Hindsight is available, you can freely set time breakpoints in the past as well as in the future.

    simics> cpu0.cycle-break-absolute 100
    simics> cpu0.step-break-absolute 100

All the commands cycle-break, step-break, cycle-break-absolute, and step-break-absolute, can be given without prefixing them with the CPU. Note that the in this case the commands will plant a breakpoint for current front-end processor (and not all processors).

12.1.3   Control Register Breakpoints

A control register breakpoint is triggered when a selected control register is accessed. The control register is specified either by name or number, and the access type can be any combination of read or write. For example:

    simics> break-cr reg-name = asi

Note that the exact arguments to this command depend on the target architecture. A list of available control registers can be obtained by tab-completing the reg-name argument. See the documentation for break-cr in the Simics Reference Manual for more information..

12.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 the hme0 device, we would use the following syntax:

    simics> break-io object-name = hme0

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

12.1.5   Graphics Breakpoints

The graphics-console can be used to save and set graphical breakpoints. A graphical breakpoint is a rectangular area on the simulated display that triggers a hap (Gfx_Break_String) whenever the pixels inside the saved breakpoint rectangle exactly match those on the display.

The following commands can be used to save and set breakpoints for a graphics console:

<gfx-console>.save-break filename [comment]
Let the user select a rectangular area inside the graphics console using the mouse pointer. To cancel the select operation press the right mouse button. The selected area will be saved as a graphical breakpoint file. You can add an optional comment that will be put in the beginning of the (binary) breakpoint file.
<gfx-console>.save-break-xy filename left top right bottom [comment]
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. You can add an optional comment that will be put at the beginning of the breakpoint file.
<gfx-console>.break filename
Activate a previously saved breakpoint and return a breakpoint id. When a graphical breakpoint is reached, it is immediately deleted and a Gfx_Break_String hap is triggered. If no callbacks for this hap were registered, Simics halts execution and returns to the command prompt.
<gfx-console>.delete id
Delete the breakpoint associated with id.
12.1.6   Text Output Breakpoints

The text console can set breakpoints on the occurrence of certain character sequences in the output sent to the screen.


Note: To find out if a specific simulated machine uses a text console, look for an object of class text-console in the list provided by list-objects once the configuration is loaded.

12.1.7   Magic Instructions and Magic Breakpoints

For each simulated processor architecture, a special 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 (see chapter 8 to get more information about haps).

If the architecture makes it possible, an immediate value is encoded 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 in 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 immediate value to use.


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. You will need to write by yourself the inline assembly corresponding to the magic instruction you want to use. The GCC compiler should always be supported.


Note: Using magic instructions in other languages than C requires the ability to insert inline assembler in a program, or at least the ability to call arbitrary functions written in assembly. For example, in Java it would be necessary to use the JNI interface. As always, check your compiler and language documentation for details on how to enter inline assembly.

A complete definition of magic instructions and the values the parameter n can take is provided in figure 3.

TargetMagic instructionConditions on n
Alphabinary: 0x70000000n = 0
ARMorreq rn, rn, rn0 <= n < 15
IA-64nop (0x100000 + n)0 <= n < 0x100000
MIPSli %zero, n0 <= n < 0x10000
MSP430bis r0,r0n = 0
PowerPC 32-bitmr n, n0 <= n < 32
PowerPC 64-bitfmr n, n0 <= n < 32
SPARCsethi n, %g01 <= n < 0x400000
x86xchg %bx, %bxn = 0
Figure 3. Magic instructions for different Simics Targets

Here is a simple pseudo-code example:

#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 prompt.

Magic breakpoints can be enabled and disabled with the commands magic-break-enable and magic-break-disable . 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.

On architectures that only offer a single magic instruction (x86 and Alpha), more information can be passed from the simulation to the magic instruction hap handler by putting data values in machine registers prior to triggering the magic instruction.

As a concrete example, on the x86, the hap argument can only be 0. This can be worked around by putting extra information into register eax before executing the MAGIC(0) magic instruction. The hap handler for magic instructions then needs to read the value from eax and do different things depending on its contents.

The following is an example program implementing this technique, using the gcc compiler's syntax for inserting inline assembler:

#include "stdio.h"
#include "magic-instruction.h"

#define MY_MAGIC(n) do {                                        \
    asm volatile ("movl %0, %%eax" : : "g" (n) : "eax");        \
    MAGIC(0);                                                   \
} while (0)

int main(void)
{
    printf("Hello,\n");
    MY_MAGIC(1);
    printf("World!\n");
    MY_MAGIC(2);
    return 0;
}

The hap handler for this would look something like the following:

@def call_back_1(cpu):
    pr("call back one triggered\n")

@def call_back_2(cpu):
    pr("another one here\n")

@def hap_callback(user_arg, cpu, arg):
    eax = cpu.eax             # read value passed from program
    if eax == 1:              # take appropriate action
        call_back_1(cpu)
    elif eax == 2:
        call_back_2(cpu)
    else:
        print "Unknown callback, eax is", eax
        SIM_break_simulation("snore")

@SIM_hap_add_callback("Core_Magic_Instruction", hap_callback, None)

The same technique can be applied to other architectures, but you need to adapt the names of the registers involved.


Note: This method is slightly intrusive since a register will change its value. It is thus important to make sure that the compiler is aware of the change to the register so that no broken code is emitted; in the gcc compiler, this is specified in the last argument to the asm() statement.

Previous - Up - Next