A Quick Introduction To Using The Debugger
Ladebug supports debugging simple programs, as well as situations involving multiple threads, multiple processes, core files, kernels, and remote systems.
The official Software Product Description is part of the Developers' Toolkit Software Product Description.
Information in this documentation is subject to change without notice.
Compaq Computer Corporation makes no representations that the use of its products in the manner described in this publication will not infringe on existing or future patent rights, nor do the descriptions contained in this publication imply the granting of licenses to make, use, or sell equipment or software in accordance with the description.
COMPAQ, the Compaq logo, and the Digital logo are registered in the U. S. Patent and Trademark Office.
The following are trademarks of Compaq Computer Corporation: Alpha AXP, AXP, DEC Ada, DEC Fortran, and Ladebug.
UNIX is a registered trademark in the U.S. and other countries, licensed exclusively through X/Open Company, Ltd.. X/Open is a trademark of X/Open Company Limited.
All other trademarks and registered trademarks are the property of their respective holders.
You look for a bug by doing the following:
If the problem only occurs in optimized code, use the -g3 switch.
% cat > tmp.c
int i;
int main(int argc)
{
while (argc < 2 && i < 10000000)
i = 1; // bug, should be +=
return *((int*)1); // bug, bad memory fetch
}
% cc -g tmp.c
For instance, if your terminal is 47x80...
% stty rows 47 ; setenv LINES 47
% stty cols 80 ; setenv COLS 80
There are four basic methods for getting the debugger started on a process.
% ladebug a.out
(ladebug) stop in main
(ladebug) run
% ladebug
(ladebug) load a.out
(ladebug) stop in main
(ladebug) run
% a.out &
% ladebug a.out -pid 27859
Attached to process id 27859 ....
Type Ctrl/C to interrupt the process.
% a.out &
% ladebug
(ladebug) attach 27859 a.out
Attached to process id 27859 ....
Type Ctrl/C to interrupt the process.
The debugger outputs a prompt when it is ready for the next command from the terminal.
(ladebug) you type here
While entering commands, the Left and Right arrow keys support editing within the line. The Up and Down arrow keys recall previous commands for editing. The Enter key submits the completed line to the debugger for execution.
Lines can be continued by ending the line to be continued with a backslash (\) character.
A blank line causes the most recent valid command to get re-executed.
Two very useful commands are:
(ladebug) help
(ladebug) quit
(ladebug) source some-file-namewhich causes the debugger to read and execute the commands from some-file-name.
If you want the debugger to create the program, you have to command it to run the program, specifying if necessary any input and output redirection and arguments.
% ladebug a.out
(ladebug) run
(ladebug) run args
(ladebug) run > output-file
(ladebug) run args > output-file < input-file
Doing any of these leads to a situation akin to having attached to a running
process.
% ladebug a.out
(ladebug) run
^C
Interrupt (for process)
Stopping process localhost:27903 (a.out).
Thread received signal INT
stopped at [int main(int):5 0x120001138]
5 while (argc < 2 && i < 10000000)
% ladebug a.out
(ladebug) run arg1 arg2
Thread received signal SEGV
stopped at [int main(int):8 0x120001168]
8 return *((int*)1); // bug, bad memory fetch
% ladebug load a.out
(ladebug) stop in main
[#1: stop in int main(void) ]
(ladebug) run
[1] stopped at [int main(void):3 0x120001108]
3 int i = 0;
% ladebug a.out
(ladebug) stop in main
...
(ladebug) run
...
(ladebug) watch variable i
[#2: watch variable (write) i 0x140000170 to 0x140000173 ]
(ladebug) cont
[2] Address 0x140000170 was accessed at:
int main(int): tmp.c
[line 6, 0x12000115c] stl r2, 0(r0)
0x140000170: Old value = 0x00000000
0x140000170: New value = 0x00000001
[2] stopped at [int main(int):6 0x120001160]
6 i = 1; // bug, should be +=
(ladebug) cont
% ladebug a.out
...
Thread received signal SEGV
stopped at [int main(int):8 0x120001168]
8 return *((int*)1); // bug, bad memory fetch
(ladebug) file
tmp.c
(ladebug) # change to the same file, just to provide an example
(ladebug) file tmp.c
(ladebug) list 6:3
6 i = 1; // bug, should be +=
7
> 8 return *((int*)1); // bug, bad memory fetch
(ladebug) /return
> 8 return *((int*)1); // bug, bad memory fetch
Aliases are short-hand forms of longer commands.
This example shows using the 'W' alias, which lists 20 lines around the
current line.
(ladebug) alias W
W list $curline - 10:20
(ladebug) W
1 int i;
2
3 int main(int argc)
4 {
5 while (argc < 2 && i < 10000000)
6 i = 1; // bug, should be +=
7
> 8 return *((int*)1); // bug, bad memory fetch
9 }
(ladebug) thread
Thread State Substate Policy Priority Name
------ ---------- --------------- ---------- -------- -------------
>* 2 running throughput 11
(ladebug) show thread *
Thread State Substate Policy Priority Name
------ ---------- --------------- ---------- -------- -------------
1 ready throughput 11 default thread
-1 blocked kernel fifo 32 manager thread
-2 ready idle 0 null thread for VP 0x0
>* 2 running throughput 11
3 ready not started throughput 11
(ladebug) thread 1 Thread State Substate Policy Priority Name ------ ---------- --------------- ---------- -------- ------------- > 1 ready throughput 11 default thread (ladebug) thread Thread State Substate Policy Priority Name ------ ---------- --------------- ---------- -------- ------------- > 1 ready throughput 11 default thread
(ladebug) where 2
>0 0x120001bf8 in shared_function(my_number=0) "pthread_sample.c":70
#1 0x120001fec in workerMain(arg=0x0) "pthread_sample.c":155
(ladebug) up
>1 0x120001fec in workerMain(arg=0x0) "pthread_sample.c":155
155 shared_function(my_number);
(ladebug) w
156 }
157
158 /*
159 * shut down deterministically
160 */
161 shutDown(my_number+1);
162
163 pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, &disable);
164 }
165 }
(ladebug) down
>0 0x120001bf8 in shared_function(my_number=0) "pthread_sample.c":70
70 if (my_number == 1) function1b();
% ladebug a.out
(ladebug) run
^C
Interrupt (for process)
Stopping process localhost:28097 (a.out).
Thread received signal INT
stopped at [int main(int):6 0x120001160]
6 i = 1; // bug, should be +=
(ladebug) print i
1
(ladebug) p i+99
100
% ladebug a.out
(ladebug) run arg1 arg2
Thread received signal SEGV
stopped at [int main(int):8 0x120001168]
8 return *((int*)1); // bug, bad memory fetch
(ladebug) alias wi
wi ($curpc - 20)/10 i
(ladebug) wi
int main(int): tmp.c
[line 6, 0x120001160] br r31, 0x120001130
[line 8, 0x120001164] bis r31, 0x1, r16
*[line 8, 0x120001168] ldq_u r3, 0(r16)
[line 8, 0x12000116c] ldq_u r4, 3(r16)
[line 8, 0x120001170] extll r3, r16, r3
(ladebug) px $r16
0x1
(ladebug) printregs
$r0 [$v0] = 1 $r1 [$t0] = 3
$r2 [$t1] = 4396972783200 $r3 [$t2] = 2
...
(ladebug) $pc/10x
0x120001168: 0000 2c70 0003 2c90 04c3 4870 0d44 4890
0x120001178: 0400 4464
(ladebug) $pc/6xx
0x120001168: 2c700000 2c900003 487004c3 48900d44
0x120001188: 00000000 00000000
(ladebug) $pc/2x
0x120001168: 2c9000032c700000 48900d44487004c3
| Continue until another interesting thing happens |
|
|
| *Single step by line, but step over calls |
|
|
| *Single step to a new line, stepping into calls |
|
|
| Continue until control returns to the caller |
|
|
| *Single step by instruction, over calls |
|
|
| *Single step by instruction, into calls |
|
|
(ladebug) W
2 {
3 static int i = 0;
4 i = i+1;
5 return i;
6 }
7
8
9 int main()
10 {
11 static int j = 0;
> 12 j = j+1;
13 j = j+f();
14 j = j+1;
15 return j;
16 }
(ladebug) n
stopped at [int main(void):13 0x120001198]
13 j = j+f();
(ladebug) n 2
stopped at [int main(void):15 0x1200011c4]
15 return j;
(ladebug) run
Process has exited
[1] stopped at [int main(void):12 0x120001184]
12 j = j+1;
Information: Ladebug allows you to restart the execution of your program
from saved positions. Enter "help snapshot" for details.
(ladebug) s
stopped at [int main(void):13 0x120001198]
13 j = j+f();
(ladebug) s 2
stopped at [int f(void):4 0x120001158]
5 return i;
(ladebug) return
stopped at [int main(void):13 0x1200011a4]
13 j = j+f();
(ladebug) wi
int main(void): tmp.c
[line 13, 0x12000119c] ldl r3, -32764(gp)
[line 13, 0x1200011a0] bsr r26, f
*[line 13, 0x1200011a4] addl r3, r0, r0
[line 13, 0x1200011a8] bis r31, r31, r31
[line 13, 0x1200011ac] stl r0, -32764(gp)
(ladebug) si
stopped at [int main(void):13 0x1200011a8] bis r31, r31, r31
(ladebug) ni 3
stopped at [int main(void):14 0x1200011b4] ldl r5, -32764(gp)
In a program that does not use multiple threads, you can use snapshots to save your state before you step over the call, and then clone that snapshot to get another process positioned just before the call so you can step into it.
The following example shows a snapshot being used in this way.
% cat > call.c
int f()
{
return 0;
}
int g()
{
return f();
}
int main()
{
return g();
}
% cc -g call.c
%
% ladebug a.out
...
(ladebug) stop in g
[#1: stop in int g(void) ]
(ladebug) run
[1] stopped at [int g(void):8 0x120001158]
8 return f();
(ladebug) wi
[line 6, 0x120001154] stq r26, 0(sp)
*[line 8, 0x120001158] bis r31, r31, r31
[line 8, 0x12000115c] bsr r26, f
(ladebug) save snapshot
# 1 saved at 11:29:17 (PID: 23178).
stopped at [int g(void):8 0x120001158]
8 return f();
(ladebug) n
stopped at [int main(void):13 0x12000118c]
13 return g();
(ladebug) wi
[line 13, 0x120001188] bsr r26, g
*[line 13, 0x12000118c] ldah gp, 8192(r26)
[line 13, 0x120001190] lda gp, 28404(gp)
(ladebug) clone snapshot 1
Process 23147 cloned from Snapshot 1.
# 1 saved at 11:29:17 (PID: 23178).
[1] stopped at [int g(void):8 0x120001158]
8 return f();
(ladebug) wi
[line 6, 0x120001154] stq r26, 0(sp)
*[line 8, 0x120001158] bis r31, r31, r31
[line 8, 0x12000115c] bsr r26, f
Some precise details have been pulled out into the parallel portion of the Precise Details About Using The Ladebug Debugger section so they would not hinder the reading of this section.
However, there are things that you can do to make it easier:
The debugging information is propagated into the a.out or *.so by ld(1). It is removed by strip(1). If you strip your programs, keep the unstripped form to point the debugger at. See the ostrip -t command.
The debugging information can be very large, causing long link times, but it can also be incomplete. You should be aware of the cxx -gall and -gall_pattern switches. See the cxx(1) man page.
| Invoke debugger on an executable file | % ladebug executable_file |
| Invoke debugger on a core file | % ladebug executable_file core_file |
| Invoke debugger on the local kernel | % ladebug -k /vmunix |
| Invoke debugger and attach to running process | % ladebug -pid process_id executable_file |
To have the debugger GUI start up immediately, include the -gui switch.
% ladebug -gui
You can control your debugger process entirely through the Emacs GUD (Grand Unified Debugger) buffer, which is a variant of shell mode. All the Ladebug commands are available and you can use the shell mode history commands to repeat them.
Ladebug Version 4.0-48 and above supports GNU Emacs Version 19 and above.
The information in the following sections assumes you are familiar with Emacs and is using the Emacs notation for naming keys and key sequences.
For each Emacs session, before you can invoke the debugger, you must load the Ladebug-specific Emacs lisp code, as follows:
M-x load-fileAt the Load file: prompt, type:
/usr/lib/emacs/lisp/ladebug.el
You can also place a load-file call in your emacs initialization file (~/.emacs). For example:
(load-file "/usr/lib/emacs/lisp/ladebug.el")To start Ladebug with Emacs, type:
M-x ladebugThe following invocation line displays:
Run ladebug (like this): ladebugEdit the invocation line by typing the target program and pressing Return. Emacs remembers the invocation. To debug the same program again, you need only press Return.
Emacs displays the GUD buffer and runs Ladebug within it; Ladebug will typically start up and display its (ladebug) prompt indicating readiness. The GUD buffer saves all of the commands you type and the program output for you to edit. In general, interact with Ladebug in the GUD buffer as you would with a Ladebug started from a shell.
One of the benefits of running Ladebug under Emacs is to get tighter correlation between program execution with source. When your program stops, for example at a breakpoint, Emacs displays the source of your program in a second buffer (source buffer) and indicates the current execution line with =>.
NOTE: If you have the source already loaded into a buffer, Emacs often finds that buffer. However, in some NFS mounting situations, Emacs may use an alternate name for some directories and will create a second buffer for your source (often with <2> appended to the name). Care should be exercised to either avoid modifying the original buffer, or perhaps killing it outright.
By default, Emacs sets its current working directory to be the directory containing the target program. Ladebug does not do this when invoked directly, therefore you may need to change the source code search path when using Ladebug from within Emacs. To set an alternate source code search path, type the use command with a directory argument, for example:
(ladebug) use /usr/prog/test
All Emacs editing functions and GUD key bindings are available. For example:
C-x SPC
M-x infoThen select menu Emacs, then menu Debuggers.
To start the debugger from within CDE and bring your program under debugger control:
Alternatively, you can drag an executable file from the CDE File Manager onto the Ladebug icon in the Developer's Toolkit group to start the debugger using that executable.
If you do not specify a file to debug, click on OK and the debugger displays the Main Window. The Main Window remains empty until you bring a program under debugger control. Upon startup, the debugger executes any user-defined initialization file.
% ladebug -prompt ">> " sample >> quitThe prompt can also be changed by setting the $prompt debugger variable.
Each line from stdin is copied to the record input file (described later), if you have requested that file.
Each line is scanned from the beginning, looking for backslash ('\') characters which 'quote' their immediate following character. If the line ends in a quoted newline, then another line is similarly processed from stdin and appended to this one, with the quoted newline removed.
When processing stdin, and stdin is a terminal, and the $editline is 1 (the default, see the set command to change it), Ladebug supports command line editing. For this to work correctly, you must have the terminal width set to the right value.
Whether or not command line editing is enabled, you can always use your terminal's cut-and-paste to save typing while entering input.
If the line begins with an '!' character, then:
If the line begins with an '^' character, then:
If there are no formals, this match consumes no more of the input.
Again, the characters within strings are not tested.
Nesting is caused by '(' and ')' characters outside of strings.
Nested expansion is currently badly broken, but it should work as follows:
The expansion is processed in the same way as the assembled line, except that the alias is undefined during the processing.
After alias expansion, the resulting assembled line is lexed and parsed as Ladebug commands.
Leading comments are left until later, because completely blank lines have special significance.
As Ladebug starts tokenizing a line, it starts processing the characters using the LKEYWORDS state. It uses the rules for lexical tokens in this state, recognizing the longest sequence of characters that forms a lexical token.
Once the lexical token is recognized, Ladebug appends it to the tokenized form of the line, perhaps changes to another lexical state, and starts on the next token.
input : command_list | comment | repeat_last_commandA command_list is a sequence of commands that are executed one after the other.
command_list : command ; command_list | command semicolon_optA comment is a line beginning (possibly after white-space) with a '#' character. Any characters after the '#' are ignored.
comment : #A repeat_last_command is a line with nothing on it at all.
repeat_last_line_of_command :
A command is one of:
command : quit_command | help_command | guion_command | braced_command_list | dbgvar_command | execute_commands_from_file | record_command | history_command | alias_command | execute_shell_command | load_command | unload_command | run_command | kill_command | environment_variable_command | attach_command | detach_command | multiprocess_command | kernel_debugging_command | reflex_command | browse_source_command | thread_command | call_stack_command | look_around_command | machinecode_level_command | shared_library_command | modifying_command | continue_command | snapshot_command | ladebug_internal_command
quit_command : quitExample:
(ladebug) quit
help_command : help | help the rest of the line is the help keyExample:
(ladebug) help
displays a summary of how to use ladebug
(ladebug) help ladebug
displays the main function-oriented commands
(ladebug) help step
displays help on the step command
For example:
% ladebug -gui
guion_command : guiExample:
(ladebug) guiThe GUI can be shut down from the GUI window, and the rest of the debugger does not shut down.
The GUI can be restarted after it has been shut down.
semicolon_opt : ; | nothing at all string_opt : string | nothing at all name_list : identifier_or_typedef_name , name_list | identifier_or_typedef_nameExample:
(ladebug) alias A(Name1,Name2,Name3) "p Name1,Name2,Name3"
In debugger commands, the identifierss in the following list are treated as keywords unless they are somewhere within parentheses:
Example:
(ladebug) print (3*thread + 4)
It is possible to surround a command_list with braces to make them a single command. There are places in the grammar that require a braced_command_list just for readability, or to assist the debugger in understanding your input.
braced_command_list : { command_list } braced_command_list_opt : braced_command_list | // nothing at all
Debugger variables have three classes: