mirror of
https://github.com/libretro/hatari.git
synced 2026-11-07 10:36:00 -10:00
Add section on getting backtraces to debugger manual, and add recent debugger improvements to the release notes.
2459 lines
83 KiB
HTML
2459 lines
83 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
<head>
|
||
<title>Hatari Debugger User's Manual</title>
|
||
<meta name="description"
|
||
content="User's manual for the Hatari emulator debugger" />
|
||
<meta name="author" content="Hatari development team" />
|
||
<meta name="keywords" content="hatari, documentation, manual" />
|
||
<meta name="viewport" content="width=device-width; initial-scale=1.0;" />
|
||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
||
<link href="manual.css" rel="stylesheet" type="text/css" />
|
||
<script src="toc.js"></script>
|
||
</head>
|
||
<body>
|
||
|
||
<h1 class="pageheader">Hatari Debugger User's Manual</h1>
|
||
|
||
<p class="pageheader">
|
||
Version 2.6.1, August 2025
|
||
</p>
|
||
<p class="pageheader">
|
||
Debugger manual written by: <strong>Eero Tamminen</strong>
|
||
</p>
|
||
<p class="pageheader">
|
||
See also:
|
||
<strong>
|
||
<a href="manual.html">Hatari manual</a>
|
||
</strong>
|
||
</p>
|
||
<p class="pageheader">
|
||
Hatari on the WWW:
|
||
<strong>
|
||
<a href="https://www.hatari-emu.org/">https://www.hatari-emu.org/</a>
|
||
</strong>
|
||
</p>
|
||
|
||
<h2 class="no-TOC">Index</h2>
|
||
|
||
<div id="generated-toc">
|
||
<!-- The TOC is generated automatically via JavaScript -->
|
||
</div>
|
||
|
||
<h2 id="The_debugger">The debugger</h2>
|
||
|
||
<p>
|
||
Hatari has a built-in debugging interface which can be used for
|
||
analyzing code that runs in the emulated system.
|
||
</p>
|
||
|
||
<p>
|
||
On Unix (Linux / macOS) debugger uses Hatari's parent console window,
|
||
so run Hatari from the command line to use the debugger. On Windows
|
||
"-W" option is needed for the console window. You can add a shortcut
|
||
/ icon on your desktop that does it. On Linux such shortcut would
|
||
execute something like this (replace "xterm" with your favorite
|
||
terminal program):
|
||
</p>
|
||
<pre>
|
||
xterm -T "Hatari debug window" -e hatari
|
||
</pre>
|
||
|
||
<p>
|
||
To run debugger commands from a file at Hatari startup, one can use
|
||
the "--parse <file>" command line option. This is useful e.g.
|
||
for debugging TOS or some demo startup code, or if you always want to
|
||
use some specific debugger setup (breakpoints etc).
|
||
</p>
|
||
|
||
<p>
|
||
Note that when debugger scripts are run, current directory is set to
|
||
the currently running script's directory i.e. all file operations are
|
||
relative to it. After script finishes, earlier current directory is
|
||
restored. To set current directory from a setup script, e.g. for
|
||
scripts run at breakpoints, "-f" option needs to be give for the
|
||
"cd" command.
|
||
</p>
|
||
|
||
|
||
<h3>Invoking the debugger</h3>
|
||
|
||
<p>
|
||
Debugger can be invoked with the <span class="key">AltGr + Pause</span>
|
||
key combination.
|
||
</p>
|
||
|
||
<p>
|
||
Command line "-D" option can be used to toggle whether m68k
|
||
exceptions will invoke the debugger. Which exceptions cause
|
||
this, can be controlled with the "--debug-except" option.
|
||
</p>
|
||
|
||
<p>
|
||
Giving "-D" option at Hatari startup is not advised because TOS HW
|
||
checks generate some exceptions at every TOS boot. It is better to
|
||
toggle exception catching later from the debugger with the "setopt -D"
|
||
command.
|
||
</p>
|
||
|
||
<p>
|
||
Alternatively, "--debug-except" option can be prefixed with "prg:"
|
||
(e.g. "--debug-except prg:all") to enable catching of (specified)
|
||
exceptions when Atari program given on Hatari command line gets
|
||
<em>autostarted</em>.
|
||
</p>
|
||
|
||
|
||
<h3>Debugger configuration options</h3>
|
||
|
||
<p>When saving Hatari configuration, current debugger
|
||
settings are also saved to the Hatari configuration file.</p>
|
||
|
||
<p>Settings are following:</p>
|
||
<dl>
|
||
<dt>nNumberBase</dt>
|
||
<dd>Debugger number base.
|
||
Set with the debugger "setopt [bin|dec|hex]" command</dd>
|
||
<dt>nSymbolLines</dt>
|
||
<dd>Number of lines to show when listing debug symbols</dd>
|
||
<dt>nMemdumpLines</dt>
|
||
<dd>Number of memory dump lines to show</dd>
|
||
<dt>nFindLines</dt>
|
||
<dd>Number of find (search) lines to show</dd>
|
||
<dt>nDisasmLines</dt>
|
||
<dd>Number of disassembly lines to show</dd>
|
||
<dt>nBacktraceLines</dt>
|
||
<dd>Number of items to show in stack/bactraces</dd>
|
||
<dt>nExceptionDebugMask</dt>
|
||
<dd>Mask of exceptions which invoke debugger when exceptions catching
|
||
is enabled (-D). Set with the "--debug-except" option</dd>
|
||
<dt>nDisasmOptions</dt>
|
||
<dd>Disassembler output options, set with the "--disasm" option</dd>
|
||
<dt>bDisasmUAE</dt>
|
||
<dd>Whether disassembly uses CPU core internal disassembler ("uae"),
|
||
or the external disassembler ("ext" one with output options).
|
||
Set with the "--disasm" option</dd>
|
||
<dt>nSymbolsAutoLoad</dt>
|
||
<dd>Specifies whether (GEMDOS HD) program symbols are autoloaded when
|
||
program is executed, when debugger is invoked after that, or whether
|
||
symbol autoloading is disabled. Set with the "--symload" option and
|
||
the "symbols autoload [on|off]" command</dd>
|
||
<dt>bMatchAllSymbols</dt>
|
||
<dd>Whether symbol name TAB-completion matches just symbols from
|
||
a relevant code section, or all of them. Toggled with the
|
||
"symbols match" command</dd>
|
||
</dl>
|
||
|
||
<p>These are their defaults:</p>
|
||
<pre>
|
||
[Debugger]
|
||
nNumberBase = 10
|
||
nSymbolLines = -1
|
||
nMemdumpLines = -1
|
||
nFindLines = -1
|
||
nDisasmLines = -1
|
||
nBacktraceLines = 0
|
||
nExceptionDebugMask = 515
|
||
nDisasmOptions = 15
|
||
bDisasmUAE = TRUE
|
||
nSymbolsAutoLoad = 1
|
||
bMatchAllSymbols = FALSE
|
||
</pre>
|
||
|
||
<p>Settings on how many lines are shown can be changed only from the
|
||
configuration file. This needs to changed/set only when Hatari
|
||
debugger is built without readline support, as it is then unable
|
||
to determine terminal size ("-1" = use terminal height).</p>
|
||
|
||
<p>Note that "--debug-except" and "--disasm" options can be given either
|
||
on Hatari command line, or (like all other Hatari command line options),
|
||
with the debugger "setopt" command.</p>
|
||
|
||
|
||
<h3>General debugger use</h3>
|
||
|
||
<p>
|
||
At the debugger prompt, type "help" to get a list of all
|
||
the available commands and their shortcuts:
|
||
</p>
|
||
<pre>
|
||
Generic commands:
|
||
cd ( ) : change directory
|
||
echo ( ) : output given string(s)
|
||
evaluate ( e) : evaluate an expression
|
||
help ( h) : print help
|
||
history (hi) : show last CPU and/or DSP PC values + instructions
|
||
info ( i) : show machine/OS information
|
||
lock ( ) : specify information to show on entering the debugger
|
||
logfile ( f) : open or close log file
|
||
parse ( p) : get debugger commands from file
|
||
rename ( ) : rename given file
|
||
reset ( ) : reset emulation
|
||
screenshot ( ) : save screenshot to given file
|
||
setopt ( o) : set Hatari command line and debugger options
|
||
stateload ( ) : restore emulation state
|
||
statesave ( ) : save emulation state
|
||
trace ( t) : select Hatari tracing settings
|
||
variables ( v) : List builtin symbols / variables
|
||
quit ( q) : quit emulator
|
||
|
||
CPU commands:
|
||
address ( a) : set CPU PC address breakpoints
|
||
breakpoint ( b) : set/remove/list conditional CPU breakpoints
|
||
disasm ( d) : disassemble from PC, or given address
|
||
find ( ) : find given value sequence from memory
|
||
profile ( ) : profile CPU code
|
||
cpureg ( r) : dump register values or set register to value
|
||
memdump ( m) : dump memory
|
||
struct ( ) : structured memory output, e.g. for breakpoints
|
||
memwrite ( w) : write bytes to memory
|
||
loadbin ( l) : load a file into memory
|
||
savebin ( ) : save memory to a file
|
||
symbols ( ) : load CPU symbols & their addresses
|
||
step ( s) : single-step CPU
|
||
next ( n) : step CPU through subroutine calls / to given instruction type
|
||
cont ( c) : continue emulation / CPU single-stepping
|
||
|
||
DSP commands:
|
||
dspaddress (da) : set DSP PC address breakpoints
|
||
dspbreak (db) : set/remove/list conditional DSP breakpoints
|
||
dspdisasm (dd) : disassemble DSP code
|
||
dspmemdump (dm) : dump DSP memory
|
||
dspsymbols ( ) : load DSP symbols & their addresses
|
||
dspprofile (dp) : profile DSP code
|
||
dspreg (dr) : read/write DSP registers
|
||
dspstep (ds) : single-step DSP
|
||
dspnext (dn) : step DSP through subroutine calls / to given instruction type
|
||
dspcont (dc) : continue emulation / DSP single-stepping
|
||
</pre>
|
||
|
||
|
||
<h4 id="Entering_arguments">Entering arguments to debugger commands</h4>
|
||
|
||
<p>
|
||
After writing (with TAB completion) one of the above command names,
|
||
pressing TAB will (for most commands) show all the available subcommands.
|
||
</p>
|
||
|
||
<p>
|
||
To give numbers in other number bases
|
||
than the default/selected one, they need to be prefixed with a
|
||
character indicating this. For decimals this prefix is "#" (#15),
|
||
for hexadecimals "$" ($F), and for binary values it is "%" (%1111).
|
||
</p>
|
||
|
||
<p>
|
||
By default debugger expects all numbers without a prefix to be
|
||
decimals, but the default number base can be changed with the "setopt"
|
||
command, by giving it the desired default number base (bin/dec/hex).
|
||
<em>When using the hexadecimal number base, remember still to prefix
|
||
hexadecimal numbers with '$' if they could be confused with register
|
||
names (a0-7, d0-7)!</em> Otherwise results from expressions and
|
||
conditional breakpoints can be unexpected.
|
||
</p>
|
||
|
||
|
||
<h4>Calculations and immediate evaluation</h4>
|
||
|
||
<p>
|
||
Instead of giving plain values, one can also use (arithmetic)
|
||
expressions. Such expression can contain calculations with values of
|
||
CPU and DSP registers, symbols and Hatari variables, in addition to
|
||
plain numbers.
|
||
</p>
|
||
|
||
<p>
|
||
Most commands evaluate those by default, but for some of them (e.g.
|
||
conditional breakpoints) such expressions need to be indicated by
|
||
surrounding them with single quotes. For example to give a sum
|
||
of A0 and D0 register values to a command, use <em>'a0+d0'</em>.
|
||
</p>
|
||
|
||
<p>
|
||
Within expressions, parenthesis are used to indicate indirect
|
||
addressing, <em>not</em> to change the order of precedence.
|
||
Width of the addressing can be specified after the parenthesis.
|
||
For example to get a long value pointed by stack pointer + 2,
|
||
use <em>'(a7+2).l'</em>.
|
||
</p>
|
||
|
||
<p>
|
||
Values of expressions are always evaluated before being given to a
|
||
command. Besides arithmetic, they can be used also to give symbol /
|
||
address / register / variable values to commands that do not otherwise
|
||
interpret them. If command complains that it did not recognize e.g. a
|
||
register name, just surround it with single quotes and it will be
|
||
"evaluated" before being given to the command.
|
||
</p>
|
||
|
||
<p>
|
||
(To support single quotes within string arguments, orphan single quotes
|
||
are passed as-is, and empty single quote pairs are collapsed to a
|
||
single ' character.)
|
||
</p>
|
||
|
||
<p>
|
||
Virtual V0-V7 "registers" can be used to store intermediate results
|
||
for calculations. For example, to get a sum of "_counter" symbol
|
||
address contents one could use following in suitable breakpoint:
|
||
</p>
|
||
<pre>
|
||
# store counter sum to V0 virtual register
|
||
r v0='(_counter)'
|
||
# store count of how many values are added
|
||
r v1='v1+1'
|
||
</pre>
|
||
<p>And then later on, calculate the average:</p>
|
||
<pre>
|
||
# round the counter sum (add half count to sum)
|
||
r v2='v0 + v1/2'
|
||
# and calculate the rounded average (rounded sum / count)
|
||
e v2/v1
|
||
</pre>
|
||
<p>(Another virtual register was used for rounding here, in case one
|
||
wants to continue summing the "_counter" values with the original
|
||
value.)
|
||
</p>
|
||
|
||
<p>
|
||
With command argument completion (see <a href="#readline">readline</a>),
|
||
result from the last "evaluate" command can be inserted by typing '$'
|
||
and pressing TAB.
|
||
</p>
|
||
|
||
|
||
<h4>Hatari outputs and their controls</h4>
|
||
|
||
<p>When debugging something, there can be so much output that you
|
||
want to store emulator output for more detailed inspection later.</p>
|
||
|
||
<p>Hatari has 5 different kinds of outputs and their controls:</p>
|
||
<ul>
|
||
<li><a href="#Tracing">Tracing</a>, controlled by "--trace" and
|
||
"--trace-file" options</li>
|
||
<li>Logging, controlled by "--log-level" and "--log-file" options</li>
|
||
<li>Debugger CPU+DSP <a href="#Inspecting_emulation_state">disassembly /
|
||
memdump / register</a> commands output, controlled by "logfile" command</li>
|
||
<li>Xconout redirection [1] + Hatari command line help,
|
||
hard-coded to "stdout"</li>
|
||
<li>Rest of debugger (breakpoints etc) + Hatari output,
|
||
hard-coded to "stderr"</li>
|
||
</ul>
|
||
|
||
<p>With 3 first options defaulting to "stderr".</p>
|
||
|
||
<p>To catch all of them, it is better just to redirect
|
||
all "stdout" and "stderr" output from Hatari to a file:</p>
|
||
<pre>
|
||
hatari --parse debugger.ini --trace os_base 2>&1 | tee out.log
|
||
</pre>
|
||
|
||
<p>("2>&1" redirects stderr to stdout for piping, and "tee"
|
||
command both saves and shows the output, so that one can still see
|
||
the saved output in more or less real-time.)</p>
|
||
|
||
<p>[1] E.g. VT52 console redirection to Hatari standard output is enabled
|
||
with "os_base" and "os_all" trace settings and "--conout 2" option.</p>
|
||
|
||
|
||
<h3>Inspecting emulation state</h3>
|
||
|
||
<p>
|
||
In the beginning, probably the most interesting commands are "m" and
|
||
"d" for dumping and disassembling memory regions. To do the same for
|
||
the DSP, use "dm" and "dd" commands.
|
||
</p>
|
||
<pre>
|
||
> help memdump
|
||
'memdump' or 'm' - dump memory
|
||
Usage: m [p|s|u] [b|w|l] [start address-[end address| count]]
|
||
dump memory at address or continue dump from previous address.
|
||
By default memory output is done as bytes, with 'w' or 'l'
|
||
option, it will be done as words/longs instead. With 's' or
|
||
'u' the address is a logical instead of physical ('p') one,
|
||
read through the MMU as supervisor or user data, and '--' marks
|
||
what the MMU can't translate. Without arguments, the dump
|
||
continues from the previous address in the same address space.
|
||
Output amount can be given either as a count or an address range.
|
||
</pre>
|
||
<pre>
|
||
> help disasm
|
||
'disasm' or 'd' - disassemble from PC, or given address
|
||
Usage: d [start address-[end address]]
|
||
If no address is given, this command disassembles from the last
|
||
position or from current PC if no last position is available.
|
||
</pre>
|
||
<pre>
|
||
> disasm pc
|
||
(PC)
|
||
$00aa6e : 2f08 move.l a0,-(sp)
|
||
$00aa70 : 0241 0fff andi.w #$fff,d1
|
||
$00aa74 : 207c 00fe 78c0 movea.l #$fe78c0,a0
|
||
$00aa7a : 2070 1000 movea.l (a0,d1.w),a0
|
||
$00aa7e : 4ed0 jmp (a0)
|
||
</pre>
|
||
|
||
<p>
|
||
Both commands accept in addition to numeric addresses also register
|
||
and symbol names, like in above example. If address is not specified,
|
||
commands continue showing from the next address after the previously
|
||
shown data.
|
||
</p>
|
||
|
||
<p>
|
||
"disasm" command default address will be reset to program counter (PC)
|
||
address every time debugger is re-entered. If history is enabled
|
||
and it includes addresses just before PC, disassembly will instead
|
||
start from a slightly earlier address to give more context.
|
||
</p>
|
||
|
||
<p>
|
||
Use "setopt --disasm help" to see options controlling the disassembly
|
||
output.
|
||
</p>
|
||
|
||
<p>
|
||
Use the "info" command to see state of specific sets of HW
|
||
registers (e.g. "info videl") and Atari OS structures (e.g. "info gemdos").
|
||
</p>
|
||
|
||
<p>
|
||
One can also show contents of arbitrary program structs with the
|
||
"struct" command. Parts of the structure can be skipped (with 's')
|
||
and they can be shown in different number bases:</p>
|
||
<pre>
|
||
> help struct
|
||
'struct' - structured memory output, e.g. for breakpoints
|
||
Usage: struct <name> <address>[name]:<type>[base][:<count>[/<split>]] ...]
|
||
|
||
Show <name>d structure content at given <address>, with each
|
||
[name]:<type>[base][:<count>] arg output on its own line, prefixed
|
||
with offset from struct start address, if [name] is not given.
|
||
Output uses multiple lines when type count <split> is given.
|
||
Supported <type>s are 'b|c|w|l|s' (byte|char|word|long|skip).
|
||
Optional [base] can be 'b|o|d|h' (bin|oct|dec|hex).
|
||
Defaults are hex [base], and [count] of 1.
|
||
</pre>
|
||
|
||
<p>For example:</p>
|
||
<pre>
|
||
> struct "TOS info" 0xe00000 :s:2 version:wh:1 :s:20 os_date:lh os_conf:wb :s:14 :c:4
|
||
TOS info: $e00000
|
||
+ version : 0206
|
||
+ os_date : 03172024
|
||
+ os_conf : 0000000011111111
|
||
+ $2c : ETOS
|
||
</pre>
|
||
|
||
<p>By saving such command to a file, it can be used with
|
||
<a href="#Breakpoint_options">breakpoint</a> ":file" option, to
|
||
show contents of a given structure, whenever that breakpoint
|
||
matches.</p>
|
||
|
||
<p>Prefixing "info" and "struct" commands with "echo \ec" command
|
||
would clear screen before their output. This could help noticing
|
||
changes in real-time output.</p>
|
||
|
||
|
||
<h4>Selecting what information is shown on entering the debugger</h4>
|
||
|
||
<p>
|
||
By using the "lock" command, one can ask Hatari to show specific
|
||
information whenever debugger is entered, e.g. due to a breakpoint.
|
||
For example to see disassembly from current PC address, use "lock
|
||
disasm".
|
||
</p>
|
||
|
||
<p>
|
||
"regaddr" subcommand shows disassembly or memory
|
||
dump of an address pointed by a given register ("lock regaddr disasm
|
||
a0"). Of the DSP registers, only Rx ones are valid for this
|
||
subcommand.
|
||
</p>
|
||
|
||
<p>
|
||
"file" subcommand can be used to get (arbitrary number of) commands
|
||
parsed and executed from a given debugger input file whenever debugger
|
||
is entered. With this one can output any needed information:
|
||
</p>
|
||
<pre>
|
||
lock file debugger.ini
|
||
</pre>
|
||
|
||
<p>
|
||
To disable showing of this extra information, use "lock default".
|
||
Without arguments "lock" command will show the available options
|
||
(like the "info" command does).
|
||
</p>
|
||
|
||
|
||
<h3>Debug symbols</h3>
|
||
|
||
<p>
|
||
By default, Hatari loads debug symbols for programs executed from
|
||
GEMDOS HD whenever debugger is first invoked after that, if program
|
||
includes debug symbols, or there's a separate *.sym file for them.
|
||
</p>
|
||
<p>
|
||
Symbolic names for program function and data addresses can be used in
|
||
arithmetic expressions and conditional breakpoint expressions. They
|
||
show up in the "disasm" command output, and one can trace calls to
|
||
them with "trace cpu_symbols" (and DSP symbols with "trace
|
||
dsp_symbols").
|
||
</p>
|
||
<p>
|
||
To load debugging symbols in other situations, use the debugger
|
||
"symbols" command (and "dspsymbols" for DSP).
|
||
</p>
|
||
|
||
|
||
<h4>Demangling (C++) symbols</h4>
|
||
|
||
<p>C++ (and other similar languages) store symbols in binaries
|
||
in a "mangled" format used by tools like linkers, and symbol names
|
||
need to be "demangled" (expanded) to a more readable / user-friendly
|
||
format. This can be done using a "c++filt" tool coming with the
|
||
compiler "binutils" package (if host version does not produce corrent
|
||
symbols, use "m68k-atari-mint-c++filt" from "binutils-m68k-atari-mint"
|
||
package instead).</p>
|
||
|
||
<p>ScummVM example:</p>
|
||
<pre>
|
||
$ gst2ascii scummvm.ttp | m68k-atari-mint-c++filt > scummvm.sym
|
||
</pre>
|
||
<p>(When symbols are in a file named as "<prgname>.sym", Hatari
|
||
debugger will load symbols from it, not from the program file.)
|
||
|
||
<p>Note: most demangled C++ symbols, such as method signatures, contain
|
||
special characters which prevent them from being used as arguments to
|
||
breakpoints and other debugger commands. To solve that, debugger
|
||
commands can accept also a part of a C++ method symbol name, if it
|
||
is a complete sub-part of that name, and it matches only a single
|
||
symbol.
|
||
</p>
|
||
|
||
<p>For example, given the following C++ symbols:</p>
|
||
<pre>
|
||
- void ScummEngine::startScriptQuick()
|
||
- int ScummEngine::startScriptQuick2(int, int)
|
||
- int ScummEngine::startObject(int, char*)
|
||
</pre>
|
||
<p>Following partial names would be accepted:</p>
|
||
<pre>
|
||
- startScriptQuick
|
||
- ScummEngine::startObject
|
||
</pre>
|
||
<p>But these would not:</p>
|
||
<pre>
|
||
- ScummEngine (multiple matches)
|
||
- startObj (incomplete symbol part match)
|
||
</pre>
|
||
|
||
<p>This leaves out destructor, template and operator overloading
|
||
method names because those contain characters already reserved for
|
||
breakpoint and debugger expression operations (and without those
|
||
characters, partial name would produce multiple matches).</p>
|
||
|
||
<p>For rest of the demangled symbols, one needs to use symbol addresses
|
||
instead. Addresses for the loaded symbols (that match user specified
|
||
substring), can be listed like this:</p>
|
||
<pre>
|
||
symbols name XMLParser::parse
|
||
</pre>
|
||
|
||
|
||
<h4>Symbols for a program under GEMDOS HD emulation</h4>
|
||
|
||
<p>
|
||
If currently running program contains debug symbol table,
|
||
and it is started from GEMDOS HD emulated drive, its symbol names /
|
||
addresses are automatically loaded when debugger is invoked, and
|
||
removed when that program terminates.</p>
|
||
|
||
<p>
|
||
Above happens only if there are no symbols loaded when the program
|
||
starts. If there are, one can load program symbol data manually with
|
||
the following command, after program has been loaded to the memory by
|
||
TOS (see <a href="#Breakpoint_variables">setting breakpoint at program
|
||
startup</a>):
|
||
</p>
|
||
<pre>
|
||
symbols prg
|
||
</pre>
|
||
|
||
<p>
|
||
The options needed to add a suitable symbol table to the program,
|
||
depend on which toolchain is used to build it:
|
||
</p>
|
||
<dl>
|
||
<dt><em>Devpac</em>:</dt>
|
||
<dd>"OPT D+,X+"</dd>
|
||
<dt><em>AHCC</em>:</dt>
|
||
<dd>"-g" and "-l" options for linking</dd>
|
||
<dt><em>GCC</em>:</dt>
|
||
<dd>"-g" for compilation, and no strip option for linking</dd>
|
||
<dt><em>VBCC</em>:</dt>
|
||
<dd>"-g" (can only be used at linking phase), <em>when VBCC
|
||
configuration file uses "-bataritos" option for
|
||
the linker</em></dd>
|
||
</dl>
|
||
|
||
<p>Generated symbols can be viewed (and converted to debugger
|
||
ASCII format) with a tool installed with Hatari:</p>
|
||
<pre>
|
||
$ gst2ascii program.tos > program.sym
|
||
</pre>
|
||
<p>(By default "gst2ascii" filters out same symbols as Hatari debugger does.)</p>
|
||
|
||
<p>For C++ programs, pipe the that output through
|
||
<em>m68k-atari-mint-c++filt</em> demangler (see above)
|
||
before directing it to a file.</p>
|
||
|
||
|
||
<h5>Overriding program symbols</h5>
|
||
|
||
<p>Symbols in a program can be overridden by providing similarly
|
||
named ".sym" file in the same directory. For example, if there is
|
||
a file called "program.sym", debugger will try to load symbols from
|
||
that instead of "program.prg" (when debugger is first invoked after
|
||
given program started).</p>
|
||
|
||
<p>There are few reasons why one might want to use ".sym" file:</p><ul>
|
||
<li>Debugging C++ programs with demangled (user-friendly) symbol names</li>
|
||
<li>Binary itself can be stripped (be much smaller) when symbols are
|
||
provided in a separate ".sym" file / download</li>
|
||
<li>Addding missing (function) symbols, or removing (loop) symbols,
|
||
to improve profiling results</li>
|
||
</ul>
|
||
|
||
|
||
<h4>For a program on a (disk) image</h4>
|
||
|
||
<p>
|
||
If the program is not run from a GEMDOS HD emulated drive, but from a
|
||
cartridge, floppy or HD image, corresponding program is needed also as
|
||
a normal host file which location can be given to the debugger:
|
||
</p>
|
||
<pre>
|
||
symbols /path/to/the/program.tos
|
||
</pre>
|
||
|
||
|
||
<h4>ASCII debug symbol files</h4>
|
||
|
||
<p>
|
||
If Hatari complains that program does not have debug symbol table,
|
||
or its symbols are in some unsupported format, there are two options:
|
||
</p>
|
||
<ul>
|
||
<li>Convert the symbols to ASCII format understood by the Hatari debugger.
|
||
Writing converters for other ASCII formats is easy, and Hatari already
|
||
contains converters for DSP LOD files, <span class="commandline">nm</span>
|
||
output, and a few other formats.</li>
|
||
<li>Create the ASCII symbols file by hand while debugging a program.</li>
|
||
</ul>
|
||
|
||
<p><b>NOTE:</b> <span class="commandline">nm</span> output for GCC generated
|
||
<span class="commandline">a.out</span> binaries includes labels also for loops,
|
||
not just functions. While loop labels are fine for debugging, <em>they should
|
||
be removed before profiling</em>. Besides causing misleading profile
|
||
results, loop labels can <em>seriously</em> slow down profiling
|
||
(call graph tracking is automatically enabled for profiling when debug
|
||
symbols are loaded, and operations done on each matched symbol address
|
||
cause huge overhead if that match is for something happening every few
|
||
instructions).
|
||
</p>
|
||
|
||
<p>ASCII symbols file format is following:</p>
|
||
<pre>
|
||
e01034 T random
|
||
e01076 T kbdvbase
|
||
e0107e T supexec
|
||
</pre>
|
||
<p>
|
||
Where 'T' means text (code), 'D' means data and 'B' means BSS section
|
||
type of address. The hexadecimal address, address type letter and the
|
||
symbol name are separated by white space. Empty lines and lines
|
||
starting with '#' (comments) are ignored.
|
||
</p>
|
||
|
||
<p>
|
||
Debugger will automatically "relocate" the symbol addresses when it
|
||
loads them from a program binary, but with ASCII symbol files
|
||
relocation offset needs to be specified separately, unless the
|
||
symbol names are for fixed addresses (like is the case e.g. with
|
||
EmuTOS):
|
||
</p>
|
||
<pre>
|
||
symbols program.sym TEXT
|
||
</pre>
|
||
<p>
|
||
If there are symbols for DATA and BSS sections, they are also assumed
|
||
to have TEXT address offsets. This is the case both with "gst2ascii"
|
||
tool, and "nm" output for (at least GCC generated) binaries.
|
||
</p>
|
||
|
||
<p>
|
||
(TEXT, DATA and BSS are virtual debugger variables which values come
|
||
from the currently loaded program's basepage. They're set after the
|
||
program is loaded by TOS, see "info basepage" output.)
|
||
</p>
|
||
|
||
|
||
<h4>Debugging resident programs</h4>
|
||
|
||
<p>When debugging resident (TSR) programs (terminated with a Ptermres()
|
||
GEMDOS call), it's common to have a 'trigger' program that invokes
|
||
some functionality in the TSR being debugged. With symbol autoloading
|
||
enabled, executing the 'trigger' program from Hatari GEMDOS HD could
|
||
replace symbols for the TSR with ones from the 'trigger' program.</p>
|
||
|
||
<p>Symbol replacement can be avoided in two ways:</p>
|
||
<ul>
|
||
<li>Using "symbols autoload off" debugger command, or</li>
|
||
<li>Loading TSR symbols from an ASCII file (see above). This is the way
|
||
to go if TSR is not a normal Atari program, or some symbols need to
|
||
be added or filtered out (e.g. for profiling)</li>
|
||
</ul>
|
||
|
||
|
||
<h3>Backtraces</h3>
|
||
|
||
<p>If profiling was enabled after program symbols were loaded,
|
||
debugger will show a program backtrace whenever debugger is invoked,
|
||
<em>for the code executed since debugger was last invoked</em>.</p>
|
||
|
||
<p>Loading of program symbols and enabling profiling <em>at program
|
||
start</em> (so that backtrace covers whole program execution),
|
||
can be automated like this:</p>
|
||
<pre>
|
||
$ echo "profile on" > profile.ini
|
||
$ hatari --symload exec --parse prg:profile.ini ... some.prg
|
||
</pre>
|
||
|
||
<p>Note: crashes are best debugged with EmuTOS, because it does not
|
||
immediately terminate program on a crash (like Atari TOS does),
|
||
but shows a panic message and waits for a key before termination.
|
||
Invoking debugger at that point, can then show (profiler) backtrace
|
||
for the program crash.</p>
|
||
|
||
<p>(EmuTOS 1024k ROM releases include symbols file and Hatari loads
|
||
that automatically if it is present beside the EmuTOS ROM. This
|
||
provides EmuTOS backtrace when program symbols are missing.)</p>
|
||
|
||
<p>If <em>program (and libraries it links) are compiled with
|
||
frame-pointers</em> (GCC <code>-fno-omit-frame-pointer</code> option),
|
||
'bt' command can be used to get frame-pointer based backtrace instead.
|
||
However, while 'bt' command can provide backtrace also in cases where
|
||
profiling was not enabled and/or symbols are loaded only when invoking
|
||
the debugger, its frame-pointer based backtrace often is less accurate
|
||
than the profiler provided backtraces.</p>
|
||
|
||
|
||
<h3>Breakpoints</h3>
|
||
|
||
<p>
|
||
There are two ways to specify breakpoints for Hatari. First, there are
|
||
the simple address breakpoints which trigger when the CPU (or DSP)
|
||
program counter hits a given address. Use "a" (or "da" for the DSP)
|
||
to create them, for example:
|
||
</p>
|
||
<pre>
|
||
a $e01034
|
||
a some_symbol
|
||
</pre>
|
||
|
||
<p>
|
||
Note that address breakpoints are just wrappers for conditional
|
||
breakpoints so "b" command is needed to list or remove them.
|
||
</p>
|
||
|
||
<p>
|
||
Then there are the conditional breakpoints which can handle much more
|
||
complex break condition expressions; they can track changes to
|
||
register and memory values with bitmasks, include multiple conditions
|
||
for triggering a breakpoint and so on. Use "b" (or "db" for the DSP)
|
||
to manage them.
|
||
</p>
|
||
|
||
<p>Help explains the general syntax:</p>
|
||
<pre>
|
||
> help b
|
||
'breakpoint' or 'b' - set/remove/list conditional CPU breakpoints
|
||
Usage: b <condition> [&& <condition> ...] [:<option>] | <index> | help | all
|
||
|
||
Set breakpoint with given <conditions>, remove breakpoint with
|
||
given <index>, remove all breakpoints with 'all' or output
|
||
breakpoint condition syntax with 'help'. Without arguments,
|
||
lists currently active breakpoints.
|
||
</pre>
|
||
|
||
<p>
|
||
Unless "breakpoint" command is given one of the pre-defined
|
||
subcommands ("all", "help"), an index for a breakpoint to remove, or
|
||
no arguments (to list breakpoints), its arguments are interpreted as a
|
||
new breakpoint definition.
|
||
</p>
|
||
|
||
<p>
|
||
Each conditional breakpoint can have (currently up to 4) conditions
|
||
which are separated by "&&". All of the breakpoint's
|
||
conditions need to be true for a breakpoint to trigger.
|
||
</p>
|
||
|
||
|
||
<h4 id="Breakpoint_options">Breakpoint options</h4>
|
||
|
||
<p>
|
||
Normally when a breakpoint is triggered, emulation is stopped and
|
||
debugger invoked. Breakpoint options can be used to affect what
|
||
happens when a breakpoint is triggered. These options are given after
|
||
the conditions, and are prefixed with a (space and) ':' character.
|
||
</p>
|
||
|
||
<dl>
|
||
<dt><em><count></em></dt>
|
||
<dd>Break only on every <count> hit. For example, to stop
|
||
on every other time PC is at given address, use:
|
||
<pre>
|
||
a $1234 :2
|
||
</pre>
|
||
</dd>
|
||
|
||
<dt><em>file <file></em></dt>
|
||
<dd>
|
||
Execute debugger commands from given <file> when this breakpoint
|
||
is hit. This provides complete control over what information is
|
||
show when the debugger is hit, one can even chain breakpoints (as
|
||
explained in
|
||
<a href="#Chaining_breakpoints">Chaining breakpoints</a> later on).
|
||
This should be used when "info" and "lock" options are not enough.
|
||
</dd>
|
||
|
||
<dt><em>info <name></em></dt>
|
||
<dd>
|
||
Show on breakpoint hits the same information as "info" command would
|
||
show (see <a href="#Inspecting_emulation_state">Inspecting emulation
|
||
state</a> above). With "lock" option and command there is more control
|
||
over what information is shown, whereas with "info" option, every
|
||
breakpoint can show different information, and one does not need to
|
||
change what is shown on entering the debugger. This option also
|
||
enables trace option.
|
||
</dd>
|
||
|
||
<dt><em>lock</em></dt>
|
||
<dd>
|
||
Show the same information on breakpoint hit as what is shown when entering
|
||
the debugger (see the "lock" command in
|
||
<a href="#Inspecting_emulation_state">Inspecting emulation state</a>
|
||
above). This enables also trace option as one would anyway see this
|
||
information when entering debugger.
|
||
</dd>
|
||
|
||
<dt><em>noinit</em></dt>
|
||
<dd>
|
||
Avoid debugger initialization (profiling data reset and disassembly
|
||
address being set to current PC) on breakpoint hit. This enables trace
|
||
option as entering debugger would anyway re-initialize debugger state.
|
||
This option is mainly intended for breakpoints that use either
|
||
":file" or ":lock" option to show backtraces with "profile stack"
|
||
command during <a href="#Profiling">profiling</a>. See
|
||
<a href="#Usage_examples">Usage examples</a> section for an example.
|
||
</dd>
|
||
|
||
<dt><em>once</em></dt>
|
||
<dd>
|
||
Delete the breakpoint when it is hit, i.e. trigger it only once.
|
||
Useful when just wanting to reach a specific address. Or when on an
|
||
instruction that jumps back to a start of the loop, and one wants to
|
||
finish the loop, following could use:
|
||
<pre>
|
||
b pc > "pc" :once
|
||
continue
|
||
</pre>
|
||
</dd>
|
||
|
||
<dt><em>print <string></em></dt>
|
||
<dd>
|
||
Print given string when breakpoint is hit. When many breakpoints can
|
||
hit in succession, this helps in differentiating between them.
|
||
</dd>
|
||
|
||
<dt><em>quiet</em></dt>
|
||
<dd>
|
||
Inhibit showing of extra information when breakpoint is either set or
|
||
hit i.e. show only the information that breakpoint itself outputs.
|
||
</dd>
|
||
|
||
<dt><em>trace</em></dt>
|
||
<dd>
|
||
Continue emulation without stopping after printing the value that
|
||
triggered the breakpoint and doing other possible option actions.
|
||
This is most useful when investigating memory or register value
|
||
changes (explained below).
|
||
</dd>
|
||
</dl>
|
||
|
||
<p>
|
||
Arguments to "file", "info" and "print" options are terminated by the
|
||
next option (to ':' character), or end of input.
|
||
</p>
|
||
<p>
|
||
Note: while multiple options can be given for conditional breakpoints,
|
||
address breakpoints accept only <em>one</em> (with no argument).
|
||
</p>
|
||
|
||
|
||
<h4>Breakpoint conditions</h4>
|
||
|
||
<p>
|
||
"b help" explains very briefly the breakpoint condition syntax:
|
||
</p>
|
||
<pre>
|
||
> b help
|
||
condition = <value>[.mode] [& <mask>] <comparison> <value>[.mode]
|
||
|
||
where:
|
||
value = [(] <register/symbol/variable name | number> [)]
|
||
number/mask = [#|$|%]<digits>
|
||
comparison = '<' | '>' | '=' | '!'
|
||
addressing mode (width) = 'b' | 'w' | 'l'
|
||
addressing mode (space) = 'p' | 'x' | 'y'
|
||
</pre>
|
||
|
||
<p>
|
||
For CPU breakpoints, mode is the address width; it can be byte ("b"),
|
||
word ("w") or long ("l", default). For DSP breakpoints, mode specifies
|
||
the address space: "P", "X" or "Y". Note that on DSP only R0-R7
|
||
registers can be used for memory addressing. For example;
|
||
<pre>
|
||
db (r0).x = 1 && (r0).y = 2
|
||
</pre>
|
||
|
||
<p>
|
||
If the value is in parenthesis like in "($ff820)" or "(a0)", then the
|
||
used value will be read from the memory address pointed by it. Note
|
||
that this conditional breakpoint expression value is checked at
|
||
run-time whereas evaluated arithmetic expressions (mentioned in
|
||
<a href="#Entering_arguments">Entering arguments to debugger commands</a>
|
||
above) are evaluated already when
|
||
adding a breakpoint. For example, to break when a value in an address
|
||
(later) pointed by A0 matches the value <em>currently</em> in D0, one
|
||
would use:
|
||
</p>
|
||
<pre>
|
||
b (a0) = 'd0'
|
||
</pre>
|
||
|
||
<p>
|
||
When interested only on certain bits in the value, one can use
|
||
'&' and a numeric mask on either side of comparison operator to
|
||
mask the corresponding value, like this:
|
||
<pre>
|
||
b ($ff820).w & 3 = (a0) && (a1) = d0 & %1100
|
||
</pre>
|
||
|
||
<p>
|
||
Comparison operators should be familiar and obvious, except for '!'
|
||
which indicates inequality ("is not") comparison. For example:
|
||
</p>
|
||
<pre>
|
||
b d0 > $20 && d0 < $40 && d0 ! $30
|
||
</pre>
|
||
|
||
|
||
<h5>Tracking breakpoint conditions</h5>
|
||
|
||
<p>
|
||
As a convenience, if the both sides of the comparison are exactly the
|
||
same (i.e. condition is redundant as it is always either true or
|
||
false), the <em>right side</em> of the comparison is replaced with
|
||
its current value. With that, one can give something like this:
|
||
</p>
|
||
<pre>
|
||
b pc > "pc"
|
||
</pre>
|
||
<p>As:</p>
|
||
<pre>
|
||
b pc > pc
|
||
</pre>
|
||
|
||
<p>
|
||
That in itself is not so useful, but for inequality ('!') comparison,
|
||
conditional breakpoint will additionally track and output all further
|
||
changes for the given address/register expression. This can be used
|
||
for example to find out all value changes in a given memory address,
|
||
like this:
|
||
</p>
|
||
<pre>
|
||
b ($ffff9202).w ! ($ffff9202).w :trace
|
||
</pre>
|
||
<p>
|
||
Because tracking breakpoint conditions will print the evaluated
|
||
value when it changes, they're typically used with the trace option
|
||
to track changes e.g. to some I/O register.
|
||
</p>
|
||
|
||
|
||
<h5>Breakpoint condition notes</h5>
|
||
|
||
<ul>
|
||
<li>
|
||
Any '!' condition should be given as the first condition. Because
|
||
breakpoint evaluation is stopped ("short-circuited") when any of the
|
||
conditions fails, the tracked value would not be updated correctly
|
||
unless tracking condition is given as the first one.
|
||
</li>
|
||
|
||
<li>
|
||
Hatari will internally update some register values without immediately
|
||
updating the corresponding I/O address range memory addresses. For
|
||
example the Busy bit for the internal Blitter control register is
|
||
(internally) cleared when Blitter activity stops, but the actual I/O
|
||
address for that control register gets updated only when something
|
||
actually writes or reads that I/O address. Many HW registers behave
|
||
like this (status registers in FDC, ACIA, MFP, Blitter...).
|
||
<br>
|
||
For breakpoints that track just a single I/O register memory address, or
|
||
multiple ones of which <strong>none</strong> are modified by Hatari,
|
||
only by emulated code, this is not a problem, they get triggered as
|
||
expected.
|
||
<br>
|
||
However, a breakpoint that tracks multiple I/O registers where some of
|
||
them are updated by Hatari (for example one checking that other Blitter
|
||
registers are not updated while control register indicates Blitter to
|
||
be active/busy), will not work as expected!
|
||
</li>
|
||
</ul>
|
||
|
||
|
||
<h4>Breakpoint variables</h4>
|
||
|
||
<p>
|
||
In addition to loaded symbols, the debugger supports also setting
|
||
conditional breakpoints on values of some "virtual" variables listed
|
||
by "variables" (v) command. For example:
|
||
</p>
|
||
<ul>
|
||
<li>Aes/Bios/Gemdos/LineA/LineF/Vdi/XbiosOpcode variables can be used
|
||
to catch AES, BIOS, GEMDOS, Line-A, Line-F, VDI and XBIOS OS-calls.
|
||
By default they contain the 0xffff value, so to trace e.g. all AES
|
||
calls (instead of a specific one) one needs to use something like this:
|
||
<pre>
|
||
b AesOpcode ! AesOpcode && AesOpcode < 0xffff :trace
|
||
</pre>
|
||
</li>
|
||
<li>To stop when TOS starts loading next program, set a breakpoint for
|
||
Pexec(0,...) OS call:
|
||
<pre>
|
||
b GemdosOpcode = 0x4B && OsCallParam = 0x0
|
||
</pre>
|
||
</li>
|
||
<li>To stop emulation after program has been loaded, but before
|
||
it runs, set next breakpoint on its first instruction i.e. when
|
||
program counter matches the TEXT (code) segment address (taken
|
||
from program basepage):
|
||
<pre>
|
||
b pc = TEXT :once
|
||
</pre>
|
||
Note1: Set this breakpoint only after boot reaches desktop, and
|
||
trigger it only once. Otherwise during (re)boot there would be a
|
||
warning for every instruction (until TOS sets a valid basepage).
|
||
<br />
|
||
Note2: <em>address</em> breakpoint cannot be used for this because
|
||
Hatari variable values are evaluated at run-time only for conditional
|
||
breakpoints.
|
||
</li>
|
||
<li>To view current program DATA and BSS segment contents,
|
||
use the corresponding variables:
|
||
<pre>
|
||
m DATA
|
||
m BSS
|
||
</pre>
|
||
</li>
|
||
<li>To stop at a specific cycle within a frame (that is,
|
||
PC relative to the current VBL/HBL in cycles), set breakpoints to
|
||
specific "VBL", "FrameCycles", "HBL" and "LineCycles" variable
|
||
values. For example, to break after 20 HBLs, use:
|
||
<pre>
|
||
b HBL = 'HBL+20'
|
||
</pre>
|
||
</li>
|
||
<li>To stop on every symbol, break on:
|
||
<pre>
|
||
b PConSymbol = 1
|
||
</pre>
|
||
</li>
|
||
</ul>
|
||
|
||
<p>
|
||
Hint: "info" command "aes", "bios", "gemdos", "vdi" and "xbios"
|
||
subcommands can be used to list the corresponding OS-call opcodes.
|
||
For example, to see the GEMDOS opcodes, use:</p>
|
||
<pre>
|
||
info gemdos 1
|
||
</pre>
|
||
|
||
|
||
<h4 id="Chaining_breakpoints">Chaining breakpoints and other actions</h4>
|
||
|
||
<p>
|
||
As the file pointed by the breakpoint ":file" option (see
|
||
<a href="#Breakpoint_options">Breakpoint options</a>) can contain any
|
||
debugger commands, it can also be used to do automatic "chaining" of
|
||
debugger and breakpoint actions so that after one breakpoint is hit,
|
||
another one is set.
|
||
</p>
|
||
|
||
<p>For example, with these input files:</p>
|
||
<ul>
|
||
<li>"pexec.ini":
|
||
<pre>
|
||
# continue to "program.ini" on Pexec(0, ....)
|
||
b GemdosOpcode = 0x4B && OsCallParam = 0x0 :trace :once :file program.ini
|
||
</pre>
|
||
</li>
|
||
<li>"program.ini":
|
||
<pre>
|
||
# continue to "trace.ini" when program execution starts
|
||
b pc = TEXT :trace :once :file trace.ini
|
||
</pre>
|
||
</li>
|
||
<li>"trace.ini":
|
||
<pre>
|
||
# load symbols, trace gemdos & program function calls
|
||
symbols prg
|
||
trace gemdos,cpu_symbols
|
||
# continue to "disable.ini" after 4 VBLs
|
||
b VBL = 'VBL+4' :trace :once :file disable.ini
|
||
</pre>
|
||
</li>
|
||
<li>"disable.ini":
|
||
<pre>
|
||
# stop tracing and remove breakpoints
|
||
trace none
|
||
b all
|
||
</pre>
|
||
</li>
|
||
</ul>
|
||
|
||
<p>
|
||
And then start Hatari with the first debugger input file:
|
||
</p>
|
||
<pre>
|
||
hatari --parse pexec.ini /path/to/your/program.tos
|
||
</pre>
|
||
|
||
<ol>
|
||
<li>"pexec.ini" sets a breakpoint to parse debugger commands from
|
||
"program.ini" when TOS starts loading the given program
|
||
(it is first Pexec(0) after boot)</li>
|
||
<li>"program.ini" sets a breakpoint to parse debugger commands from
|
||
"trace.ini" when program code begins executing. These two steps
|
||
are needed because TEXT variable is not valid until TOS has booted</li>
|
||
<li>"trace.ini" input file loads symbols for the run program, sets Hatari
|
||
to trace several things (see <a href="#Tracing">Tracing</a> section
|
||
below) in the emulated system for few VBLs until breakpoint runs
|
||
commands from the "disable.ini" file</li>
|
||
<li>"disable.ini" input file will disable tracing and remove
|
||
all (remaining) breakpoints</li>
|
||
</ol>
|
||
|
||
<p><em>Note:</em></p>
|
||
<ul>
|
||
<li>Because debugger input files cannot "continue"
|
||
emulation, ":trace" option needs to be used for the breakpoint(s)
|
||
when emulation should continue after the breakpoint action(s).</li>
|
||
<li>In simpler breakpoint chain (like above), where new breakpoint
|
||
just replaces the previous one, ":once" option tells debugger that
|
||
breakpoint is not needed after it is hit.
|
||
</li>
|
||
</ul>
|
||
|
||
<p>
|
||
Hint: It is better to test each input file separately before testing
|
||
the whole chain. Besides the ":file" breakpoint option, one can test
|
||
these debugger input files also with the debugger "file" command,
|
||
"file" option for the "lock" command, and with the Hatari "--parse"
|
||
command line option.
|
||
</p>
|
||
|
||
|
||
<h4 id="State_saves">Breakpoints in emulation state saves</h4>
|
||
|
||
<p>
|
||
When emulation state is saved, currently active breakpoints are saved
|
||
to a separate file with additional ".debug" extension. If breakpoints
|
||
refer to other files, that file starts with a "cd" command that sets
|
||
Hatari working directory to what it was when state was saved.
|
||
</p>
|
||
|
||
<p>
|
||
This way the relative paths referred by breakpoint ":file" options
|
||
(and possible further files referred from those files), should work
|
||
fine. However, if any of the related files are moved, rest of the them
|
||
need to be moved too, and the "cd" command path in the ".debug" state
|
||
save file updated accordingly.
|
||
</p>
|
||
|
||
|
||
<h3 id="Stepping">Stepping through code</h3>
|
||
|
||
<p>
|
||
After analyzing the emulation state and/or setting new breakpoints,
|
||
emulation can continued with the "c" command. One can continue
|
||
for a given number of CPU instructions (or DSP instructions when "dc"
|
||
is used), or continue forever (until a non-tracing breakpoint
|
||
triggers) if the nstruction count is omitted.
|
||
</p>
|
||
|
||
<p>
|
||
To continue just to the next instruction, use "s" (step) command to
|
||
execute just one instruction, or "n" (next), to skip subroutine +
|
||
exception calls and DBCC branching backwards (i.e. loops). "ds" and
|
||
"dn" commands do the same for DSP (except that "dn" does not skip
|
||
loops).
|
||
</p>
|
||
|
||
<p>
|
||
One can also continue with the "n" until instruction of certain
|
||
type is encountered, by giving it the instruction type:
|
||
<ul>
|
||
<li>"branch" matches branch instructions:<br>
|
||
CPU: BCC, BRA, DBCC, JMP<br>
|
||
DSP: DO/ENDO JCC, JCLR, JMP, JSET, REP</li>
|
||
<li>"subcall" matches subroutine calls:<br>
|
||
CPU: BSR, JSR<br>
|
||
DSP: JSCC, JSCLR, JSSET, JSR</li>
|
||
<li>"subreturn" matches return from subroutine:<br>
|
||
CPU: RTD, RTR, RTS<br>
|
||
DSP: RTS</li>
|
||
<li>"exception" matches exceptions:<br>
|
||
CPU: BKPT, ILLG, STOP, TRAP, TRAPV</li>
|
||
<li>"exreturn" matches return from exception:<br>
|
||
CPU: RTE<br>
|
||
DSP: RTI</li>
|
||
<li>"return" matches both subroutine and exception returns</li>
|
||
</ul>
|
||
|
||
<p>
|
||
"subreturn" differs from others by running until current subroutine
|
||
ends, even if other subroutines are called before that. This is
|
||
particularly useful for profiling more complex functions; set
|
||
breakpoint on function start, enable profiling and run until that
|
||
functions returns, to get its full profile. Example: "n subreturn",
|
||
or "dn subreturn".
|
||
</p>
|
||
|
||
<p>Notes:</p>
|
||
<ul>
|
||
<li>"next subreturn" works only for real subroutines i.e. code called
|
||
with BSR/JSR and returning with RT[DRS]. In (GCC) optimized compiled
|
||
C-code, calls to functions in same C / object file may get inlined
|
||
or just called with JMP. If that is the case, and making the
|
||
function non-static does not help, move it to another C-file</li>
|
||
<li>"exreturn" and "return" run only until first instruction of given
|
||
type is encountered. They cannot implement similar functionality
|
||
as "subreturn", because it is not possible for "next" command to
|
||
track CPU / DSP exception invocations, and therefore it cannot
|
||
do call depth tracking required for this either</li>
|
||
<li>Tracking CPU CHK2 and FPU FBCC, FDBCC & FTRAPCC exception /
|
||
branch instructions is not supported currently</li>
|
||
</ul>
|
||
|
||
|
||
<h3>Tracing</h3>
|
||
|
||
<p>
|
||
(Hatari needs to be built with ENABLE_TRACING define set for
|
||
tracing to work. By default it is.)
|
||
</p>
|
||
|
||
<p>
|
||
For example, to continue with real-time disassembling, that can be
|
||
enabled with "trace cpu_disasm" (or "trace dsp_disasm" for DSP) at
|
||
the debugger prompt, before continuing.
|
||
</p>
|
||
<p>
|
||
Disable tracing with "trace none" when entering the debugger again.
|
||
"trace help" (or TAB) can be used to list all the (about 70) supported
|
||
traceable things, from HW events to OS functions.
|
||
</p>
|
||
<p>
|
||
At run-time trace flags can be enabled and disabled individually by
|
||
starting the trace flags with -/+, like this:
|
||
</p>
|
||
<pre>
|
||
trace gemdos,aes,vdi # trace just these
|
||
trace +xbios,bios # trace additionally these
|
||
trace -aes,-vdi # remove tracing of these
|
||
</pre>
|
||
<p>
|
||
('+' is optional for addition except at start of the trace flags list.)
|
||
</p>
|
||
<p>
|
||
Notes:
|
||
</p>
|
||
<ul>
|
||
<li>
|
||
If GEMDOS HD emulation is not enabled, GEMDOS call tracing needs to be
|
||
enabled at Hatari command line, it is not possible to enable it after
|
||
TOS has initialized GEMDOS.
|
||
</li>
|
||
<li>
|
||
AES, BIOS, GEMDOS and XBIOS traces show arguments for (most of) the
|
||
calls, VDI trace shows only function calls (parsing the arguments
|
||
would be too complicated).
|
||
</li>
|
||
<li>
|
||
Tracing options can be set even from a program within the emulation,
|
||
by enabling the (deprecated) "--bios-intercept" option and calling
|
||
XBios 255 from the program with a suitable trace options string.
|
||
</li>
|
||
<li>
|
||
Note that the trace output file can be set only when Hatari starts,
|
||
it cannot be changed from within the debugger (or emulation).
|
||
</li>
|
||
</ul>
|
||
<p>
|
||
If there is not a trace option for something one would like to track,
|
||
it should be possible do that with tracing breakpoints, as explained above.
|
||
For example, following tracks Line-A calls:
|
||
</p>
|
||
<pre>
|
||
b LineAOpcode ! LineAOpcode && LineAOpcode < 0xffff :trace
|
||
</pre>
|
||
|
||
|
||
<h3>Profiling</h3>
|
||
|
||
<p>Hatari supports both Atari programs profiling themselves, and
|
||
its debugger includes powerful profiling functionality, especially
|
||
when its use is automated with <a href="#Breakpoints">conditional
|
||
breakpoints</a>.</p>
|
||
|
||
<p>
|
||
Profiling tells where the running code spends most of its (emulated)
|
||
time. It can be used to find out where a program is (apparently)
|
||
stuck, or what are the largest performance bottlenecks for a program.
|
||
</p>
|
||
|
||
<h4>NF_CYCLES NatFeat for Atari programs</h4>
|
||
|
||
<p>Motorola CPUs do not have CPU cycle counter instruction, but Atari
|
||
programs can query (lower 32-bits of) Hatari's internal cycles counter
|
||
value. Returned counter value differences tell how much time (cycles)
|
||
elapsed between successive queries.</p>
|
||
|
||
<p>NF_CYCLES "Native Feature" can be enabled with the "--natfeats on"
|
||
option. Examples of using Hatari Native Features are included to
|
||
Hatari "tests/natfeats" directory.</p>
|
||
|
||
<p>Note: 32-bit cycle counter will wrap in few minutes even on
|
||
8-32Mhz CPUs, so caller needs to handle counter wrapping.</p>
|
||
|
||
|
||
<h4>Collecting profile data in the debugger</h4>
|
||
|
||
<p>
|
||
Profiling will start (anew) each time emulation is continued ('c'),
|
||
after it has been explicitly enabled (use "dp" for DSP):
|
||
</p>
|
||
<pre>
|
||
> profile on
|
||
Profiling enabled.
|
||
> c
|
||
Returning to emulation...
|
||
</pre>
|
||
|
||
<p>
|
||
At next debugger invocation, the collected profiling information is
|
||
processed. If program symbols were available for profiler, it will
|
||
show a program backtrace covering the profiled part.
|
||
</p>
|
||
|
||
<p>If there are no symbols, profiling statistics will be shown instead:</p>
|
||
<pre>
|
||
ROM TOS (0xE00000-0xE80000):
|
||
- active address range:
|
||
0xe00030-0xe611a4
|
||
- active instruction addresses:
|
||
14240 (100.00% of all)
|
||
- executed instructions:
|
||
4589668 (100.00% of all)
|
||
- used cycles:
|
||
56898472 (100.00% of all)
|
||
= 7.09347s
|
||
Cartridge ROM (0xFA0000-0xFC0000):
|
||
- no activity
|
||
|
||
= 7.09347s
|
||
</pre>
|
||
<p>
|
||
(DSP RAM will be shown only as single area in profile information.)
|
||
</p>
|
||
|
||
|
||
<h4>Investigating the profile data</h4>
|
||
|
||
<p>
|
||
When back in the debugger, the collected profile data can be inspected:
|
||
</p>
|
||
<pre>
|
||
> h profile
|
||
'profile' - profile CPU code
|
||
Usage: profile <subcommand> [parameter]
|
||
|
||
Subcommands:
|
||
- on
|
||
- off
|
||
- counts [count]
|
||
- cycles [count]
|
||
- i-misses [count]
|
||
- d-hits [count]
|
||
- symbols [count]
|
||
- addresses [address]
|
||
- callers
|
||
- caches
|
||
- stack
|
||
- stats
|
||
- save <file>
|
||
- loops <file> [CPU limit] [DSP limit]
|
||
|
||
|
||
'on' ¨ 'off' enable and disable profiling. Data is collected
|
||
until debugger is entered again, at which point profiling
|
||
statistics ('stats') summary is shown.
|
||
|
||
Most active PC addresses can be queried, sorted either by
|
||
execution 'counts', used 'cycles', i-cache misses or d-cache hits.
|
||
First can be limited just to named addresses with 'symbols'.
|
||
Optional count will limit how many items will be shown.
|
||
|
||
'caches' shows histogram of CPU cache usage.
|
||
|
||
'addresses' lists the profiled addresses in order, with the
|
||
instructions (currently) residing at them. By default this
|
||
starts from the first executed instruction, or from the
|
||
specified start address.
|
||
|
||
'callers' shows (raw) caller information for addresses which
|
||
had symbol(s) associated with them. 'stack' shows the current
|
||
profile stack (this is useful only with :noinit breakpoints).
|
||
|
||
Profile address and callers information can be saved with
|
||
'save' command.
|
||
|
||
Detailed (spin) looping information can be collected by
|
||
specifying to which file it should be saved, with optional
|
||
limit(s) on how many bytes first and last instruction
|
||
address of the loop can differ (0 = no limit).
|
||
</pre>
|
||
|
||
<p>For example, to see which memory addresses were executed most
|
||
and what instructions those have at the end of profiling, use:</p>
|
||
<pre>
|
||
> profile counts 8
|
||
addr: count:
|
||
0xe06f10 12.11% 555724 move.l $4ba,d1
|
||
0xe06f16 12.11% 555724 cmp.l d1,d0
|
||
0xe06f18 12.11% 555724 bgt.s $e06f06
|
||
0xe06f06 12.11% 555708 move.b $fffffa01.w,d1
|
||
0xe06f0a 12.11% 555708 btst #5,d1
|
||
0xe06f0e 12.11% 555708 beq.s $e06f1e
|
||
0xe00ed8 1.66% 76001 subq.l #1,d0
|
||
0xe00eda 1.66% 76001 bpl.s $e00ed8
|
||
8 CPU addresses listed.
|
||
</pre>
|
||
|
||
<p>
|
||
Then, to see what the executed code and its costs look like
|
||
around top addresses:
|
||
<pre>
|
||
> profile addresses 0xe06f04
|
||
# disassembly with profile data:
|
||
# <instructions percentage>% (<sum of instructions>, <sum of cycles>, <sum of i-cache misses>, <sum of d-cache hits>)
|
||
|
||
$e06f04 : bra.s $e06f10 0.00% (48, 576, 0, 0)
|
||
$e06f06 : move.b $fffffa01.w,d1 12.11% (555708, 8902068, 0, 0)
|
||
$e06f0a : btst #5,d1 12.11% (555708, 6685268, 0, 0)
|
||
$e06f0e : beq.s $e06f1e 12.11% (555708, 4457312, 0, 0)
|
||
$e06f10 : move.l $4ba,d1 12.11% (555724, 11125668, 0, 0)
|
||
$e06f16 : cmp.l d1,d0 12.11% (555724, 4461708, 0, 0)
|
||
$e06f18 : bgt.s $e06f06 12.11% (555724, 4455040, 0, 0)
|
||
$e06f1a : moveq #1,d0 0.00% (16, 64, 0, 0)
|
||
Disassembled 8 (of active 14240) CPU addresses.
|
||
</pre>
|
||
<p>
|
||
Unlike normal disassembly, "profile addresses" command shows only
|
||
memory addresses which instructions were executed during profiling.
|
||
Cache hit/miss information is provided only when using cycle-accurate
|
||
680x0 emulation.
|
||
<p>
|
||
With symbol information loaded, symbol names are shown above the
|
||
corresponding addresses. And "profile symbols" command provides a list
|
||
of how many times the code execution passed through the defined symbol
|
||
addresses.
|
||
</p>
|
||
|
||
|
||
<h4>Profile data accuracy</h4>
|
||
|
||
<p>Profile data accuracy depends on Hatari emulation accuracy.
|
||
Profile data accuracy, from most to least accurate, with default
|
||
Hatari emulation options, is following:</p>
|
||
<ul>
|
||
<li>CPU and DSP instruction counts: accurate.</li>
|
||
<li>68000 cycle counts: tested to be accurate to within 1%.</li>
|
||
<li>MegaSTE 16Mhz 68000 (16KB) cache: hit/miss/cycle counts should also be accurate.</li>
|
||
<li>DSP cycle counts (and the variance information): should be accurate.</li>
|
||
<li>030 (256B+256B) cache: hit/miss counts are assumed to be accurate.</li>
|
||
<li>030 cycle counts: can be off by tens of percents.</li>
|
||
<li>FPU cycle counts: can be off by 2x.</li>
|
||
<li>TT-RAM access: perf improvement over ST-RAM is not emulated.</li>
|
||
<li>040/060 cycle counts: reported, but not accurate, nor really emulated - ignore.</li>
|
||
</ul>
|
||
|
||
|
||
<h4>Caller information</h4>
|
||
|
||
<p>
|
||
With symbols loaded (see <a href="#Debug_symbols">Debug symbols</a>)
|
||
before continuing emulation/profiling, additional caller information
|
||
will be collected for all the code symbol addresses which are called
|
||
as subroutines. This information includes callstack, call counts,
|
||
calling instruction type (subroutine call, branch, return etc), and
|
||
costs for those calls, both including costs for further subroutine
|
||
calls and without them.
|
||
</p>
|
||
|
||
<p>When debugger is re-entered, current callstack is output:</p>
|
||
<pre>
|
||
> a <em>_P_LineAttack</em>
|
||
CPU condition breakpoint 1 with 1 condition(s) added:
|
||
pc = $30f44
|
||
$030f44 : 48e7 3820 movem.l d2-d4/a2,-(sp)
|
||
> c
|
||
...
|
||
CPU breakpoint condition(s) matched 1 times.
|
||
pc = $30f44
|
||
Profiler backtrace:
|
||
#1 0x32a3c: _P_GunShot (return = 0x32b7e)
|
||
#2 0x32b18: _A_FireShotgun (return = 0x3229a)
|
||
#3 0x3223a: _P_SetPsprite (return = 0x32e86)
|
||
#4 0x32e4e: _P_MovePsprites (return = 0x38070)
|
||
#5 0x37f44: _P_PlayerThink (return = 0x36ea0)
|
||
#6 0x36e44: _P_Ticker (return = 0x260e0)
|
||
#7 0x25dcc: _G_Ticker (return = 0x1e4c6)
|
||
#8 0x1e29e: _TryRunTics (return = 0x239fa)
|
||
#9 0x238e8: _D_DoomLoop (return = 0x2556a)
|
||
#10 0x24d7a: _D_DoomMain (return = 0x44346)
|
||
...
|
||
</pre>
|
||
|
||
<p>("profile stack" command can be used in breakpoints with ":noinit"
|
||
option to show backtraces during caller profiling.)</p>
|
||
|
||
<p>Note: rest of this subsection is about caller information format
|
||
which is mainly of interest for people writing profiling
|
||
post-processing tools. Come back here if there is
|
||
some problem with callgraphs produced by those tools.</p>
|
||
|
||
<p>Other information collected during profiling is shown with
|
||
following command:</p>
|
||
<pre>
|
||
> profile callers
|
||
# <callee>: <caller1> = <calls> <types>[ <inclusive/totals>[ <exclusive/totals>]], <caller2> ..., <callee name>
|
||
# types: s = subroutine call, r = return from subroutine, e = exception, x = return from exception,
|
||
# b = branch/jump, n = PC moved to next instruction, u = unknown PC change
|
||
# totals: calls/instructions/cycles/misses
|
||
0xe00030: 0xffffff = 1 e, _main
|
||
0xe000fe: 0xe00a0c = 1 b, memdone
|
||
0xe0010a: 0xe04e34 = 1 s 1/5/72 1/5/72, _run_cartridge_applications
|
||
0xe00144: 0xe04dbe = 1 s 4/118/1512 1/27/444, _init_acia_vecs
|
||
0xe001ea: 0xe00ec6 = 1 b, _int_acia
|
||
0xe0038c: 0xe04c28 = 1 s 1/191/2052 1/191/2052, _init_exc_vec
|
||
0xe003a6: 0xe04c2e = 1 s 1/388/4656 1/388/4656, _init_user_vec
|
||
...
|
||
</pre>
|
||
|
||
<p>
|
||
For example, when one does not know all the places from which a certain
|
||
function is called, or in what context a certain interrupt handler can
|
||
be called during the period being profiled, profile caller
|
||
information will tell that:
|
||
</p>
|
||
<pre>
|
||
callee: caller: calls: calltype:
|
||
| | | /
|
||
0x379: 0x155 = 144 r, 0x283 = 112 b, 0x2ef = 112 b, 0x378 = 72 s
|
||
583236/359708265/1631189180 72/4419020/19123430, dsp_interrupt
|
||
| | |
|
||
inclusive costs exclusive costs callee name
|
||
(of calls from 0x378)
|
||
|
||
Calltypes:
|
||
- b: jump/branch
|
||
- n: PC just moved to next address
|
||
- r: subroutine return
|
||
- s: subroutine call
|
||
</pre>
|
||
<p>
|
||
(Most "calls" to "dsp_interrupt" were subroutine call returns (=r)
|
||
to it from address 0x155.)
|
||
</p>
|
||
|
||
<p>
|
||
With the execution counts in normal profiling data, caller information
|
||
can actually be used to have complete picture of what exactly the code
|
||
did during profiling. Main/overview work for this analysis is best done
|
||
automatically, by the profiler data post-processor (documented below).
|
||
</p>
|
||
|
||
|
||
<h4>Caller data accuracy</h4>
|
||
|
||
<p>Everything about profile data accuracy applies also to caller costs,
|
||
but there are additional things to take into account, mainly because
|
||
profiler cannot determine when exceptions are being handled:</p>
|
||
<ul>
|
||
<li>If there are exception(s) during a subroutine call, costs for
|
||
the exception handling will also be accounted for that subroutine.
|
||
This should not be a problem unless those costs are very large,
|
||
i.e. check how much CPU those exception handlers take.</li>
|
||
<li>Indicated exception handler call type can be incorrect.</li>
|
||
<li>Profiled code doing return address related stack manipulations
|
||
confuses call tracking and produces incorrect results (profiler
|
||
has special code to handle EmuTOS AES switcher because of this).
|
||
Typically this produces large list of functions that are finalized
|
||
at profile end, so it should be easy to detect.</li>
|
||
<li>Complicated recursive calls seem to sometimes cause inclusive
|
||
costs (ones including costs of further subroutine calls) to be
|
||
incorrect, e.g. >100%.</li>
|
||
<li>On DSP, profiler heuristics assume (for speed reasons) that
|
||
<em>conditional</em> subroutine calls never call the very next
|
||
instruction (as that would be very bad/inefficient code).</li>
|
||
</ul>
|
||
|
||
|
||
<h4>Saving profile data to a file</h4>
|
||
|
||
<p>It is useful to save the profile data to a file:</p>
|
||
<pre>
|
||
> profile save program-profile.txt
|
||
</pre>
|
||
|
||
<p>With the saved profile disassembly (and optional caller information)
|
||
one can more easily investigate what the program did during
|
||
profiling, search symbols & addresses in it, and compare the
|
||
results to profiles saved from earlier versions of the code.</p>
|
||
|
||
<p>One could even create own post-processing tools for
|
||
investigating the profiling data more closely, e.g. to
|
||
<a href="http://www.atari-forum.com/viewtopic.php?f=68&t=24561&start=75#p226505">find
|
||
CPU/DSP communication bottlenecks</a>.</p>
|
||
|
||
|
||
<h3>Profile data post-processing</h3>
|
||
|
||
<p>Saved profile data can be post-processed with (Python) script
|
||
installed by Hatari, to:</p>
|
||
<ul>
|
||
<li>Get lists of functions/symbols with highest costs.</li>
|
||
<li>Get callgraphs of what functions/symbols cause those
|
||
costs and what kind of call hierarchy the profiled code
|
||
has.</li>
|
||
<li>Export profile data in Valgrind's
|
||
<a href="http://valgrind.org/docs/manual/cl-format.html">Callgrind format</a>
|
||
for viewing it in
|
||
<a href="http://kcachegrind.sourceforge.net/">Kcachegrind</a>
|
||
GUI.</li>
|
||
</ul>
|
||
|
||
|
||
<h4>Providing symbols for the post-processor</h4>
|
||
|
||
<p>When the data is post-processed, one should <em>always</em> provide
|
||
the post-processor symbols for the profile code! Relying just on the
|
||
symbols in the profile disassembly can cause costs to be assigned to
|
||
wrong symbol, if code within a function was not called through its
|
||
symbol address, but by jumping directly somewhere inside the function.
|
||
</p>
|
||
|
||
<p>If code is at a fixed location, one should tell
|
||
post-processor to handle symbol addresses as absolute (-a):</p>
|
||
<pre>
|
||
$ hatari_profile.py <b>-a</b> etos1024k.sym emutos-profile.txt
|
||
</pre>
|
||
|
||
<p>Normal programs are relocated and one should instead give
|
||
the symbols as TEXT (code) section relative ones (-r):</p>
|
||
<pre>
|
||
$ hatari_profile.py <b>-r</b> program.sym program-profile.txt
|
||
</pre>
|
||
|
||
<p>If symbols are included to the binary, first they need to be
|
||
extracted to <a href="#Debug_symbols">the ASCII format</a>
|
||
understood by the post-processor:</p>
|
||
<pre>
|
||
$ gst2ascii -b -a -d program.prg > program.sym
|
||
</pre>
|
||
<p>(Options given to "gst2ascii" filter out symbols for other
|
||
things than what are in the program code section.)
|
||
|
||
<p>If there are some extra symbols that one does not want to see
|
||
separately in profiles, because they are not real functions,
|
||
but e.g. loop labels, one can either remove them manually
|
||
from the ASCII *.sym file, or filter them out with "grep":
|
||
</p>
|
||
<pre>
|
||
$ gst2ascii -b -a -d program.prg | grep -v -e useless1 -e useless2 > program.sym
|
||
</pre>
|
||
|
||
<p>For C++ programs, see earlier section(s) on how to best
|
||
provide symbols for them.</p>
|
||
|
||
|
||
<h4>Post-processor provided statistics</h4>
|
||
|
||
<p>Above post-processor examples just parse + verify the given data
|
||
and produce output like this:</p>
|
||
<pre>
|
||
Hatari profile data processor
|
||
|
||
Parsing TEXT relative symbol address information from program.sym...
|
||
[...]
|
||
3237 lines with 1550 code symbols/addresses parsed, 0 unknown.
|
||
|
||
Parsing profile information from program-profile.txt...
|
||
[...]
|
||
9575 lines processed with 368 functions.
|
||
|
||
CPU profile information from 'program-profile.txt':
|
||
- Hatari v1.6.2+ (May 4 2013), WinUAE CPU core
|
||
</pre>
|
||
|
||
<p>To get statistics (-s) and list of top (-t) CPU users in profile,
|
||
add "-st" option:</p>
|
||
<pre>
|
||
$ hatari_profile.py <b>-st</b> -r program.sym program-profile.txt
|
||
[...]
|
||
CPU profile information from 'program-profile.txt':
|
||
- Hatari v1.6.2+ (May 4 2013), WinUAE CPU core
|
||
|
||
Time spent in profile = 34.49539s.
|
||
|
||
Calls:
|
||
- max = 187738, in __toupper at 0x52b88, on line 8286
|
||
- 1585901 in total
|
||
Executed instructions:
|
||
- max = 1900544, in flat_remap_mips+14 at 0x47654, on line 7020
|
||
- 64499351 in total
|
||
Used cycles:
|
||
- max = 15224620, in flat_remap_mips+18 at 0x47658, on line 7022
|
||
- 553392132 in total
|
||
Instruction cache misses:
|
||
- max = 184308, in _BM_T_GetTicks at 0x43b90, on line 4772
|
||
- 4941307 in total
|
||
|
||
Calls:
|
||
11.84% 187698 __toupper
|
||
11.48% 182105 _BM_T_GetTicks
|
||
11.48% 182019 _I_GetTime
|
||
[...]
|
||
Executed instructions:
|
||
34.83% 22462729 flat_generate_mips
|
||
14.08% 9080215 flat_remap_mips
|
||
8.55% 5515945 render_patch_direct
|
||
5.09% 3283328 _TryRunTics
|
||
[...]
|
||
Used cycles:
|
||
23.62% 130702768 flat_generate_mips
|
||
12.42% 68735832 flat_remap_mips
|
||
9.77% 54041148 _TryRunTics
|
||
5.80% 32111536 correct_element
|
||
[...]
|
||
Instruction cache misses:
|
||
37.03% 1829764 _TryRunTics
|
||
11.20% 553314 _BM_T_GetTicks
|
||
9.44% 466319 _NetUpdate
|
||
9.27% 457899 _HGetPacket
|
||
[...]
|
||
</pre>
|
||
|
||
<p>To see also symbol addresses and what is per call
|
||
cost, add -i option:</p>
|
||
<pre>
|
||
$ hatari_profile.py -st <b>-i</b> -r program.sym program-profile.txt
|
||
[...]
|
||
Executed instructions:
|
||
34.83% 22462729 flat_generate_mips (0x04778a, 774576 / call)
|
||
14.08% 9080215 flat_remap_mips (0x047646, 313110 / call)
|
||
8.55% 5515945 render_patch_direct (0x047382, 29977 / call)
|
||
5.09% 3283328 _TryRunTics (0x042356, 19660 / call)
|
||
[...]
|
||
Used cycles:
|
||
23.62% 8.14728s 130702768 flat_generate_mips (0x04778a, 0.28094s / call)
|
||
12.42% 4.28461s 68735832 flat_remap_mips (0x047646, 0.14775s / call)
|
||
9.77% 3.36863s 54041148 _TryRunTics (0x042356, 0.02017s / call)
|
||
5.80% 2.00165s 32111536 correct_element (0x04a658, 0.00001s / call)
|
||
[...]
|
||
Instruction cache misses:
|
||
37.03% 1829764 _TryRunTics (0x042356, 10956 / call)
|
||
11.20% 553314 _BM_T_GetTicks (0x043b90, 3 / call)
|
||
9.44% 466319 _NetUpdate (0x041bcc, 5 / call)
|
||
9.27% 457899 _HGetPacket (0x041754, 5 / call)
|
||
[...]
|
||
</pre>
|
||
|
||
<p>(For cycles the "per call" information is in seconds, not as
|
||
a cost count.)</p>
|
||
|
||
<p>If profile file contains caller information, "-p" option should
|
||
be added to see it, as that will also help in detecting symbol
|
||
issues (see <a href="#Interpreting_the_results">Interpreting
|
||
the results</a>):</p>
|
||
<pre>
|
||
$ hatari_profile.py -st <b>-p</b> -r program.sym program-profile.txt
|
||
[...]
|
||
9575 lines processed with 368 functions.
|
||
[...]
|
||
Of all 1570498 switches, ignored 581 for type(s) ['r', 'u', 'x'].
|
||
|
||
CPU profile information from 'badmood-level-load-CPU.txt':
|
||
- Hatari v1.6.2+ (May 4 2013), WinUAE CPU core
|
||
[...]
|
||
Calls:
|
||
11.84% 11.84% 187698 187698 __toupper
|
||
11.48% 11.48% 182105 182105 _BM_T_GetTicks
|
||
11.48% 22.95% 182019 364038 _I_GetTime
|
||
[...]
|
||
Executed instructions:
|
||
34.83% 34.86% 34.86% 22462729 22484024 22484024 flat_generate_mips
|
||
14.08% 14.10% 14.10% 9080215 9091270 9091676 flat_remap_mips
|
||
8.55% 5515945 render_patch_direct
|
||
5.09% 5.11% 94.96% 3283328 3294022 61247717 _TryRunTics
|
||
[...]
|
||
Used cycles:
|
||
23.62% 23.69% 23.69% 130702768 131100604 131100604 flat_generate_mips
|
||
12.42% 12.46% 12.46% 68735832 68928816 68930904 flat_remap_mips
|
||
9.77% 9.80% 95.66% 54041148 54238744 529368824 _TryRunTics
|
||
5.80% 5.82% 5.82% 32111536 32193664 32193664 correct_element
|
||
[...]
|
||
Instruction cache misses:
|
||
37.03% 37.14% 98.57% 1829764 1835261 4870573 _TryRunTics
|
||
11.20% 11.24% 11.24% 553314 555191 555191 _BM_T_GetTicks
|
||
9.44% 9.49% 29.13% 466319 468782 1439340 _NetUpdate
|
||
9.27% 9.29% 9.37% 457899 459197 463217 _HGetPacket
|
||
[...]
|
||
</pre>
|
||
|
||
<p>Now there's a message telling that some of the calls were ignored
|
||
because according to their "call type", they were actually returns from
|
||
exceptions, not real calls (this is mainly important for callgraph
|
||
generation, discussed below).</p>
|
||
|
||
|
||
<h4>Interpreting the results</h4>
|
||
|
||
<p>In addition to accuracy issues mentioned in previous Profiling
|
||
sections, function/symbol level costs have gotchas of their own.</p>
|
||
|
||
<p>The first cost percentage and count column are sums for costs
|
||
accounted for all the <em>addresses</em> that were in profile data file
|
||
<em>between the indicated symbol's address and the address of the next
|
||
symbol</em> (= "between-symbols" cost).</p>
|
||
|
||
<p><strong>NOTE:</strong> If symbols file does not contain addresses
|
||
for all the relevant symbols, results from this can be misleading;
|
||
instruction costs get assigned to <em>whatever</em> symbol's address
|
||
happened to precede those instructions. One does not see which caller
|
||
is causing it from the caller info or callgraphs either, as missing a
|
||
symbol for an entry point for such a time sink, means that calls to it
|
||
had not been tracked by profiler...</p>
|
||
|
||
<p>The next two percentages (and cost counts) are total cost of calls
|
||
to given subroutine, based on profiler runtime branch tracking (see
|
||
caller information documented above). First value is ("exclusive")
|
||
cost for just that subroutine (from its entry, until execution returns
|
||
to where it was called from), <em>without</em> costs for branches to
|
||
further subroutines. Latter value is ("inclusive") cost covering also
|
||
costs for all the subroutines it calls.</p>
|
||
|
||
<p>Reasons why "between-symbols" costs, and subroutine call costs
|
||
can differ, are following:</p>
|
||
<ul>
|
||
<li>Subroutine terminates before next symbol address: "exclusive" cost
|
||
is smaller than "between-symbols" cost <em>because of missing
|
||
symbol information</em>
|
||
(these are indicated with '*' in statistics).</li>
|
||
<li>Subroutine is called more through jumps/branches than through
|
||
subroutine calls (JSR/BSR): "inclusive" call count may be smaller
|
||
than "between-symbols" call count which includes costs also for
|
||
function entries through branches/jumps.</li>
|
||
<li>Subroutine jumps/branches to another function instead of
|
||
using subroutine call, or function contains additional
|
||
(non-function) labels: "exclusive" cost is larger than
|
||
"between-symbols" cost.</li>
|
||
<li>Exception happening during subroutine call: "exclusive" cost
|
||
is (slightly) larger than "between-symbols" cost.</li>
|
||
</ul>
|
||
|
||
<p>In the first case, one should check saved profile disassembly to
|
||
find out whether there are missing symbols for executed function entry
|
||
points. One can notice function entry points as address gaps and/or
|
||
instructions retrieving arguments from stack. Exit points can be seen
|
||
from RTS instructions.</p>
|
||
|
||
<p>Second case can also be seen from the profile disassembly. Call
|
||
count is same as count for how many times first instruction is executed
|
||
(worst case: large loop on subroutine's first instruction).</p>
|
||
|
||
<p>While subroutine costs should be more accurate and relevant, due to
|
||
code optimizations, many of the functions are not called as subroutines
|
||
(on m68k, using JSR/BSR), but just jumped or branched to. Because of
|
||
this, it is useful to compare both subroutine and "between-symbols"
|
||
costs. One should be able to see from the profile disassembly which
|
||
of the above cases is cause for the discrepancy in the values.</p>
|
||
|
||
<p><strong>NOTE:</strong> Before starting to do any serious
|
||
optimizations based on profile information, one should <em>always</em>
|
||
verify from profile disassembly where exactly the costs are in a
|
||
function, to make sure optimization efforts actually <em>can</em> have
|
||
an impact on performance.</p>
|
||
|
||
|
||
<h4>Generating and viewing callgraphs</h4>
|
||
|
||
<p>Callgraphs require that saved profile data contains caller
|
||
function address information, i.e. symbols for the code should
|
||
be loaded before starting profiling it (see
|
||
<a href="#Debug_symbols">loading symbol data</a>).</p>
|
||
|
||
<p>Separate callgraphs will be created for each of the costs
|
||
(0=calls, 1=instructions, 2=cycles) with the -g option:</p>
|
||
<pre>
|
||
$ hatari_profile.py <b>-p -g</b> -r program.sym program-profile.txt
|
||
[...]
|
||
Generating 'program-profile-0.dot' DOT callgraph file...
|
||
|
||
Generating 'program-profile-1.dot' DOT callgraph file...
|
||
|
||
Generating 'program-profile-2.dot' DOT callgraph file...
|
||
[...]
|
||
</pre>
|
||
|
||
<p>Callgraphs are saved in <a href="http://www.graphviz.org/">GraphViz</a>
|
||
"dot" format. Dot files can be viewed:</p>
|
||
<ul>
|
||
<li>With "dotty" program included with GraphViz</li>
|
||
<li>With <a href="https://github.com/jrfonseca/xdot.py">XDot</a>
|
||
Python GUI (best option on Linux), or some platform specific viewer</li>
|
||
<li>By converting dot file to PostScript or SVG format before
|
||
viewing it with viewers for those:
|
||
<pre>
|
||
$ dot -Tsvg program-profile-1.dot > program-profile-1.svg
|
||
</pre>
|
||
(problem with most PS/PDF and SVG viewers is that either they
|
||
do not allow zooming large callgraphs enough or they use huge
|
||
amounts of memory and get very slow)
|
||
</li>
|
||
</ul>
|
||
|
||
<p>Produced callgraph will look like this:</p>
|
||
<div style="text-align:center">
|
||
<a href="images/callgraph.svg">
|
||
<img src="images/callgraph.png" width="953" height="589"
|
||
alt="Part of callgraph" />
|
||
</a>
|
||
</div>
|
||
|
||
<p>Interpreting the callgraph:</p>
|
||
<ul>
|
||
<li>Diamond shaped nodes are symbols called as subroutines.
|
||
Values listed in them are subroutine call costs; inclusive
|
||
(total) cost with exclusive (own) cost in parenthesis,
|
||
followed by inclusive cost count. Exclusive cost is
|
||
shown only if it differs from inclusive one.</li>
|
||
<li>Ellipse shaped nodes are for other symbols (functions
|
||
called using jumps/branches, loop labels etc). Values
|
||
listed in them are between-symbols costs, i.e. normally
|
||
they are included to inclusive (total) costs shown in
|
||
subroutine call node somewhere higher in call hierarchy.</li>
|
||
<li>Nodes which exclusive (own) or between-symbols costs
|
||
exceed default or explicitly given threshold value,
|
||
have gray background.</li>
|
||
<li>Both nodes, which inclusive or between-symbols cost exceeds
|
||
the threshold value, and the arrows to & from them,
|
||
are marked red.</li>
|
||
<li>Arrow types indicate call types; normal arrows subroutine
|
||
calls, circles branches/jumps, backarrows returns.
|
||
Exception calls and returns are indicated with dashed lines,
|
||
unknown calls with dotted lines.</li>
|
||
<li>Arrow text tells from which address (within the caller)
|
||
the call originated. If symbol had multiple callers, text
|
||
includes count of calls from that particular address, and its
|
||
percentage is of all calls done to that symbol.</li>
|
||
</ul>
|
||
|
||
|
||
<h4>Making large callgraphs readable</h4>
|
||
|
||
<p>
|
||
If profile is for larger and more varied amount of code (e.g. program startup),
|
||
the resulting callgraph can be so huge that it not really readable anymore.
|
||
</p>
|
||
|
||
<p>When code includes interrupt handlers, they can get called
|
||
at any point, which can show in callgraph as "explicit" calls
|
||
from the interrupted functions. To get rid of such incorrect
|
||
calls, give interrupt handler names to "--ignore-to" option:</p>
|
||
<pre>
|
||
$ hatari_profile.py -p -g <b>--ignore-to handler1,handler2</b> -r program.sym program-profile.txt
|
||
</pre>
|
||
|
||
<p>In large callgraph most of the functions are not really interesting,
|
||
because their contribution to the cost is insignificant. One can
|
||
remove large number of them with "--no-leafs" and "--no-intermediate"
|
||
options, those options act <em>only</em> on nodes which costs are below
|
||
given threshold. Leaf nodes are ones which do not have any parents
|
||
and/or children. Intermediate ones have only single parent and
|
||
children (node calling itself is not taken into account).
|
||
|
||
<p>Threshold for this is given with the "--limit" (-l) option. With
|
||
that it typically makes also sense to change the node emphasis
|
||
threshold with --emph-limit (-e) option:</p>
|
||
<pre>
|
||
$ hatari_profile.py -p -g <b>-l 0.5 -e 2.0</b> -r program.sym program-profile.txt
|
||
</pre>
|
||
|
||
<p>When not interested from how many different addresses
|
||
a given function calls another function, use "--compact" option. If
|
||
multiple calls between two nodes are still present with it, the reason
|
||
is them happening through different call paths which were removed
|
||
from the callgraph after "--compact" option was applied:</p>
|
||
<pre>
|
||
$ hatari_profile.py -p -g -l 1.0 -e 2.0 <b>--no-leafs --no-intermediate --compact</b> -r program.sym program-profile.txt
|
||
</pre>
|
||
|
||
<p>If even this does not help, all nodes below the given cost threshold
|
||
limit can be removed with the "--no-limited" option, but this
|
||
often does not leave much of a call hierarchy. Instead you may
|
||
consider removing all nodes except for subroutine call ones, with the
|
||
"--only-subroutines" option.</p>
|
||
|
||
<p>When having trouble locating nodes of special interest, one can
|
||
either color them differently with the "--mark" option, or exclude
|
||
everything else from the callgraph except those nodes and their
|
||
immediate callers & callees, with the "--only" option:</p>
|
||
<pre>
|
||
$ hatari_profile.py -p -g <b>--only func1,func2</b> -r program.sym program-profile.txt
|
||
</pre>
|
||
|
||
<p>NOTE: post-processor options taking symbol names, understand
|
||
"fnmatch" patterns. They help when one would need to specify very
|
||
long / complex C++ symbol names, or group(s) of symbols with similar
|
||
names (e.g. "*_interrupt_handler").
|
||
|
||
<p>Last option for reading the callgraph is using "-k" option to
|
||
export the data for use in (Linux) Kcachegrind UI. Kcachegrind
|
||
generates callgraphs on the fly, and just for the area around the
|
||
selected function, so navigating in callgraph may be easier. It also
|
||
shows the related profile disassembly, which can make verifying
|
||
matters easier:</p>
|
||
<pre>
|
||
$ hatari_profile.py <b>-p -k</b> -r program.sym program-profile.txt
|
||
[...]
|
||
Generating callgrind file 'program-profile.cg'...
|
||
[...]
|
||
$ kcachegrind program-profile.cg
|
||
</pre>
|
||
<div style="text-align:center">
|
||
<img src="images/kcachegrind.png" width="887" height="442"
|
||
alt="Kcachegrind screenshot" />
|
||
</div>
|
||
|
||
|
||
<h3>Usage examples</h3>
|
||
|
||
<p>
|
||
Here is a list of some common debugging tasks and how to do them
|
||
with the Hatari debugger:
|
||
</p>
|
||
|
||
<dl>
|
||
<dt><em>Stopping on program startup, e.g. to examine its data</em></dt>
|
||
<dd>Run Hatari with options asking it to load symbols for a program
|
||
when it's executed (from GEMDOS HD) and parse a debugger input
|
||
file (e.g. "break.ini") that sets breapoint on the first program
|
||
instruction (i.e. at start of its TEXT section):
|
||
<pre>
|
||
$ echo "b pc = TEXT" > break.ini
|
||
$ hatari --symload exec --parse prg:break.ini ...
|
||
</pre>
|
||
</dd>
|
||
|
||
<dt><em>Tracing specific things in the system</em></dt>
|
||
<dd>To trace e.g. all GEMDOS calls and I/O operations, use:
|
||
<pre>
|
||
trace gemdos,io_all
|
||
</pre>
|
||
Please see <a href="#Tracing">Tracing</a> section for more information
|
||
on tracing, what is possible with it and what are its limitations.
|
||
</dd>
|
||
|
||
<dt><em>Stopping when certain PC address is passed Nth time</em></dt>
|
||
<dd>To stop e.g. after function/subroutine at $12345 is called for
|
||
the 6th time:
|
||
<pre>
|
||
a $12345 :6
|
||
</pre>
|
||
</dd>
|
||
|
||
<dt><em>Stopping when specific exception happens</em></dt>
|
||
<dd>If one wants a specific breakpoint to trigger on a specific
|
||
exception, one can check when its handler address is called by the
|
||
CPU. At the start of memory is the CPU exception table for exception
|
||
vectors. So, to stop e.g. at bus error with some extra information,
|
||
one can use following:
|
||
<pre>
|
||
history on
|
||
b pc=($8)
|
||
</pre>
|
||
After bus error invokes debugger, "history" command can then be used
|
||
to see (executed memory addresses with their current) instructions
|
||
leading to the error. The most interesting vector addresses are:
|
||
$8 (Bus error), $C (Address error), $10 (Illegal instruction),
|
||
$14 (Division by zero).<br>
|
||
NOTE: Normally, to invoke debugger for larger set of exceptions, one
|
||
would use Hatari's "--debug-except" option to specify on which
|
||
exceptions debugger is invoked, and "setopt -D" to enable that on
|
||
run-time.
|
||
</dd>
|
||
|
||
<dt><em>Stopping when register has a specific value</em></dt>
|
||
<dd>To stop when e.g. D1 register contains value 5, set a breakpoint on:
|
||
<pre>
|
||
b d1 = 5
|
||
</pre>
|
||
</dd>
|
||
|
||
<dt><em>Stopping when a register value changes</em></dt>
|
||
<dd>To stop when e.g. D1 register value changes, set a breakpoint on:
|
||
<pre>
|
||
b d1 ! d1
|
||
</pre>
|
||
</dd>
|
||
|
||
<dt><em>Stopping when part of register value changes</em></dt>
|
||
<dd>To stop when e.g. D1 register lowest byte changes, set a
|
||
breakpoint on masked lowest 8 bits:
|
||
<pre>
|
||
b d1 & 0xff ! d1 & 0xff
|
||
</pre>
|
||
</dd>
|
||
|
||
<dt><em>Stopping when register value is within some range</em></dt>
|
||
<dd>To stop when e.g. D1 register value is within range of 10-30,
|
||
set a breakpoint on:
|
||
<pre>
|
||
b d1 > 9 && d1 < 31
|
||
</pre>
|
||
</dd>
|
||
|
||
<dt><em>Stopping when memory location has a specific value</em></dt>
|
||
<dd>To stop when e.g. bit 1 of the Video Shifter Sync Mode byte at
|
||
I/O address $ff820a is set i.e. video frequency is 60Hz, set
|
||
a breakpoint on:
|
||
<pre>
|
||
b ($ff820a).b & 2 = 2
|
||
</pre>
|
||
</dd>
|
||
|
||
<dt><em>Stopping when a memory value changes</em></dt>
|
||
<dd>To stop when above bit changes, set a breakpoint on its value
|
||
being different from the current value ('!' compares for inequality):
|
||
<pre>
|
||
b ($ff820a).b & 2 ! ($ff820a).b & 2
|
||
</pre>
|
||
</dd>
|
||
|
||
<dt><em>Tracing all changes in specific memory location</em></dt>
|
||
<dd>To see the new values and continue without stopping, add
|
||
the ":trace" breakpoint option:
|
||
<pre>
|
||
b ($ff820a).b & 2 ! ($ff820a).b & 2 :trace
|
||
</pre>
|
||
</dd>
|
||
|
||
<dt><em>Finding specific data from memory</em></dt>
|
||
<dd>Find "ETOS" character string matches from ROM:
|
||
<pre>
|
||
find c 0xe00000 E T O S
|
||
</pre>
|
||
Find all potential (word aligned) RTS instructions from RAM:
|
||
<pre>
|
||
find w 0x0 0x4e75
|
||
</pre>
|
||
</dd>
|
||
|
||
<dt><em>Values in different number base</em></dt>
|
||
<dd>To see values in different number bases, use "evaluate"
|
||
command. It can be used also with more complex expressions:
|
||
<pre>
|
||
> e ($ff8802).b & 0x7
|
||
value at ($ff8802).b = $27
|
||
= %111 (bin), #7 (dec), $7 (hex)
|
||
</pre>
|
||
</dd>
|
||
|
||
<dt><em>Viewing OS structure and IO register values</em></dt>
|
||
<dd>To see e.g. basepage for currently running program:
|
||
<pre>
|
||
info basepage
|
||
</pre>
|
||
To see e.g. all Falcon Videl register values, use:
|
||
<pre>
|
||
info videl
|
||
</pre>
|
||
</dd>
|
||
|
||
<dt><em>Showing OS attribute information for specific OS calls</em></dt>
|
||
<dd>To see e.g. VDI attributes for all v_gtext VDI calls:
|
||
<pre>
|
||
b VdiOpcode = $8 :quiet :info vdi
|
||
</pre>
|
||
</dd>
|
||
|
||
<dt><em>Stopping at specific screen position</em></dt>
|
||
<dd>To stop e.g. when VBL is 100, HBL is 40 and line cycles is 5,
|
||
use the corresponding debugger variables:
|
||
<pre>
|
||
b VBL = 100 && HBL = 40 && LineCycles = 5
|
||
</pre>
|
||
</dd>
|
||
|
||
<dt><em>Stopping after value increases/decreases by certain amount</em></dt>
|
||
<dd>To stop e.g. after D0 value has increased by 10, set breakpoint on:
|
||
<pre>
|
||
b d0 = 'd0 + 10'
|
||
</pre>
|
||
</dd>
|
||
|
||
<dt><em>Examining specific system call return value</em></dt>
|
||
<dd>To check e.g. what is the Fopen() GEMDOS call return value,
|
||
check with "info gemdos 1" its opcode, set a breakpoint for that
|
||
and step to next (n) instruction from the trap call when breakpoint
|
||
is hit. GEMDOS call return value is then in register D0:
|
||
<pre>
|
||
> trace gemdos
|
||
> b GemdosOpcode = $3D
|
||
> c
|
||
[...continue until breakpoint...]
|
||
1. CPU breakpoint condition(s) matched 1 times.
|
||
GemdosOpcode = $3D
|
||
> n
|
||
GEMDOS 0x3D Fopen("TEST.TXT", read-only)
|
||
> e d0
|
||
= %1000000 (bin), #64 (dec), $40 (hex)
|
||
</pre>
|
||
</dd>
|
||
|
||
<dt><em>Seeing code leading to a breakpoint</em></dt>
|
||
<dd>To see CPU instructions executed before debugger was entered,
|
||
history tracking needs to be enabled <em>before</em> it. Whenever
|
||
debugger is entered, one can then request given number (here 16) of
|
||
past PC addresses and their (current) instructions to be shown:
|
||
<pre>
|
||
history cpu
|
||
c
|
||
[breakpoint is hit and debugger entered]
|
||
history 16
|
||
</pre>
|
||
</dd>
|
||
|
||
<dt><em>Getting instruction execution history for every breakpoint</em></dt>
|
||
<dd>
|
||
To see last 16 instructions for both CPU and DSP whenever
|
||
(a normal or tracing) breakpoint is hit:
|
||
<pre>
|
||
history on
|
||
lock history 16
|
||
c
|
||
</pre>
|
||
</dd>
|
||
|
||
<dt><em>Single stepping so that new register values are shown after each step</em></dt>
|
||
<dd>
|
||
<pre>
|
||
lock registers
|
||
s
|
||
[new register values]
|
||
s
|
||
[new register values]
|
||
...
|
||
</pre>
|
||
</dd>
|
||
|
||
<dt><em>Showing current stack contents</em></dt>
|
||
<dd>To see first 64 bytes on top of the stack, use:
|
||
<pre>
|
||
m 'a7-64'-a7
|
||
</pre>
|
||
</dd>
|
||
|
||
<dt><em>Seeing specific information each time debugger is entered</em></dt>
|
||
<dd>To see above information whenever some breakpoint is hit,
|
||
debugger is entered manually etc, write that command to e.g.
|
||
<span class="file">stack.ini</span> file and then use:
|
||
<pre>
|
||
lock file stack.ini
|
||
</pre>
|
||
Please see also <a href="#Chaining_breakpoints">Chaining breakpoints</a>
|
||
section for more examples on what can be done with the debugger input files.
|
||
</dd>
|
||
|
||
<dt><em>Adding cycle information to disassembly</em></dt>
|
||
<dd>Profiling collects cycle usage information for all executed instructions:
|
||
<pre>
|
||
profile on
|
||
c
|
||
[after a while, use AltGr+Pause to get back to debugger]
|
||
d
|
||
[or when wanting to see just the executed instructions]
|
||
profile addresses
|
||
</pre>
|
||
Please see <a href="#Profiling">Profiling</a> section for more info.
|
||
</dd>
|
||
|
||
<dt><em>Finding where a program or the OS is stuck</em></dt>
|
||
<dd>Profiling tells from which addresses CPU is executing the instructions:
|
||
<pre>
|
||
profile on
|
||
c
|
||
[after a while, use AltGr+Pause to get back to debugger]
|
||
profile addresses
|
||
</pre>
|
||
Please see <a href="#Profiling">Profiling</a> section for more info.
|
||
</dd>
|
||
|
||
<dt><em>Profiling specific function and everything it calls</em></dt>
|
||
<dd>Set breakpoint for the function entrypoint and when it is hit,
|
||
enable profiling and continue to the end of the function:
|
||
<pre>
|
||
address myfunction
|
||
c
|
||
[back in debugger at myfunction]
|
||
profile on
|
||
next subreturn
|
||
[back in debugger when function returns]
|
||
</pre>
|
||
Please see <a href="#Profiling">Profiling</a> section on and how
|
||
to save and analyze profiling information after that, and
|
||
<a href="#Stepping">Stepping</a> section for some "next subreturn"
|
||
command limitations.
|
||
</dd>
|
||
|
||
<dt><em>Seeing program callstack when breakpoint is hit</em></dt>
|
||
<dd><a href="#Caller_information">Profiler caller data</a>, shown
|
||
automatically when debugger is entered, includes callstack information
|
||
(with some limitations) if program <a href="#Debug_symbols">debug
|
||
symbols</a> have been loaded.
|
||
</dd>
|
||
|
||
<dt><em>Seeing call backtraces whenever given function is called</em></dt>
|
||
<dd>After enabling profiling and setting breakpoint for a function
|
||
symbol like this:
|
||
<pre>
|
||
profile on
|
||
b pc = _my_function :quiet :noinit :file showstack.ini
|
||
</pre>
|
||
With "showstack.ini" containing following command:
|
||
<pre>
|
||
profile stack
|
||
</pre>
|
||
Each call to "my_function" address quietly triggers a breakpoint
|
||
(without resetting profiling callstack information) which shows the
|
||
function backtrace. If this is enabled right when program starts,
|
||
those backtraces will go up to main().
|
||
</dd>
|
||
|
||
<dt><em>Seeing how program functions/symbols call each other</em></dt>
|
||
<dd><a href="#Profile_data_post_processing">Profile data
|
||
post-processing</a> can provide execution callgraphs.
|
||
</dd>
|
||
|
||
</dl>
|
||
|
||
<p>
|
||
Hint: for most of the above commands, one just needs to prefix them with
|
||
"d" (or "dsp" when using full command names) to do similar operation on
|
||
the DSP.
|
||
</p>
|
||
|
||
|
||
<h3 id="readline">Command line editing and libreadline</h3>
|
||
|
||
<p>
|
||
Hatari debugger is much nicer to use with the command line history,
|
||
editing and especially the completion support for the command, command
|
||
argument and symbol names. Like many other programs (Bash etc),
|
||
debugger uses libreadline to handle this.
|
||
</p>
|
||
|
||
|
||
<h4>Hatari building</h4>
|
||
<p>
|
||
When building Hatari, please make sure to have the GNU readline
|
||
development files installed (on Debian / Ubuntu these come from the
|
||
"libreadline*-dev" packages). Otherwise TAB completion and other
|
||
nice features do not get enabled when Hatari is configured.
|
||
</p>
|
||
|
||
|
||
<h4>Keyboard shortcuts</h4>
|
||
|
||
<p>
|
||
In addition to the normal line editing keys, libreadline supports
|
||
several keyboard shortcuts ('^' indicates Control-key):
|
||
</p>
|
||
<ul>
|
||
<li>Cursor movement (sometimes preferable over using arrow keys):</li>
|
||
<ul>
|
||
<li>^B - backward</li>
|
||
<li>^F - forward</li>
|
||
<li>^arrow - move by word</li>
|
||
<li>^A - start of line</li>
|
||
<li>^E - end of line</li>
|
||
<li>^P - previous line</li>
|
||
<li>^N - next line</li>
|
||
</ul>
|
||
<li>Cut / paste (relative to cursor position):</li>
|
||
<ul>
|
||
<li>^W - cut previous word (space separated)</li>
|
||
<li>^K - cut ("kill") to end of line</li>
|
||
<li>^U - cut to start of line</li>
|
||
<li>^Y - paste ("yank") deleted content</li>
|
||
</ul>
|
||
<li>Other text changes:</li>
|
||
<ul>
|
||
<li>^D - delete character</li>
|
||
<li>^T - transpose (switch) last 2 characters</li>
|
||
<li>^_ - undo last change</li>
|
||
</ul>
|
||
<li>Miscanellous:</li>
|
||
<ul>
|
||
<li>^R - history ("reverse") search</li>
|
||
<li>^L - clear screen and redraw line</li>
|
||
</ul>
|
||
</ul>
|
||
|
||
<p>
|
||
(Above shortcuts are inherited from Emacs text editor, and work also
|
||
in all shells using readline. See "man readline" on how to configure
|
||
them.)
|
||
</p>
|
||
|
||
</body>
|
||
</html>
|