Features of GUSS

Table of contents

Break- and watchpoints

Breakpoints

GUSS provides several types of break- and watchpoints. One of the most used breakpoints is the time breakpoint. It halts the simulator after it has executed a specified number of instructions. It can be useful for inspecting processor state as regular periods.
(guss) tbreak 9000
Time breakpoint installed at 9,000 (+9000) simulated insns.
(guss) run
Topsy 1.1 (c) 1996-1998, ETH Zurich, TIK
($Id: Configuratio
Time breakpoint 1 caught at <0x8002647c>
(guss) show register sp
sp       0x80000f7c
In the example above a time breakpoint is inserted at 9000 simulated instructions. The simulation session is started and after 9000 instructions the simulator halts execution.

As in GDB, it is possible to list information about installed breakpoints. This is done (as in GDB) with the info breakpoints command.

(guss) info breakpoints 
Num Type                Hits Physical   Virtual    What
1   time breakpoint        1                       9,000 ticks
2   breakpoint             0 0x00010242 0x80010242 
Since breakpoint 1 is a time breakpoint no physical or virtual address is listed. Breakpoint 2 is a "normal" breakpoint in that sense that is breaks the execution flow whenever an instruction at 0x00010242 is executed. Note that breakpoints is set on physical addresses.

Another example, this time made with the OpenRISC target of GUSS. This example runs the ORPMON, a monitor software for the OpenRISC architecture. A breakpoint on function _printf is installed. Since the OpenRISC target is not configured with a MMU and has no memory aliases it possible to omit the virtual flag to the break command.

Simulation is started and breakpoint 1 breaks at _printf, as it should. Since GUSS does not have back-tracing facilities as GDB does, it is not possible to get a stack trace to know where the call was made from. Instead the branch-profile command is used to display branches made into the _printf function. More on that command in the next section.

(guss) break _printf
(guss) info breakpoints 
Num Type                Hits Physical   Virtual    What
1   breakpoint             0 0x000031f0 0x000031f0 <_printf>
(guss) run
Breakpoint 1 caught at 31f0 <_printf>
(guss) branch-profile _printf
Branches into physical range: 0x31f0-0x3d74
 Source                          Target
 physical   virtual    (source)  physical   virtual    (source)       count
 0x00002ef0 0x00002ef0 cpu#0     0x000031f0 0x000031f0 cpu#0              1
...

Watchpoints

GUSS provides memory watchpoints. The watchpoint can be set to be triggered on read, write or access to a specified memory range.
(guss) wwatch 0x30000000 +16
(guss) info breakpoints 
Num Type                Hits Physical   Virtual    What
1   memory watchpoint      0 0x30000000 0x30000000 (0x10 bytes)
(guss) run
Memory watchpoint 1 caught at 4027c0 
  Accessing address 0x30000004
In the example above a write watchpoint is installed for the memory range 0x30000000 to 0x30000015. The watchpoint is hit at 0x4027c0 (0x414 in chunk_alloc.) To install a read watchpoint, use the rwatch command, or the awatch to install a access watchpoint (either read or write).

Profiling

Branch profiling

GUSS provides a profiling facility called branch profiling. All branches made by the simulated program is gathered, and assembled as from-to pairs (called a branch arc) into a call-graph. The call-graph can be dumped into a gong.out-file, using the dump-call-graph command, which can later be used together with gprof, the GNU profiler.

Since all branches is stored in a hash table it is possible to perform difference analysis on the call-graph. GUSS can for example display all branches from, within and to a specified range. Example:

(guss) branch-profile/v begin
Branches into physical range: 0x1fc00200-0x1fc0030c
 Source                          Target
 physical   virtual    (source)  physical   virtual    (source)       count
 0x1fc00000 0xbfc00000 cpu#0     0x1fc00200 0xbfc00200 cpu#0              1
 0x1fc0039c 0xbfc0039c cpu#0     0x1fc00308 0xbfc00308 cpu#0              1
Branches from physical range: 0x1fc00200-0x1fc0030c
 Source                          Target
 physical   virtual    (source)  physical   virtual    (source)       count
 0x1fc00300 0xbfc00300 cpu#0     0x1fc0030c 0xbfc0030c cpu#0              1
Branches within physical range: 0x1fc00200-0x1fc0030c
 Source                          Target
 physical   virtual    (source)  physical   virtual    (source)       count
 0x1fc002f8 0xbfc002f8 cpu#0     0x1fc002e8 0xbfc002e8 cpu#0            418
 0x1fc002b0 0xbfc002b0 cpu#0     0x1fc00294 0xbfc00294 cpu#0             63
The example above displays all branches from, within and to the function begin. GUSS collects statistics on physical addresses, but the symbol table contains virtual addresses, so the v flag must be used with the branch-profile command.

The command also translate the physical addresses into virtual addresses, if so is possible. Under the column source is the processor that did the translation listed. If no processor could do a translation for the physical address *unknown* is printed.

It is also possible to display symbol table information with the command. This is done by setting the print-arc-symbols variable to on.

(guss) set print-arc-symbols on
(guss) branch-profile/v begin
Branches into physical range: 0x1fc00200-0x1fc0030c
 Source                          Target
 physical   virtual    (source)  physical   virtual    (source)       count
 0x1fc00000 0xbfc00000 cpu#0     0x1fc00200 0xbfc00200 cpu#0              1
   Branch from <__start> to <begin>
 0x1fc0039c 0xbfc0039c cpu#0     0x1fc00308 0xbfc00308 cpu#0              1
   Branch from <entry+0x90> to <begin+0x108>
Branches from physical range: 0x1fc00200-0x1fc0030c
 Source                          Target
 physical   virtual    (source)  physical   virtual    (source)       count
 0x1fc00300 0xbfc00300 cpu#0     0x1fc0030c 0xbfc0030c cpu#0              1
   Branch from <begin+0x100> to <entry>
...
You can see that the function begin was called from __start, and on address 0xbfc0030 (begin+0x100) it calls the function entry which returns to begin+0x108 from the address 0xbfc0039c.

Execution profile

The design of GUSS is largely focused on techniques for gathering detailed information on the execution.

Example, say that you want to know where the hot spots of the simulated program is. Say also that you want to focus on branches.

(guss) set branches-from-weight 1
(guss) set branches-to-weight 1
(guss) weight 
Summary for physical memory:
 physical   virtual    (source)     decode         pc       from         to      weight
 0x00400000 0x00400000 cpu#0           824 65,130,898  3,684,368  3,684,371   7,368,739
 0x00407000 0x00407000 cpu#0           339        931         88         90         178
 0x00402000 0x00402000 cpu#0           544        919         61         58         119
 0x00411000 0x00411000 cpu#0           475        793         54         58         112
 0x0042e000 0x0042e000 cpu#0           164        621         51         51         102
 0x0041a000 0x0041a000 cpu#0           271        509         40         42          82
 0x0042a000 0x0042a000 cpu#0           311        297         37         36          73
 0x0042d000 0x0042d000 cpu#0           196        268         34         34          68
 0x0042c000 0x0042c000 cpu#0           237        310         34         33          67
 0x0042b000 0x0042b000 cpu#0           211        320         27         26          53

Number of insns executed (based on branch arcs): 65,137,785
The weight command calculates an linear sum of the entire memory, sorting and displaying the largest values. Now you find that most of the action is in the page with the physical address 0x400000.

Say you want to see more in detail. For this the summary command can be used. It takes a memory range or a symbol name as argument. It splits the memory range into buckets of 64 bytes and does a linear sum for each of those and displays the larges values.

(guss) summary 0x00400000 0x00401000
Summary for range: 0x400000-0x401000 (in buckets of 64 bytes)
 physical   virtual    (source)     decode         pc       from         to      weight
 0x00400580 0x00400580 cpu#0            16  9,837,932  2,088,903    125,104   2,214,007
 0x00400500 0x00400500 cpu#0            16 15,230,983      4,801  2,002,500   2,007,301
 0x00400540 0x00400540 cpu#0            16 31,015,775    961,203    927,004   1,888,207
 0x00400700 0x00400700 cpu#0            16  1,928,444    240,867    240,571     481,438
 0x00400480 0x00400480 cpu#0            16  3,261,000    114,000    217,200     331,200
 0x004004c0 0x004004c0 cpu#0            16  3,191,390    271,501     47,100     318,601
 0x00400440 0x00400440 cpu#0            12    488,400          0    121,500     121,500
 0x004006c0 0x004006c0 cpu#0            16     27,088      2,430        294       2,724
 0x00400600 0x00400600 cpu#0            16     19,504          0      2,430       2,430
 0x00400800 0x00400800 cpu#0            16      3,925        600        300         900
If you want to go even deeper and see per-instruction statistics the disassemble command can be used. Example of such.
(guss) disassemble  0x00400580  0x004005a0
(physical)     decode         pc       from         to (disassembled insn)
0x00400580          1  2,002,802          0     39,003 sll      a3,v1,0x1
0x00400584          1  2,002,802          0          0 slt      t3,t2,a3
0x00400588          1  2,002,802  1,759,800          0 beqz     t3,400524 
0x0040058c          1  2,002,802          0          0 sll      t6,v1,0x2
0x00400590          1    243,002          0          0 lw       a2,0(t4)
0x00400594          1    243,002          0          0 lw       a3,0(a0)
0x00400598          1    243,002          0          0 addiu    t2,t2,-1
0x0040059c          1    243,002          0          0 sw       a2,0(a0)
If you find it hard to remember what the different profilers mean (decode, pc, ...) you can always issue the info profilers command.
(guss) info profilers 
Active profilers, listed left to right.
Column 0: Instruction has been decoded (1/0) (decode-flag-weight = 0)
Column 1: Count of instruction execution (based on branch arcs) (pc-weight = 0)
Column 2: Number of (taken) branches from address (branches-from-weight = 1)
Column 3: Number of (taken) branches to address (branches-to-weight = 1)

Inspecting state

One important aspect of running a program in a simulator is that at any time be able to inspect the state of the system. For example, the user would like to know how much memory the program consumes, and what regions of the memory has been used for data and instruction accesses.

The command show pmap does exactly that. It lists all configured memory regions and display their usage. In the example below D means data page and I means that instructions has been fetched from the page.

(guss) show pmap
Memory map for global physical memory:

0x00000000: I...............................IIIIIIIIDDDDDDDD.............DDD
0x00040000: .DDDDDDD........................................................
0x000c0000: DDIIIDDD.......................................................D

Pages of 4K allocated: 36 (147,456 bytes of host memory)
Regions with zero accesses is omitted. The command also displays the number of 4K pages Thais has been allocated by the simulator. This gives an estimation of how much memory the simulator uses.

It is of course possible to display values of processor registers. This is done with the show register command. Example;

(guss) show register cc a1
pc       0x800255e4
a1       0x8003ec7c
Note that the program counter holds a virtual address. The example is made with the MIPS target of GUSS, and region 0x80000000 happen to be an alias for region 0x00000000. This means that the processor is actually executing instructions at physical address 0x000255e4.


guss-hackers Last modified: Sat May 3 02:04:48 CEST 2003