Information in this page is for Use By All Ladebug Customers

Index

What is the Ladebug Debugger?
About This Documentation
Getting a Ladebug Kit
Reporting Problems with Ladebug

A Quick Introduction To Using The Debugger

A Guide To Using The Debugger Precise Details About Using The Ladebug Debugger

What is the Ladebug Debugger?

Ladebug is a debugger supporting C, C++, and Fortran programming on It also has limited support for Cobol and Ada.

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.

About This Documentation

This documentation teaches how to use the Ladebug debugger, and also is a reference manual for the product.

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.

Getting a Ladebug Kit

Ladebug kits are available from the following sources.

Reporting Problems with Ladebug

Report problems with Ladebug via: Make sure you include the following details: The Ladebug team can use ftp to fetch sources and executables provided you can place them in an anonymous FTP area. In other cases, there are other, more inconvenient, methods they may ask you to use.

A Quick Introduction To Using The Debugger

This section is intended to provide all the information needed to make simple use of the debugger.

You look for a bug by doing the following:

  1. Find a repeatable reproducer of the bug - the simpler the reproducer is, the simpler the following steps will be to do.

  2. Prepare your program for debugging

  3. Start the debugger

  4. Give commands to the debugger

  5. Do whatever it takes to reproduce the bug, so that these reflexes will stop the process close to where the bug has caused something detectably wrong to happen.

  6. Look around to determine if the bug

  7. If the bug is in code where the debugger has stopped the process, fix it.

  8. If the bug is in code executed later, remove any reflexes that are triggering too often, create other reflexes that work better at getting near the problem, and continue the process.

  9. If the bug has already happened, you would like to take the same steps of creating reflexes etc., except with the process running backwards. Unfortunately, reverse execution is a hard problem (how do you un-erase that disk?) so the compilers and the Ladebug debugger don't support it. Instead, you have to rerun from an earlier position (a snapshot if you made one, or else the beginning of the program), first creating reflexes that stop the process sooner.

How to prepare a program for debugging

Compile and link your program using the -g switch.

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

Start the debugger

Before you start the debugger, you should make sure that you have correctly set the size information for your terminal, otherwise the command line editing support may do strange things.

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.
  1. Have the debugger create your process, using the shell command line to tell the debugger which executable to run.
        % ladebug a.out
        (ladebug) stop in main
        (ladebug) run
    
  2. Have the debugger create your process, using the debugger commands to tell the debugger which executable to run.
        % ladebug
        (ladebug) load a.out
        (ladebug) stop in main
        (ladebug) run
    
  3. Have the debugger attach to a running process, using the shell command line to tell the debugger which process and also which executable file that process is running.
        % a.out &
        % ladebug a.out -pid 27859
        Attached to process id 27859  ....
    
    Type Ctrl/C to interrupt the process.

  4. Have the debugger attach to a running process, using the debugger commands to tell the debugger which process and also which executable file that process is running.
        % a.out &
        % ladebug
        (ladebug) attach 27859 a.out
        Attached to process id 27859  ....
    
    Type Ctrl/C to interrupt the process.

Giving commands to the debugger

You give commands to the debugger by typing them in a terminal window, in a debugger window, to the ladebug GUI, or by placing them in text files and commanding the debugger to execute those text files.

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

Scripting or redoing previous commands

Another useful command is:
    (ladebug) source some-file-name
which causes the debugger to read and execute the commands from some-file-name.

Getting debuggable processes running the program

As was shown above, you can tell the debugger either how to create the process, or which existing process to attach to.

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.

Pausing the process at the site of the visible problem

Once your process is running, and the debugger has been attached to it, the four most common ways to pause the process are:

Examining the paused process

Looking at the sources

You can Here is an example
    % 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 }

Looking at the threads (Tru64 Only)

In a multi-threaded application, you can find out about the thread that stopped, all the threads, and change to look more closely at a different thread.
	
    (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       

You can select any thread to be the focus of commands that show things. For example:
	
(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

Looking at the call stack

You can examine the call stack of any thread. Even if you aren't using threads explicitly, your process will have one thread to run your code. You can move up and down the stack, and examine the source being executed at each call.
    (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();

Looking at the data

Changing back to our simple example, we can also look at the variables, and evaluate expressions involving them. This example also shows the p alias for print.
    % 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

Looking at the signal state

The debugger shows you the signal that stopped the thread.
    % 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

Looking at the generated code, etc.

You can print memory as instructions or as data, and you can examine registers.
    (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

Continuing executing the process

Once you are satisfied that you understand what is going on, especially if the process has just reached a breakpoint that you placed, you usually want to inch the process forward and see what happens. The following table shows aliases and commands you can use to specify where you want to go.

Desired Behavior
Alias
Command
Continue until another interesting thing happens
c
cont
*Single step by line, but step over calls
n
next
*Single step to a new line, stepping into calls
s
step
Continue until control returns to the caller
return
*Single step by instruction, over calls
ni
nexti
*Single step by instruction, into calls
si
stepi
*Each of these commands can take a repeat count.

	(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)

Snapshots as an undo mechanism

Often when you inch the process forward as just described, you will accidentally go too far. For instance, you may step over a call that you should have stepped into.

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.

  1. Here is the program being built.
        % cat > call.c
        int f()
        {
            return 0;
        }
    
        int g()
        {
            return f();
        }
    
        int main()
        {
            return g();
        }
    
        % cc -g call.c
        %
        % ladebug a.out
        ...
    
    
  2. The next stage is to get just before the call, and take a snapshot. You can see we are just before the call because the asterix shows that the thread is about to execute the call (the bsr instruction in the instruction code listing is the branch to subroutine).
        (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();
    
    
  3. We now step over the call using the n alias for the next command. The execution is now AFTER the bsr, shown by the asterix being on the following ldah instruction.
        (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)
    
    
  4. Oh, how we wish we hadn't done that - no problem, just clone that snapshot we made.
        (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();
    
    
  5. and, hey-presto, we are back before the call was executed. We have actually changed to a different process to do this. fork() was used both to create the snapshot, and to clone it.
        (ladebug) wi
         [line 6, 0x120001154]  stq     r26, 0(sp)
        *[line 8, 0x120001158]  bis     r31, r31, r31
         [line 8, 0x12000115c]  bsr     r26, f
    
    

A Guide To Using The Debugger

This section is intended to provide most of the information needed to make expert use of the debugger.

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.

Preparing a program for debugging

Things you should do in your source code

No changes are needed to the source in order to debug the program.

However, there are things that you can do to make it easier:

Things you should have the compiler and linker do

Debugging information is put into .o files by compilers. The level of information is controlled by compiler switches. See the man page for your compiler. The switch is probably -g.

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.

Start the debugger

The three ways you can start the debugger are from:

Starting the debugger from a shell

When you invoke the debugger from a shell, you can bring a program, core file, or local kernel under debugger control, or attach to a running process.

Function
Command [TBD:need to links items in this column to description section when added]
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

Starting the debugger from within Emacs

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-file 
At 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 ladebug
The following invocation line displays:
	Run ladebug (like this): ladebug	 
Edit 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:

For more information on Emacs functionality and key bindings, refer to Emacs documentation. For example:
	M-x info
Then select menu Emacs, then menu Debuggers.

Starting the debugger from within CDE (Tru64 systems only)

TBD: DETERMINE HOW to ACCESS THE NEW LADEBUG GUI FROM WITHIN CDE
The debugger is integrated with the common UNIX desktop user environment CDE (Common Desktop Environment). For more information about CDE, see the CDE User's Guide.

To start the debugger from within CDE and bring your program under debugger control:

  1. From the CDE Front Panel, click on the Application Manager icon.
  2. From the Application Manager icon, double click on the Developer's Toolkit icon. You can move icons from the Developer's Toolkit into another group or to the front of the Control Panel depending on your preference.
  3. From the Developer's Toolkit group, double click on the Ladebug icon. To access the Ladebug release notes, double click on the README.Ladebug icon.
  4. When you double click on the Ladebug icon, a dialog box appears in which you can type the name of the executable file you want to debug. You can drag an executable file from the CDE File Manager into the dialog box space for file name .

    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.

  5. Bring a specified program under debugger control by using the following steps: TBD
You can now debug your program. . . .

Specifying a debugger prompt

You can specify a debugger prompt when you start the debugger from a shell with the -prompt switch. The default prompt is (ladebug).
	% ladebug -prompt ">> " sample
	>> quit
The prompt can also be changed by setting the $prompt debugger variable.

Giving commands to the debugger

Ladebug has several different mechanisms you can use to direct its behaviour. It gets input from:

Ladebug's command processing structure

The debugger
  1. prompts for input
  2. gets a complete line from the input file, and performs
  3. parses the entire line according to the parsing rules for the current language
  4. executes the commands.

Interrupting a debugger action

To interrupt program execution or to abort a debugger action, type Ctrl/C. This will return the debugger to the prompt.

Entering and editing command lines

Ladebug reads lines from stdin. If stdin is from a terminal, you may have edited the line before feeding the whole line to Ladebug by pressing Enter.

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.

Note: While using Up and Down, Ladebug skips duplicate commands. To see a complete list of commands you have entered, use the history command.

Whether or not command line editing is enabled, you can always use your terminal's cut-and-paste to save typing while entering input.

History replacement of the line

Leading spaces and tabs are removed from the assembled line.

If the line begins with an '!' character, then:

If the line begins with an '^' character, then:

The assembled line is now appended to the history list.

Alias expansion of the line

The assembled line is now subjected to alias expansion.
  1. This is done by scanning the line, looking for the ';' and '{' characters that are not inside strings. Strings are recognized by their opening and closing double or single quotes. Backslash quotation causes a quote character not to terminate the string.
  2. At the beginning of the line, and immediately after such ';' and '{' characters, the debugger checks for the occurrence of an alias identifier.
  3. If it finds one, it associates the formals of the alias with the specified actuals.

    If there are no formals, this match consumes no more of the input.

      1. If there are actuals, white space is skipped, and then a '(' character is checked for and skipped. The characters following the '(' up to the first non-nested ',' character or ')' are associated with the formal parameter.

        Again, the characters within strings are not tested.
        Nesting is caused by '(' and ')' characters outside of strings.

      2. If there are more formals, the ',' character must be the terminator, it is skipped, and processing continues as for the first parameter.
    1. Once the alias and the correct number of actuals have been identified, all the characters from the start of the alias identifier to its end (no parameters) or the trailing ')' (one or more parameters) are replaced by the expansion.
    2. Within the definition of the alias, all occurrences of the formal parameter are replaced by the actual, regardless of whether or not it is in a string.

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.

Comments

Somewhere in the midst of this processing, trailing comments are stripped out. Trailing comments start with a '#' character that is not the first non-space character on a line, and continue to the end of the line.

Leading comments are left until later, because completely blank lines have special significance.

The syntax of commands

Ladebug has different parsing rules for all of the different languages it supports. The whole line is processed according to the current language, even if executing the line will change the current language.

The lexical elements of commands

As well as depending on the current language, the conversion of the characters into lexical tokens also depends where they are in the command they are part of.

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.

The grammar of commands

Each command line must parse as one of:
    input
        : command_list
        | comment
        | repeat_last_command

A command_list is a sequence of commands that are executed one after the other.
    command_list
        : command ; command_list
        | command semicolon_opt

A 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
        :

The general categories of commands

Commands usually start with, and contain, keywords. These keywords must be lower case.

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

Ending a debugging session

To exit the debugger, type quit at the prompt.
    quit_command
        : quit
Example:
	(ladebug) quit

Getting help

To get help about debugger commands, use the help command.
    help_command
        : help
        | help the rest of the line is the help key
Example:
	(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 

Ladebug's GUI (Tru64 systems only)

The GUI is operational at the same time as the terminal-based command line input. The GUI can be started either:
    guion_command
        : gui
Example:
	(ladebug) gui
The 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.

The names and expressions within commands

There are some pieces of the grammar that don't belong to any particular piece:
    semicolon_opt
        : ;
        | nothing at all 

    string_opt
        : string
        | nothing at all 

    name_list
        : identifier_or_typedef_name , name_list
        | identifier_or_typedef_name
Example:
    	(ladebug) alias A(Name1,Name2,Name3) "p Name1,Name2,Name3"
Debugger keywords

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)

Using braces to make a composite command

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

Debugger variables are pseudo-variables that exist within the debugger rather than within your program. They are used to:

Debugger variables have three classes:

User-defined variables Created by you and can be set to a value of any type.
Preference variables Can be modified by you to change debugger behavior. The variable can only be set to a value within the known range of valid values for that particular variable.
Display/state variables Give the current debugger state and are not able to be modified by you.

The commands that specifically deal with these variables are:

 dbgvar_command
        : set identifier_or_typedef_name = expression
        | set identifier_or_typedef_name
        | set 
        | unset identifier_or_typedef_name

The identifier_or_typedef_name should not exist anywhere in your program, or you may confuse yourself about which of the occurrences you are actually dealing with.

The predefined debugger variables all start with a dollar sign ($), to help avoid this confusion.

The set identifier_or_typedef_name = expression form creates the debugger variable if it doesn't already exist. It then sets the value of the debugger variable to the result of evaluating the expression.

        (ladebug) set $myLoopCounter = 0
	(ladebug) print $myLoopCounter
	0
The set identifier_or_typedef_name form is equivalent to the command set identifier_or_typedef_name = 1.
        (ladebug) print $stoponattach
	0
	(ladebug) set $stoponattach
	(ladebug) print $stoponattach
	1		

The set form shows all the debugger variables and their values.

        (ladebug) set      
	$ascii = 0
        $beep = 1
        $catchexecs = 0
        $catchforkinfork = 0
	.
	.
	.        

The unset form deletes the debugger variable. Some predefined debugger variables either can't be deleted, or are automatically recreated in the future when needed.

        (ladebug) unset $myLoopCounter  
	(ladebug) print $myLoopCounter
	Symbol "$myLoopCounter" is not defined.
	Error: no value for $myLoopCounter 

Scripting or redoing previous commands

Some tips for scripting or redoing previous commands:
    execute_commands_from_file
        : source filename
        | playback input filename

The following example shows how to execute a debugger script.
	(ladebug) source myscript
	[#1: stop in main ] 
	[1] stopped at [main:4 0x120000b14] 
 	      4     for (i=1 ; i<3 ; i++) { 
	stopped at [main:5 0x4001d4] 
	      5         f = factorial(i); 

recording input and output

To assist you in making such files, as well as to assist you in seeing what has happened before, the debugger can write both its input and its output to files. The output is still also written to stdout (normal output) or stderr (error messages).
    record_command
        : record input  filename_opt
        | record output filename_opt
        | record io     filename_opt

Use record input to save all the debugger commands to a file. The commands in the file can be executed using the source command or the playback input command. The following example shows how to use the record input command to record a series of debugger commands in a file named myscript.

	(ladebug) record input myscript
	(ladebug) stop in main 
	[#1: stop in int main(void) ]
	(ladebug) run
	[1] stopped at [int main(void):12 0x120001130]
     	     12 int i;
	(ladebug) quit
	% cat myscript 
	stop in main 
	run
	quit

The record output command saves all debugger output to a file. The output is simultaneously written to stdout (normal output) or stderr (error messages).
For example:

	(ladebug) record output myscript
	(ladebug) stop in main
	[#1: stop in int main(void) ]
	(ladebug) run
	[1] stopped at [int main(void):12 0x120001130]
             12 int i;
	(ladebug) next
	stopped at [int main(void):13 0x120001138]
     	     13 {
	(ladebug) quit
	% cat myscript
	(ladebug) [#1: stop in int main(void) ]
	(ladebug) [1] stopped at [int main(void):12 0x120001130]
	     12 int i;
	(ladebug) stopped at [int main(void):13 0x120001138]
	     13 {

The record io command saves both input to and output from the debugger.
For example:

	(ladebug) record io myscript
	(ladebug) stop in main
	[#1: stop in int main(void) ]
	(ladebug) run
	[1] stopped at [int main(void):12 0x120001130]
     	     12 int i;
	(ladebug) quit
	% cat myscript	
	(ladebug) stop in main
	[#1: stop in int main(void) ]
	(ladebug) run
	[1] stopped at [int main(void):12 0x120001130]
     	     12 int i;
	(ladebug) quit

To stop recording debugger input or output, redirect to /dev/null:

	(ladebug) record input /dev/null
    	(ladebug) record output /dev/null

History

You can see all the commands you have already entered by using the history_command. The INTEGERconstant is how far back, starting at the most recent, to show. If you don't specify it, 20 are shown.
    history_command
        : history
        | history INTEGERconstant
For example:
	(ladebug) history 4 
	4: step 
	5: where
	6: cont 
	7: history 4 

User-defined commands - aliases

You can extend the set of debugger commands by defining aliases.

When the debugger is tokenizing a command line, it expands aliases and then retokenizes the expansion.

    alias_command
        : alias alias_name string
        | alias alias_name identifier_or_typedef_name
        | alias alias_name ( name_list ) string
        | alias alias_name
        | alias
        | unalias identifier_or_typedef_name

    alias_name
        : identifier_or_typedef_name
The following example shows how to define and use an alias.
	(ladebug) alias cs 
	alias cs is not defined 
	(ladebug) alias cs "stop at 5; run"
	(ladebug) alias cs
	cs      stop at 5; run 
	(ladebug) cs
	[#1: stop at "sample.c":5 ] 
	[1] stopped at [main:5 0x120000b1c] 
	5         f = factorial(i); 

The following example modifies the cs alias which was defined in the previous example to specify the breakpoint's line number when you type the abbreviated alias command.

	(ladebug) alias cs(x) "stop at x; run" 
	(ladebug) cs(5) 
	[#1: stop at "sample.c":5 ] 
	[1] stopped at [main:5 0x120000b1c] 
	5         f = factorial(i); 
	

Executing shell commands

You can have the debugger execute a call to the UNIX system function. This function is documented in the man pages, see % man 3 system. The call results in a shell executing the words you specify.
    execute_shell_command
        : sh words
For example:
	(ladebug) sh ls -l sample.c 
	-rw-r----- 1 Ladebug        259 May 15 13:08 sample.c
You can spawn a shell from the debugger by issuing:
	(ladebug) sh sh 
	
NOTE: dbx allows just:
	(dbx) sh
and so should we!

Getting debuggable processes running the program

Often running the program in a process just requires forking() a process and exec()'ing the program within it with the right environment variables, argc/argv, file descriptors, etc. This is what usually happens when you run your program from a shell command line.

However, sometimes the program requires a lot more context, or may already have been created. Perhaps it is part of a pipe, or perhaps it is a long running process, or perhaps it is created from a shell script or makefile.

Hence, the following situations are possible:

load and run

Using either the shell command line that started the debugger, or the load_command, you can tell the debugger about an executable file that you intend to create a process executing. The load_command reads the symbol table information of an executable file and optionally, a core file. After loading an executable file, use the run_command to start program execution.
    load_command
	: load filename filename_opt
For example:
	% ladebug factorial
or:
    	(ladebug) load factorial
The opposite of loading an executable file is:
    unload_command
	: unload pid_list
	| unload filename
The unload command removes all related symbol table information that the debugger associated with the process being debugged, specified by either a process ID or executable file.
For example:
	(ladebug) unload 13497
	Process has exited

Once you have loaded a program, you can create a process executing this program using either of the following forms of the run_command.

    run_command
        : run   arglist io_redirection
        | rerun arglist io_redirection
Creating a process both creates the debugger's knowledge of it, and makes it the current process that the debugger is controlling. For example:
	(ladebug) run -s > prog.output
	Thread has finished executing
	(ladebug) stop in main
	#1: stop in main
	(ladebug) rerun
	(1) stopped at [main:4 0x1200011c0]
	      4         for (i=1 ; i<3 ; i++) { 

Killing the current process:

    kill_command
        : kill
Killing a process leaves the debugger running. Any reflexes previously set are retained. You can later rerun the program. For example:
	(ladebug) kill
	Process has exited

The arglist provides both the argc and argv for the created process.

The debugger does its own breaking up of the arglist into words, and does not do the various environment variable substitution, wildcarding, etc. that a shell might.

    arglist
        : words
        | // nothing at all
The io-redirection allows you to change stdin, stdout, and stderr - which are otherwise inherited from the debugger process.
    io_redirection
        : io_redirection <  filename
        | io_redirection >  filename
        | io_redirection 1> filename
        | io_redirection 2> filename
        | io_redirection >& filename
        | // nothing at all
The various forms have the same effect as in the shell. Although the grammar currently allows more than the following forms of redirection, you should only use the following because we may fix the grammar in the future.
     > filename               Redirect stdout
    1> filename               Redirect stdout
    2> filename               Redirect stderr
    >& filename               Redirect stdout and stderr
    1> filename 2> filename   Redirect stdout and stderr to different files

attach

If a process already exists, you can have the debugger attach to it by:
    attach_command
        : attach pid filename_opt
The process is specified by its pid.
    pid
        : INTEGERconstant
Example:
    attach 12345 a.out
The filename_opt, if specified, must be an executable file that the process is executing, or a copy of it, or an unstripped copy of it.

Attaching to a process both creates the debugger's knowledge of it, and makes it the current process that the debugger is controlling.

The opposite of attaching to a process is detaching from a process. The process is left to run by itself, and the debugger forgets everything it ever knew about it.

    detach_command
        : detach pid_list
    pid_list
        : pid
        | pid , pid_list
        | // nothing at all
Example:
    detach 12345,789

Controlling the process environment

You can set and unset the environment variables for future created processes. creating an environment that is different from the environment of the debugger and also different from the shell from which the debugger was invoked.

The environment commands have no effect on the environment of the currently running process.

NOTE: The environment commands do NOT change or show the environment variables of the current process, they only reference those that will be used when a new process is created.

    environment_variable_command
        : show_environment_variable_command
        | set_environment_variable_command
        | unset_environment_variable_command
To print either all the environment variables that will be set when the process is run, or a specific one, use a show_environment_variable_command.
    show_environment_variable_command
        : printenv
        | export
        | setenv
        | printenv environment_variable_name
Note: export and setenv are synonyms.

To add or change an environment variable that will be set when the process is run, use a set_environment_variable_command. If the environment_variable_value is not specified, the environment variable has its value set to "".

    set_environment_variable_command
        : export environment_variable_name
        | setenv environment_variable_name
        | export environment_variable_name = environment_variable_value
        | setenv environment_variable_name   environment_variable_value

    environment_variable_name
        : identifier_or_typedef_name
Although not obvious from the syntax, a environment_variable_value is usually a string literal.
    environment_variable_value
        : filename
Example:
        (ladebug) setenv LD_LIBRARY_PATH "/usr/proj/libraries"
        (ladebug) printenv

To remove an environment variable, use an unset_environment_variable_command.

    unset_environment_variable_command
        : unsetenv environment_variable_name
        | unsetenv *
NOTE: There is no way of simply getting back to the initial state when the debugger started.

Multiprocess debugging

The debugger can know about more than one process at a time. The debugger knows about each process for one of three reasons: At any one time, only one of the processes the debugger knows about can be controlled by the debugger. The rest are stalled, and you have to switch the debugger to the one you want, idling the one it was controlling, to work with one of the other processes.
    multiprocess_command
        : show_process_command
        | switch_process_command

You can show the processes the debugger knows about.

    show_process_command
        : show process
        | show process *
        | process
Example:
    (ladebug) show process *

Both the run_command and the attach_command automatically switch the debugger to the process they operate on.

You can explicitly command the debugger to control a different process.

    switch_process_command
        : process pid
        | process filename
The process you are switching away from is left stalled until either the debugger exits, or you switch to it and start it moving again.

The following example creates two processes and switches from one to the other and back again:

	%ladebug
	Welcome to the Ladebug Debugger Version 4.0-n
	(ladebug) process
	There is no current process.
	You may start one by using the `load' or `attach' commands.
	(ladebug) load eq
	(ladebug) process
	>localhost:20866 (eq) loaded.
	(ladebug) show process *
	>localhost:20866 (eq) loaded.
	(ladebug) load factorial
	(ladebug) show process *
 	localhost:20866 (eq) loaded.
	>localhost:8966 (factorial) loaded.
	(ladebug) process 20866
	(ladebug) process
	>localhost:20866 (eq) loaded.
 	localhost:8966 (factorial) loaded.
	  

The debugger variables $childprocess and $parentprocess can also be specified in place of the process ID. The debugger automatically sets these variables when an application forks a child process.

Processes that use fork()

Ladebug contains the following predefined variables that you set for debugging a program that forks. By default, the settings are turned off. When activated, the settings apply to all processes you debug.

When a fork occurs, the debugger automatically sets the debugger variables $childprocess and $parentprocess to the new child or parent process ID. All examples in this section assume these variables are set.

In the following example, the debugger notifies you that the child process has stopped. The parent process continues to run.

	(ladebug) run
	Process 200 forked.  The child process is 201.
1	Process 201 stopped on fork.                             
2	stopped at [void main(void):14 0x120001248]              
             14   if ((pid=fork()) == 0)
3	Process has exited with status 18                        
	(ladebug) show process *                               
4	>localhost:200 (mp-fork) dead.
	\_localhost:201 (mp-fork) stopped.
	(ladebug) 
  1. Indicates that the child process has stopped.
  2. Tells where the child process stopped.
  3. Indicates that the parent process, which was not stopped, has completed execution.
  4. Shows that the child process (process 201) has stopped and the parent process has completed execution. The parent process (process 200) remains in the current context, as indicated by the arrow (>).

The following example shows changing the process. Listing the source code shows the source for the child process.

1	(ladebug) process 201               
	(ladebug) show process *
	localhost:200 (mp-fork) dead.
2	>  \_localhost:201 (mp-fork) stopped.               
        (ladebug) process
        Current Process: localhost:201 (mp-fork).
3        (ladebug) list                                      
        15     {
        16       printf("about to exec\n");
        17       execlp("./c_whatis", "./c_whatis", NULL);
        18       perror(" execve failed.");
        19     }
        .
	.
	.
                 
  1. The current process context is changed to the child process (process 201).
  2. The arrow indicates process 201 is the current process.
  3. The debugger lists the source code for the current process. Note that it lists around the line where the parent process forked.

You can continue to debug the current process (the child process). When the child process finishes, you cannot rerun the child process. By setting the process context to the parent process, you can rerun the program, as shown in the next example.

1	Process has exited with status 0 
	(ladebug) show process *
	localhost:200 (mp-fork) dead.
	>  \_localhost:201 (mp-fork) dead
2	(ladebug) rerun
	Error: cannot restart existing process.
3	(ladebug) process 200 
4	(ladebug) rerun 
	Process 200 forked.  The child process is 201.
	Process 201 stopped on fork.
	stopped at [void main(void):14 0x120001248]
             14   if ((pid=fork()) == 0)
	in parent process
	Process has exited with status 18
	(ladebug)
  1. Indicates that the child process has finished executing.
  2. You cannot rerun the child process.
  3. Setting back to the parent process (process 200), you can now rerun.
  4. The program reruns; a new child process is created.

TBD - examples - parent and child processes stopped,
switching to parent process

Processes that use exec()

Ladebug contains the following predefined variable that you set for debugging a program that execs. By default, the settings are turned off. When activated, the settings apply to all processes you debug.

$catchexecs--When set to 1, this variable instructs the debugger to notify you and stop the program when a program execs.

TBD - example - debugging a process that execs,
setting breakpoints in a process that execs

Remote debugging

This section describes debugging programs running on remote systems. A remote debugger consists of a server running on the target system and a client (the debugger) running on the host system. Once connected to the target system, you use Ladebug to debug your program in the same way you debug your programs running locally.

For a detailed description of remote debugging, see Remote Debugging in Precise Details About Using the Ladebug Debugger.

Writing a remote server

TBD

For a detailed description of writing a remote server, see Writing a Remote Server in Precise Details About Using the Ladebug Debugger.

Kernel debugging

Kernel process status
 kernel_debugging_command
        : KPS

Remote kernel debugging

TBD

Getting to the site of the visible problem

You usually want the process being debugged to run up to the point where it does something interesting and then to have the debugger help you debug the program. For simple bugs, it is easy to describe the situation when you want the debugger to stop. For instance "the first time traverse is called", or "the first time a divide_by_zero occurs". However the interesting situations often require fairly complex descriptions, or even human involvement, to recognize.

Often the action you would like the debugger to take when something interesting happens is simply to stop the process and have the debugger wait for you to give it new instructions. But other times you may just want to trace the behaviour of your program, or even to patch around one bug so that you can continue looking for another without rebuilding your program.

The Ladebug feature that allows you to do any or all of these is called reflexes. Reflexes include the traditional concepts of breakpoints, watchpoints, signal catchers, etc., as well as other capabilities.

Interrupting the running process

TBD

^C

TBD

Default reflexes

TBD

Other reflexes

TBD

The reflexes and their specification

The debugger has tables of reflexes that it uses while controlling the process. The tables are switched when the process executes an exec() call as described here, and the one currently being used is called the current reflex table.

There are Ladebug commands to create reflexes, delete reflexes, and enable and disable them. A deleted or disabled reflex never slows down the execution of the process. If you will want the reflex again in the future, it is easier to disable the reflex rather than delete it now and create another like it.

    reflex_command
        : show_all_reflexes_command
        | create_reflex_command
        | replace_reflex_command
        | delete_reflex_command
        | disable_reflex_command
        | enable_reflex_command
        | catch_signal_reflex_command
        | ignore_signal_reflex_command
You can see all the reflexes in the current reflex table.
   show_all_reflexes_command
        : status
For example:
	(ladebug) status
	#1 PC==-x120001180 in main "sample.c":4 { break }
	#2 (at Proc entry and if $trace0!=*0x11fffe48){trace-expr i;set $trace0=}
	#3 if $trace1!=*0x11ffffe48 { trace-expr i; set $trace = *0x11ffffe48; }
Within each table the reflexes are numbered. You can get the effect of named reflexes by using debugger variables to hold the reflex numbers.
    reflex_number
        : expression

    reflex_number_list
        : *
        | reflex_number
        | reflex_number , reflex_number_list
Example:
        (ladebug) delete *
        (ladebug) delete 1,3,5
Each reflex consists of the following aspects:

Before the debugger allows the process to make any progress, the debugger analyzes the detectors of the enabled reflexes of the current table, and creates a set of sensors in the process. Sometimes there are no sensors that will work to trigger the detectors and the debugger has to single-step the process, but usually the debugger can just install these sensors and let the process run until it triggers one of them. When that happens, the process will stall and the debugger will regain control.

The sensors include:

As far as possible, the debugger hides these sensors from your running process, but if your process reads the instruction memory it will see the special instructions. Also the changes in the memory protection are sometimes visible even though the debugger intercepts the SIGSEGV's and single-steps the faulting instructions with the memory protection restored.

Of course, all sensors do slow down the execution of the process, if they are being triggered.

On a multi-cpu system, it is possible for several of these sensors to trigger simultaneously, but the operating system and the debugger serialize them so only one is considered at a time.

The triggering of a single sensor can mean that the detectors of several enabled events are all triggered. The debugger forms the set of all the reflexes whose detectors are triggered. It can do this, because the process is not changed by the checking of the detectors.

WARNING - THE DEBUGGER DOES NOT CURRENTLY WORK EXACTLY AS DESCRIBED

The reflex_conditions of each of these sensors are then evaluated in an undefined order. The evaluation may involve executing calls into the process, which may in turn trigger sensors and recursively invoke the reflexes, which may in turn result in the deletion or disabling of reflexes in the table. Even if this happens, the reflex stays in the set and still participates in the remaining steps. If the reflex_condition evaluates to zero, the reflex is removed from the set.

Each of the 'continue' disposition reflexes in the set then has its actions executed. Again there is potential for recursively invoking the reflex mechanism.

Each of the 'break-to-user' disposition reflexes in the set then has its actions executed. Again there is potential for recursively invoking the reflex mechanism.

Lastly, if there were any 'break-to-user' disposition reflexes, or if any of the actions executed a `break-to-user' command, then control is returned to the invoker of this whole mechanism - which will either be the loop executing your commands, or this same mechanism executing the condition evaluation or the action execution that caused the recursive invocation.

Obviously you can get yourself into a great deal of complexity with interacting reflexes. In practice, it pays to try to avoid their interaction.

During execution, the debugger variable $recursiveReflexDepth is set, and you can place a (!$recursiveReflexDepth && ...) around dangerous reflexes to suppress their recursive invocation. $maximumRecursiveReflexDepth is initially set to 10, and when this depth is reached, an immediate 'break-to-user' is done without any more reflexes being processed. You can set this to 0 or less to completely inhibit this break, or to a positive integer to change the limit.

Adding Reflexes To The Current Reflex Table

A reflex can be created with a previously unused reflex-number, or reusing a reflex number. In either case the debugger variable $createdReflex is set to the reflex-number, or to -1 if the creation failed for some reason.

The createReflex command uses the next unused reflex-number.

      createReflex  ::= reflex_definition
The replaceReflex command uses the specified reflex-number, which must be an integer greater than zero. If there is already a reflex using that number, that reflex is deleted.
    replaceReflex ::= replace reflex_number with reflex_definition
In either case the new reflex is created, enabled, and evaluated. If the evaluation succeeds the reflex is inserted, otherwise the reflex is re-evaluated each scan of the reflex-table until the evaluation does succeed, at which time the reflex is inserted. this DESCRIPTION CAN'T BE QUITE RIGHT - WHAT REALLY HAPPENS WHEN THE EVALUATION FAILS? The reflex definition specifies each aspect of the reflex.
    reflex_definition   ::=
       disposition_and_detector [reflex_condition] [reflex_action]
       | obsolete_forms_of_reflex_definition

Disposition

The disposition and the detector are specified first. The stop and stopi forms of the activity_detector have the `break_to_user' disposition. The when and wheni forms have the `continue' disposition.

Detectors

The following detectors are available:
        disposition_and_detector ::=
      1     activity_detector [thread_set_restrictor]
      2     thread_set_restrictor activity_detector
The thread_set_restrictor, if present, stops the detector from triggering if the stopped_thread does not meet the restriction. Currently the only form allowed is:
        thread_set_restrictor ::=
                thread thread_number
which requires that the thread_index be the thread_number.

The activity_detector is one of:

        activity_detector ::=

1       stopiOrWheni [at] address_expression
2       stopOrWhen  in function_expression
3       stopOrWhen  in *
4       stopOrWhen  at line_specification
5       stopOrWhen  catch signum_expression {,signum_expression}
6       stopOrWhen  catch unaligned
7       stopOrWhen  [read|any|changed|write]
                                [variable expression |
                                 memory memory_range]
Other ideas for detectors, that are not yet available, are

Reflex Conditions

The condition is specified
        reflex_condition ::=
                if (expression)

Reflex actions

        reflex_action ::=
                braced_command_list

CURRENTLY IMPLEMENTED REFLEX SUPPORT

    create_reflex_command
        : create_watch_reflex_command
        | create_break_reflex_command
The following command was previous undocumented and is being replaced...
    replace_reflex_command
        : ladebug_replace event reflex_number
              with event create_reflex_command
Watch reflexes
Watch reflexes (watchpoints) provide you with a mechanism to stop program execution when the debugger detects access to a variable or memory location.

You can either explicitly specify the address of the memory to watch, or specify a variable which is currently occupying the memory.

You can trigger when the storage is written (the default), read, written with a different value. Lastly you can specified any meaning either written or read.

    access
        : write
        | read
        | changed
        | any


    access_opt
        : access
        | // nothing at all, defaults to write

In the following command, watch and watch memory are synonyms.

    watch_memory
        : watch
        | watch memory
Here is how to specify the watchpoint.
    create_watch_reflex_command

watch 8 bytes starting at the address_exp : watch_memory address_exp access_opt thread_exp_opt within_opt cond_opt braced_command_list_opt

watch the bytes starting at the first address_exp up to and including the second | watch_memory address_exp , address_exp access_opt thread_exp_opt within_opt cond_opt braced_command_list_opt

watch constant_expression bytes starting at the address_exp | watch_memory address_exp : constant_expression access_opt thread_exp_opt within_opt cond_opt braced_command_list_opt

watch the memory currently occupied by a variable | watch variable what access_opt thread_exp_opt within_opt cond_opt braced_command_list_opt

Example of setting a watchpoint on 8 bytes at an address:

	
(ladebug)  watch memory 0x140000170
	[#1: watch memory (write) 0x140000170 to 0x140000177 ]
	(ladebug) status
	#1 Access memory (write) 0x140000170 to 0x140000177 { stop }
	(ladebug) run
	[1] Address 0x140000170 was accessed at:
        [main:12, 0x1200011a8] stq_u  r3, 0(r2)
        0x140000170: Old value = 0x0000000000000000
        0x140000170: New value = 0x0000000000000063
        [1] stopped at [main:11 0x1200011ac]
             11    for (i=0; i<8; i++)
        (ladebug)
	
Example of setting a watchpoint on an address range:
	(ladebug) watch memory 0x1400001a7, 0x140000b2
	[#1: watch memory (write) 0x1400001a7 to 0x1400000b2 ]
        (ladebug) run
        [1] Address 0x1400001a8 was accessed at:
        [main:20, 0x1200001284] stq     r3, 0(r2)
        0x1400001a8: Old value = 0x0000000000000000
        0x1400001a8: New value = 0x000000000000002a
        [1] stopped at [main:21 0x120001288]
            21    intarray[24577] = 22;
        (ladebug)
To set a watchpoint on an address where the size of the data is 4 bytes, and you wants to detect read access:
	(ladebug) watch memory 0x140000190:4 read
Use the watch variable alias to set a watchpoint for a variable named foo to detect any access by thread 1 in a function named bar, and then executes the where and show thread commands.
	(ladebug) wv foo any thread 1 in bar {where; show thread}

Breakpoint Reflexes

These cause the debugger to pause the process when the place is executed
    create_break_reflex_command
        | stop   what_opt thread_exp_opt where_opt  cond_opt
        | stopi  what_opt thread_exp_opt wherei_opt cond_opt

        | trace  what_opt thread_exp_opt where_opt  cond_opt
        | tracei what_opt thread_exp_opt wherei_opt cond_opt

        | when   what_opt thread_exp_opt where_opt  cond_opt braced_command_list
        | wheni  what_opt thread_exp_opt wherei_opt cond_opt braced_command_list

    what
        : expression

    what_opt
        : what
        | // nothing at all

    thread_list
        : thread_id
        | thread_id , thread_list

    thread_list_opt
        : thread_list
        | // nothing at all

    thread_exp_opt
        : thread thread_list
        | thread ( thread_list )
        | // nothing at all

    where
        : in all loc
        | in     loc
        | at string : line_number
        | at line_number

    where_opt
        : where
        | // nothing at all

    wherei
        : at INTEGERconstant
        | at loc_address

    wherei_opt
        : wherei
        | // nothing at all

    within
        : within loc

    where_opt
        : within
        | // nothing at all

    cond
        : if expression

    cond_opt
        : cond
        | // nothing at all

Setting a breakpoint on a line:

	(ladebug) stop at 13
	[#1: stop at "sample.c":13 ]
        (ladebug) run
        [1] stopped at [factorial:13 0x120001224]
             13         if (i<=1)
        (ladebug)

Setting a breakpoint at an address:

	(ladebug) stopi at 0x120000b14
	[#1: stopi at 4831841044 ]
        (ladebug) run
        [1] stopped at [main:4 0x120000b14]
              4     for (i=1 ; i<3 ; i++) {
        (ladebug)

Setting a breakpoint in a function:

	(ladebug) stop in factorial
	[#1: stop in factorial ]
        (ladebug) run
        [1] stopped at [factorial:13 0x120001224]
             13     if (i<=1)
        (ladebug)

Setting a conditional breakpoint in a function:

	(ladebug) stop in factorial if (i==2)
	[#1: stop in factorial if i==2 ]	
        (ladebug) run
        1! = 1
        [1] stopped at [factorial:13 0x120000bb8]
             13     if (i<=1)
        (ladebug) print
        2
        (ladebug)

CAUTION The following currently legal command stops when then value of 3 is 0 on entry to a function - ie: never! It does NOT set a breakpoint on line 3. This is bad ergonomics, and will be changed sometime.

	(ladebug) stop 3

Here is how to detect when signals are signalled...

    catch_signal_reflex_command
        : catch signal
        | catch
        | catch unaligned

    signal
        : INTEGERconstant
        | IDENTIFIER
Here is how to let the signals get delivered straight to the process...
    ignore_signal_reflex_command
        : ignore signal
        | ignore
        | ignore unaligned

Deleting Reflexes

The deletion of reflexes removes them from the current reflex table. If the associated sensors are not needed for other enabled reflexes, they will be removed from the process.
    delete_reflex_command
        : delete *
        | delete argument_expression_list

Disabling reflexes

The disabling of reflexes leaves them in the current reflex table, but they will not trigger. If the associated sensors are not needed for other enabled reflexes, they will be removed from the process. If the reflex is already disabled, nothing happens.
    disable_reflex_command
        : disable *
        | disable argument_expression_list

Enabling reflexes

The enabling of reflexes means they may trigger next time the process proceeds. If the associated sensors are not already present for other enabled reflexes, they will be added to the process. If the reflex is already enabled, nothing happens.
    enable_reflex_command
        : enable *
        | enable argument_expression_list

Processes that use exec()

A process starts with a copy of its parent's memory from the fork(), but after running for a while within that memory they often call one of the flavors of exec().

The debugger keeps track of all the exec's() that it sees happen in a table that it indexes by the executable file name. For each different executable file, it creates a Program, and within this Program it keeps the things that it believes you will want to have restored should this executable file ever again be exec()'ed into this process, or into a future process associated with this same Job.

The most important thing in the Program is the Reflex Table.

TBD What else is in it?

Processes that use dlOpen()

Jobs are forked() to create processes or attached to existing processes

Processes exec() programs making address spaces

Address spaces can be further tailored by dlOpen/dlClose

When a reflex is created, its detectors are placed in memory based on the evaluation of the detector expressions. Any intermediate results in that evaluation are not relevent to when the detector will be displaced. However if the resulting address is in a loaded file, and that file becomes unloaded, then the detector becomes displaced.

If that same loaded file gets reloaded, even at a different address, the detector is replaced at the same offset within the new loaded file that it had in the old loaded file.

If a different version of the same loaded file is reloaded, then the detector expression is re-evaluated, using the original file scope as its context; if there is no such file scope then only the global scope is used. I wonder if we could easily track whether an expression evaluation had any hits in a file scope, and in these cases NOT redo the evaluation at all - but instead just delete the reflex.

Support for 'looking around' - at the code, the data, and previously obtained information

Looking at the sources

    browse_source_command
        : source_searchlist_command
        | select_source_file_command
        | list_source_file_command
        | search_source_file_command

How the debugger finds the source files

The compiler puts source file names in the .o files. Usually they just copy what you put on the compiler command line.

The debugger looks in a list of directories to find the sources.

By default this list is (a) the current directory, and (b) the directory the executable file is in.

You can see this list by giving a use_command without any sourcepath_opt.

You can change this list, both by adding and by removing elements.

    source_searchlist_command
        : use_command
        | unuse_command
To add or replace the list, depending on the value of $dbxuse: if $dbxuse is zero, then add; otherwise, replace.
    use_command
        : use sourcepath_opt
To remove entries from the list
    unuse_command
        : unuse sourcepath_opt
        | unuse_all

    unuse_all
        : unuse *
The sourcepath is one or more directory names.
    sourcepath
        : filename
        | sourcepath filename

    sourcepath_opt
        : sourcepath
        | // nothing at all

How the debugger chooses which source file to list

Set current source file to filename.

Whenever the process stops the current source file is also set.

Other commands (which? - func? up? down? class?) also set the current source file.

Without a filename, the select_source_file_command shows what the current file is.

With a filename, it changes the current file to the filename.

    select_source_file_command
        : file
        | file filename

Listing source files

The simplest way to see a source file is to use a text editor.

However, there are some primitive capabilities built into the debugger.

    list_source_file_command
        : list
        | list expression
        | list expression , expression
        | list expression : expression
There are some search commands to help you find the lines to list
    search_source_file_command
        : / string_opt
        | ? string_opt

The / form searches forwards, the ? form searches backwards.

When the string_opt is omitted, the previous search continues from where it found the string.

When the string_opt is present, the search starts from either the start or the end of the current source file.

When they find a line, they list it (including the line number).

Looking at the threads (Tru64 only)

The debugger knows all about both the kernel threads and the pthreads in your process, but you can only deal with one or other at a time.

You switch modes by TBD

There are a variety of commands to manipulate the threads.

    thread_command
        : show_thread_command
        | switch_thread_command
        | show_condition_variable_command
        | show_mutex_variable_command
        | pthread_command
You can show all the threads.
    show_thread_command
        : show thread thread_list_opt with_thread_state_exp_opt
        | show thread *            with_thread_state_exp_opt
    thread_state
        : STATE EQ identifier_or_typedef_name

    thread_state_exp
        : thread_state
        | thread_state OR  thread_state
        | thread_state AND thread_state

    with_thread_state_exp_opt
        : WITH thread_state_exp
        | // nothing at all
You can either show the current thread, or switch to another current thread.
    switch_thread_command
        : thread thread_id_opt

    thread_id
        : INTEGERconstant
        | MINUS INTEGERconstant


    thread_id_opt
        : thread_id
        | // nothing at all
You can show a pthreads condition variable.
    show_condition_variable_command
        : show CONDITION condition_exp with_cond_state_exp_opt

    condition_id
        : INTEGERconstant

    condition_list
        : condition_id
        | condition_id , condition_list

    condition_exp
        : condition_list
        | ( condition_list )
        | // nothing at all

    with_cond_state_exp_opt
        : WITH cond_state_exp
        | // nothing at all

    cond_state_exp
        : STATE EQ identifier_or_typedef_name
You can show a pthreads mutex variable.
    show_mutex_variable_command
        : show MUTEX mutex_exp with_mutex_state_exp_opt

    mutex_id
        : INTEGERconstant

    mutex_list
        : mutex_id
        | mutex_id , mutex_list

    mutex_exp
        : mutex_list
        | ( mutex_list )
        | // nothing at all

    mutex_state_exp
        : STATE EQ identifier_or_typedef_name

    with_mutex_state_exp_opt
        : WITH mutex_state_exp
        | // nothing at all
You can even pass an undocumented string directly into the undocumented pthreads debugging support.
    pthread_command
        : PTHREAD string

Looking at the call stack

    call_stack_command
        : show_stack_command
        | change_stack_frame_command
        | pop_stack_frame_command
This shows the most recent call frames on the call stack of the current or specified threads. The expression restricts the output to no more than that many calls.
    show_stack_command
        : where
        | where expression
        | where expression thread *
        | where expression thread thread_list
        | where thread thread_list
        | where thread *
You can change the call frame that the debugger starts its name lookup in.
    change_stack_frame_command
        : up
        | down
        | up   expression
        | down expression
        | func
        | func loc
You can even pop call frames of the stack - but this is dangerous, and is almost never the right thing to do. The default is 1 call frame.
    pop_stack_frame_command
        : pop
        | pop expression

Looking at the data

If you want to look at variables in or visible from the current frame you don't have to do anything, otherwise you have to use the change_stack_frame_command to choose another frame as your current frame.

After looking at the source where this frame is executing, you usually want to examine some of the variables, or even evaluate some of the expressions.

Examining a variable is the same as printing it as an expression, so there are no special commands for doing just a variable. Instead you print expressions.

    look_around_command
        : call_command
        | print_command
        | whatis_command
        | whereis_command
        | which_command
        | c++_look_around_command
        | fortran_look_around_command
If you have functions that print the information you wish to see, you can call them.
    call_command
        : call call_expression
You can print the values of expressions, or print the values of all the local variables.
    print_command
        : print
        | print argument_expression_list
        | print rescoped_expression
        | printf arg_expression_list_opt
        | printregs
        | dump qual_symbol_with_null_opt
        | dump .
You can print information about the basic nature of an expression's result.
    whatis_command
        : whatis whatis_expressions
To help you find declarations you can ask where identifiers are declared.
    whereis_command
        : whereis   identifier_or_typedef_name
        | whereis ( identifier_or_typedef_name )
To show you which declaration an identifier resolves to.
    which_command
        : which   identifier_or_typedef_name
        | which ( identifier_or_typedef_name )

Commands Specific to C++

The class commands kinda make sense, but the TYPEDEFname stuff must be caused by a glitch in the grammar.
c++_look_around_command
        : class
        | class expression
        | class TYPEDEFname
        | print TYPEDEFname
        | print ~TYPEDEFname
        | whereis this
        | which   this

Commands Specific to Fortran

fortran_look_around_command
        : HPFGET identifier_or_key_word (   section_subscript_list )
        | HPFGET identifier_or_key_word [ section_subscript_list ]

Looking at the signal state

TBS

Looking at the generated code, etc.

machinecode_level_command
        : address_exp / count mode
        | address_exp , address_exp / mode

    count
        : INTEGERconstant
        | // nothing at all

    mode
        : identifier_or_typedef_name
        | // nothing at all

Looking at shared libraries

shared_library_command
        : LISTOBJ
        | READSHAREDOBJ filename
        | DELSHAREDOBJ  filename

Looking at previously obtained information

Here are some ideas for future work...

Modifying the process

In addition to the normal side-effects of evaluating expressions, including calls, you can explicitly modify the memory of the current process, and also modify the actual loadable file (either executable file or shared library) that has been mapped into memory.
modifying_command
        : ASSIGN unary_expression = expression
        | PATCH  unary_expression = expression

Continuing executing the program

Before continuing, you should decide whether or not to make a snapshot that can be backed up to if the reflexes don't trigger in time.

Once this is done:

    continue_command
        : step_into_command
        | step_over_command
        | step_out_of_command
        | cont_command
        | cont_from_place_command
To step until the PC is on a different line, stepping into function calls, use...
    step_into_command
        : step  step_number_opt
        | stepi step_number_opt

To step until the PC is on a different line, but within the current invocation of the current function, use...
    step_over_command
        : next  step_number_opt
        | nexti step_number_opt

To continue until the current function returns to its caller, use...
    step_out_of_command
        : return
        | return qual_symbol

To continue until some reflex triggers, use either the counted or one-time form of...
    multi_cont_command
        : number_of_times cont signal_opt

    number_of_times
        : integer_expression
More complex variants are...
    cont_command
        : cont
        | cont in
        | cont to
        | cont signal
        | cont signal to
        | multi_cont_command
        | cont_to_instruction_command

    in
        : in loc

    to
        : to string : line_number
        | to line_number

    step_number_opt
        :  expression
        | // nothing at all

You can modify the PC before continuing - don't do this, its too dangerous!
    cont_from_place_command
        :  goto expression

You can set a one-time breakpoint on an instruction address before continuing...
    cont_to_instruction_command
        :  conti to loc

Snapshots as a back up mechanism

See the intro for a quick overview.
    snapshot_command
        : save_snapshot_command
        | clone_snapshot_command
        | show_snapshot_command
        | delete_snapshot_command

    save_snapshot_command
        : save snapshot

    clone_snapshot_command
        : clone snapshot snapshot_number_opt

    show_snapshot_command
        : show snapshot snapshot_number_list_opt

    delete_snapshot_command
        : delete snapshot snapshot_number_list_opt
    snapshot_number
        : integer_expression

    snapshot_number_opt
        : snapshot_number
        | // nothing at all

    snapshot_number_list
        : snapshot_number
        | snapshot_number_list , snapshot_number

    snapshot_number_list_opt
        : snapshot_number_list
        | // nothing at all

Commands used by Ladebug implementors to debug Ladebug itself

To dump the contents of a loadable file (and perhaps even a .o file, if they have the same format) use:
    ladebug_internal_command
        : LADEBUG_INTERNAL_DUMPLOADABLE filename

To send mail to the maintainer: ladebugsupport@zko.dec.com