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
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 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 access mode 'x' simics> bp.memory.break p:0x4711 Breakpoint 3 set on address 0x4711 in 'board.mb.cpu0.mem[0][0]' with access mode 'x' simics> bp.memory.break 0x4711 Breakpoint 4 set on address 0x4711 in 'board.cell_context' with 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.
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.
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]', 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 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', 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.
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.
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.
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.
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.
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:
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.
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.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 | |
ARC | mov 0, n |
1 ≤ n < 64 | |
ARM | orr rn, rn, rn |
0 ≤ n ≤ 14 | |
ARMv8 | orr xn, xn, xn |
0 ≤ n ≤ 31 | |
ARM Thumb-2 | orr.w rn, rn, rn |
0 ≤ n ≤ 12 | |
H8300 | brn n |
−128 ≤ n ≤ 127 | |
M680x0 | dbt dx,y |
0 ≤ n < 0x3ffff | |
x=n[17:15], y=n[14:0] * 2 | |||
MIPS | li %zero, n |
0 ≤ n < 0x10000 | |
Nios II | or rN, rN, rN, |
0 ≤ N < 32 | |
PowerPC | rlwimi x,x,0,y,z |
0 ≤ n < 8192 | new encoding |
x=n[12:8], y=n[7:4], z=n[3:0]|16 | |||
PowerPC | rlwimi x,x,0,y,z |
0 ≤ n < 32768 | old encoding |
x=n[14:10], y=n[9:5], z=n[4:0] | |||
RISC-V | srai zero, zero, n |
0 ≤ n ≤ 31 | |
SH | mov rn, rn |
0 ≤ n < 16 | |
SPARC | sethi n, %g0 |
1 ≤ n < 0x400000 | |
x86 | cpuid |
0 ≤ n < 0x10000 | |
with eax = 0x4711 + n * 216 |
Reserved Magic Numbers |
0 |
12 |
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.