Ladebug Debugger Manual

Compaq Tru64 UNIX Version 5.1 or higher, March 2001

Information in this page is for Use By All Ladebug Customers

© 2001 Compaq Computer Corporation

Table of Contents

About This Manual Part I: A Quick Introduction to Using the Ladebug Debugger Part II: A Guide to Using the Ladebug Debugger
Part III: Advanced Topics


About This Manual

What Is the Ladebug Debugger?

This manual explains how to use the Compaq Ladebug debugger for versions 4.0-65 or higher. Earlier versions of the debugger may not support all the capabilities documented in this manual.

Ladebug is a symbolic source-level debugger with a choice of command-line or graphical user interface.

Ladebug provides extensive support for debugging programs written in C, C++, and Fortran (77 and 90) for:

Ladebug also offers limited support for COBOL and Ada programming.

The official Software Product Description is part of the Developers' Toolkit Software Product Description (SPD 44.36).

Obtaining a Ladebug Kit

Ladebug kits, manuals, and answers to the frequently asked questions (FAQs) are available from the following sources:

Audience

This manual is intended for programmers who have a basic understanding of one of the programming languages that the Ladebug debugger supports (C, C++, Fortran, COBOL, and Ada) and the Compaq Tru64 UNIX and Linux for Alpha operating systems.

Organization

This manual is organized as follows:

Related Documentation

The following documents contain related information:

Reporting Problems

Send mail to Ladebug.Support@Compaq.com.

What to Report

Please provide the following information when you enter your problem report. Doing so will make it easier for us to reproduce and analyze your problem. If you do not provide this information, we may have to ask you for it.

The Ladebug development team can use ftp to fetch sources and executables if you can place them in an anonymous FTP area. If not, you may be asked to use another method.

Conventions

The following conventions are used in this manual:

Convention Meaning
% A percent sign (%) represents the C shell system prompt.
# A pound sign (#) represents the default superuser prompt.
UPPERCASE
lowercase
The Tru64 UNIX operating system differentiates between lowercase and uppercase characters. On the operating system level, you must type examples, syntax descriptions, function definitions, and literal strings that appear in text exactly as shown.
Ctrl/C This symbol indicates that you must press the Ctrl key while you simultaneously press another key (in this case, C).
monospaced text This typeface indicates a routine, partition, pathname, directory, file, or non-terminal name. This typeface is also used in interactive examples.
monospaced bold text In interactive examples, this typeface indicates input that you enter. In syntax statements and text, this typeface indicates the exact name of a command or keyword.
monospaced italic text Monospaced italic type indicates variable values, place holders, and function argument names.

In syntax definitions, monospaced italic text indicates non-terminal names. When a non-terminal name consists of more than one word, the words are joined using the underscore (_), for example, breakpoint_command.

italic text Italic type indicates book names or emphasized terms.
foo_bar
    : item1
    | item2
    | item3
A colon starts the syntax definition of a non-terminal name (in this example, foo_bar. Vertical bars separating items that appear in syntax definitions indicate that you choose one item from among those listed.
[] In syntax definitions, brackets indicate items that are optional.
option ;...
option ,...
option  ...
A set of three horizontal ellipses indicates that you can enter additional parameters, options, or values. A semicolon, comma, or space preceding the ellipses indicates successive items must be separated by semicolons, commas, or spaces.
setld(8) Cross-references to online reference pages include the appropriate section number in parentheses. For example, setld(8) indicates that you can find the material on the setld command in Section 8 of the reference pages. The man command % man 8 setld shows the reference page for this command.


Part I
A Quick Introduction to Using the Ladebug Debugger

Part I provides all the information you need to make simple use of the debugger.

Chapter 1 - Overview

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 the breakpoints will stop the process close to where the bug has caused something detectably wrong to happen.

  6. Look around to determine the location of the bug:

1.1 Preparing 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.


1.2 Starting the Debugger

Before you start the debugger, make sure that you have correctly set the size information for your terminal; otherwise, the debugger's command line editing support may act unpredictably. For example, if your terminal is 47x80, you may need to set the following: Following are four basic alternatives for running the debugger on a process:

  1. Have the debugger create the process using the shell command line to identify the executable to run:
        % ladebug a.out
        Welcome to the Ladebug Debugger Version 4.0-58
        ------------------
        object file name: /usr/users/user1/a.out
        Reading symbolic information ...done
        (ladebug) stop in main
        [#1: stop in int main(void) ]
        (ladebug) run
    
  2. Have the debugger create the process using the debugger commands to identify the executable to run:
        % ladebug
        Welcome to the Ladebug Debugger Version 4.0-58
        (ladebug) load a.out
        Reading symbolic information ...done
        (ladebug) stop in main
        [#1: stop in int main(void) ]
        (ladebug) run
    
  3. Have the debugger attach to a running process using the shell command line to identify the process and the executable file that process is running:
        % a.out &
        [2] 27859
        % jobs
        [2]  Running
        ...
        % ladebug a.out -pid 27859
        Attached to process id 27859
        ....
    
    Press Ctrl/C to interrupt the process.

  4. Have the debugger attach to a running process using the debugger commands to identify the process and the executable file that process is running:
        % a.out &
        [2] 27859
        % jobs
        [2]  Running
        ...
        % ladebug
        (ladebug) attach 27859 a.out
        Attached to process id 27859
        ....
    
    Press Ctrl/C to interrupt the process.

NOTE: In the case of Fortran, routine main at which your program stops is not your main program unit. Rather, it is a main routine supplied by the Fortran system that performs some initialization and then calls your code. Just step forward using the step command a couple of times (probably twice) and you will soon step into your code.

1.3 Entering Debugger Commands

The debugger issues a prompt when it is ready for the next command from the terminal:

When you enter commands, you use the left and right arrow keys to move within the line and the up and down arrow keys to recall previous commands for editing. When you finish entering a command, press the Enter key to submit the completed line to the debugger for processing.

You can continue a line by ending the line to be continued with a backslash (\) character.

On a blank line, press the Enter key to re-execute the most-recent valid command.

Following are two very useful commands:

1.4 Scripting or Repeating Previous Commands

To execute debugger commands from a script, use the source command as follows: The source command causes the debugger to read and execute Ladebug commands from filename.

1.5 Context for Executing Commands

Although the debugger supports debugging multiple processes, it operates only on a single process at a time, known as the current process.

Processes contain one or more threads of execution. The threads execute functions. Functions are sequences of instructions that come from source lines within source files.

As you enter debugger commands to manipulate your process, it would be very tedious to have to repeatedly specify which thread, source file, and so on you wish the command to be applied to. To prevent this, each time the debugger stops the process, it re-establishes a static context and a dynamic context for your commands. The components of the static context are independent of this run of your program; the components of the dynamic context are dependent on this run.

You can change most of these individually to point to other instances, as described in the relevant portions of this manual, and the debugger will modify the rest of the static and dynamic context to keep the various components consistent.

1.6 Running a Program Under Debugger Control

As was shown previously, you can tell the debugger to create a process or to attach to an existing process.

After you specify the program (either on the shell command line or by using the load command), but before you have requested the debugger to create the process, you can still do things that seem to require a running process; for example, you can create breakpoints and examine sources. Any breakpoints that you create will be inserted into the process as soon as possible after it executes your program.

To have the debugger create a process (rather than attach to an existing process), you request it to run, specifying, if necessary, any input and output redirection and arguments as follows:

   % ladebug a.out
        Welcome to the Ladebug Debugger Version 4.0-58
        (ladebug) run
or
   (ladebug) run args
or
   (ladebug) run > output-file
or
   (ladebug) run args > output-file < input-file
The result of using any of the preceding command variations is similar to having attached to a running process. The rerun command repeats the previous run command with the same arguments and file redirection.

1.7 Pausing the Process at the Problem

Following are the four most common ways to pause a process:
  1. Press Ctrl/C:

  2. Wait until the process raises some signal. It will do this when there is an arithmetic exception, an illegal instruction, or an unsatisfiable memory access, such as an attempt to write to memory for which protection is set to read-only.

  3. Create a breakpoint before you run or continue the process:

  4. Create a watchpoint before you run or continue the process:

1.8 Examining the Paused Process

This section describes how to examine components of the paused process.

1.8.1 Looking at the Source Files

You can perform the following operations on source files: Following is an example that shows listing lines and using the / command to search for a string:
    % ladebug a.out
Aliases are shorthand forms of longer commands. This example shows using the W alias, which lists up to 20 lines around the current line. Note that a right bracket (>) marks the current line.

1.8.2 Looking at the Threads (Tru64 UNIX Only)

In a multithreaded application, you can obtain information about the thread that stopped or about all the threads, and you can then change the context to look more closely at a different thread. Note that a right bracket (>) marks the current thread.

You can select any thread to be the focus of commands that show things. For example:

1.8.3 Looking at the Call Stack

You can examine the call stack of any thread. Even if you are not using threads explicitly, your process will have one thread running your code. You can move up and down the stack, and examine the source being executed at each call. For example:

1.8.4 Looking at the Data

You can look at variables and evaluate expressions involving them. For example:

1.8.5 Looking at the Signal State

The debugger shows you the signal that stopped the thread. For example:

1.8.6 Looking at the Generated Code

You can print memory as instructions or as data. In the following example, the wi alias lists machine instructions before and after the current instruction. Note that the asterisk (*) marks the current instruction. You can examine individual registers by using the print command. To look at all the registers, use the printregs command. For example:

1.9 Continuing Execution of the Process

After you are satisfied that you understand what is going on, you can move the process forward and see what happens. The following table shows the aliases and commands you can use to do this.

Desired Behavior
Alias
Command
Can Take Repeat Count
Continue until another interesting thing happens
c
cont
Yes
Single step by line, but step over calls
n
next
Yes
Single step to a new line, stepping into calls
s
step
Yes
Continue until control returns to the caller
None
return
No
Single step by instruction, over calls
ni
nexti
Yes
Single step by instruction, into calls
si
stepi
Yes

The following example demonstrates stepping through lines of source code.

The following example demonstrates stepping at the instruction level:

1.10 Snapshots as an Undo Mechanism

Often when you move the process forward, you accidentally go too far. For example, 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. Then clone that snapshot to position another process just before the call so you can step into it.

The following example shows the stages of a snapshot being used in this way:

  1. The first stage is to build the program and start debugging.
  2. The next stage is to stop the process just before the call and take a snapshot. You can see you are just before the call because the right bracket (>) to the left of the source list shows the line about to be executed.
  3. You now step over the call. The execution is now after the call, shown by the right bracket (>) being on the following source line.
  4. Oh, how you wish you hadn't done that! No problem, just clone that snapshot you made.
  5. Now you are in a new process before the call is executed.

NOTE: fork() was used by the debugger both to create the snapshot and to clone it.

Part II
A Guide to Using the Ladebug Debugger

Part II provides most of the information needed to make expert use of the debugger.

Some additional details have been moved to Part III: Advanced Topics so they do not hinder the reading of this section.

Chapter 2 - Preparing a Program for Debugging

To facilitate debugging, you can prepare your source code and the compiler and linker environment.

2.1 Preparing Your Source Code

You do not need to make changes to the source code to debug the program. However, you can do the following to make debugging easier:

2.2 Preparing the Compiler and Linker Environment

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

The debugging information is propagated into the a.out (executable) or .so (shared library) by the ld command. It is removed by the strip command. If you strip your programs, keep the unstripped version to use with the debugger.

The debugging information can cause .o files to be very large, causing long link times, but even so it can also be incomplete.

If you are debugging C++ applications and you have unused variables in your code, or if opaque classes, structs, or unions keep showing up in Ladebug, you may want to compile particular files with the cxx -gall and -gall_pattern switches. See the cxx(1) reference page.

If you are debugging optimized code, refer to the appropriate compiler documentation for information about various -g switches and their relationship to optimization.

Chapter 3 - Starting the Debugger

You can start the debugger in the following ways:

This chapter also discusses the following topics:

3.1 Using the ladebug Command to Start 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 you can attach to a running process.

The following is the shell syntax to invoke the debugger using the ladebug command:

ladebug
        [ -c file ]
        [ -gui ]
        [ -i file ]
        [ -I directory ]
        [ -interactive ]
        [ -k ]
        [ -line serial_line ]
        [ -nosharedobjs ]
        [ -pid process_id  ]
        [ -prompt string ]
        [ -remote ]
        [ -rp remote_debug_protocol ]
        [ -tty terminal_device ]
        [ -V ]
                [ executable_file [ core_file ] ]
The following table describes the ladebug command options and parameters:

Options and Parameters Description
-c Specifies an initialization command file. The default initialization file is .dbxinit. During startup, the debugger searches for this file in the current directory. If it is not there, the debugger searches your home directory. This file is processed after the target process has been loaded or attached to.
-gui Activates the debugger's graphical user interface (GUI) on Tru64 UNIX systems only.
-i Specifies a pre-initialization command file. The default pre-initialization file is .ladebugrc. The debugger searches for this file during startup, first in the current directory and then in your home directory. This file is processed before the debugger has connected to the application being debugged, so that commands such as set $stoponattach = 1 will have taken effect when the connection is made.
-I Specifies the directory containing the source code for the target program, in a manner similar to the use command. Use multiple -I options to specify more than one directory. The debugger searches directories in the order in which they were specified on the command line.
-interactive Causes the debugger to act as though stdin is isatty(), regardless of whether or not it is. This flag is sometimes useful when using rsh to run the debugger. Currently, the only effect is to cause the debugger to output the prompt to stdout when it is ready for the next line of input.
-k or -kernel Enables local kernel debugging.
-line Specifies the serial line for remote kernel debugging. This must be used with -rp.
-nosharedobjs Prevents the reading of symbol table information for any shared objects loaded when the process executes. Later in the debug session, you can enter the readsharedobj command to read the symbol table information for a specified object.
-pid Specifies the process ID of the process to be debugged. This option cannot be used with any remote or kernel debugging flags.
-prompt Specifies a debugger prompt. The default debugger prompt is (ladebug). If the prompt argument contains spaces or special characters, enclose the argument in quotes (" "). You can specify a debugger prompt when you start the debugger from a shell with the -prompt option. The default prompt is (ladebug).
     % ladebug -prompt ">> " sample
     >> quit
You can also change the prompt by setting the $prompt debugger variable. For example:
    
    (ladebug) set $prompt = "newPrompt>> "
    newPrompt>>  
    
-remote Enables remote kernel debugging for use with the kdebug kernel debugger.
-rp Specifies the remote debug protocol. Currently only kdebug is supported; -rp kdebug enables remote kernel debugging.
-tty Specifies the terminal device for remote kernel debugging. This must be used with -rp .
-V Causes the debugger to print its version number and exit without starting a debugging session.
executable_file Specifies the program executable file.
core_file Specifies the core file.

For example, to invoke the debugger on an executable file:

To invoke the debugger on a core file: To invoke the debugger and attach to a running process: To invoke the debugger and attach to a running process when you do not know what file it is executing: To start the Ladebug GUI: To invoke the debugger on the local kernel: To invoke the debugger on the remote kernel:

3.2 Starting the Debugger Using Emacs

You can control your debugger process entirely through the Emacs Grand Unified Debugger (GUD) buffer mode, 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 higher support GNU Emacs Version 19 and higher.
Ladebug Version 4.0-58 and higher support Lucid XEmacs Version 19.14 and higher.

The information in the following sections assumes you are familiar with Emacs and are 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:

At the Load file: prompt, type:

You can also place a load-file call in your Emacs initialization file (~/.emacs). For example:

To start the debugger with Emacs, type: The following invocation line displays: 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 the debugger within it; the debugger starts and displays 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 the debugger in the GUD buffer as you would with a debugger started from a shell.

One of the benefits of running the debugger from within Emacs is a closer correlation between program execution and 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 the source is 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). Be careful that you do not modify the original buffer or kill it outright.

By default, Emacs sets its current working directory to be the directory containing the target program. Because the debugger does not do this when invoked directly, you may need to change the source code search path when using the debugger from within Emacs. To set an alternate source code search path, use the Ladebug map source directory command.

All Emacs editing functions and GUD key bindings are available. For example:

For more information on Emacs functionality and key bindings, see the Emacs documentation. For example: Then select the Emacs menu, then the Debuggers menu.

XEmacs will come up with the source buffer displayed. Use C-x 2 and a buffer menu to select the control buffer.

3.3 Ending a Debugging Session

To exit the debugger, use the quit command:
quit_command
        : quit

Alternatively, you can type exit, which is a pre-defined alias for quit.

3.4 Getting Help

To access the online help about debugger commands, use the help command:
help_command
        : help [ topic ]
Enter help to see a list of help topics. Enter help command to see a list of Ladebug commands. Enter help ladebug to see a list of function-oriented Ladebug commands.

3.5 The Ladebug GUI (Tru64 UNIX Only)

You can start the GUI in either of two ways: When you use the GUI, a Debugger Output window appears, in which you manually enter commands. You can also use the available controls (push buttons, menus, and so on) to execute debugger commands.

You can shut down the GUI and leave the command line session running by choosing File/Close All. In this case, you can restart the GUI any time with the Ladebug gui command.

To end the command line session and exit the GUI, choose File/Exit Debugger in the GUI window.

Chapter 4 - Giving Commands to the Debugger

The debugger has several different mechanisms you can use to direct its behavior. It receives input from: Some examples of the difference between .ladebugrc and .dbxinit are shown in the following table:

Example Command If Used in .ladebugrc If Used in .dbxinit
Assume the command "set $stoponattach = 1" is in one of these files and you invoked the debugger as:
    % ladebug -pid process_id executable_file
    
The debugger attaches and stops. The debugger attaches and waits for you to press Ctrl/C; subsequent attaches will stop.
Assume the command "stop in main" is in one of these files: The debugger generates a message that there is no main in which to place a breakpoint, because there is no target yet. The debugger sets the breakpoint (assuming there is a main in the target).

This chapter discusses the following topics:

4.1 Debugger's Command Processing Structure

The debugger processes commands as follows:
  1. Prompts for input.
  2. Obtains 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.

4.2 Interrupting a Debugger Action

To interrupt program execution or to abort a debugger action, press Ctrl/C. This returns the debugger to the prompt.

4.3 Entering and Editing Command Lines

The debugger reads lines from stdin. The debugger supports command line editing when processing stdin if stdin is a terminal and the debugger variable $editline is non-zero (the default; see the set command to change it). For this to work correctly, you must set the terminal width to the correct value. After editing, press the Return key to send the line to the debugger. NOTE: When you use the up and down arrow keys, the debugger skips duplicate commands. To see a complete list of the commands you have entered, use the history command.

The debugger copies each line from stdin to the record input file, if you have requested that file.

The debugger scans each line from the beginning, looking for backslash (\) characters, which 'quote' the immediately following character. If the line ends in a quoted new-line, then another line is similarly processed from stdin and appended to the first one, with the quoted new-line removed.

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

4.3.1 History Replacement of the Line

Leading spaces and tabs are removed from the assembled line.

For assembled lines that begin with an exclamation point (!), the following rules apply:

For lines that begin with a caret (^), these rules apply:

The assembled line is now appended to the history list.

Exclamation points and carets cannot be used in command lists built with braces ({}); for example, {print3; !!3} will not parse. They may be used in scripts.

History in a command list is not limited by braces, but goes all the way back. For example:

NOTE: Commands in breakpoint action lists are not entered into the history list.

4.3.2 Alias Expansion of the Line

The assembled line is now subjected to alias expansion. This is done by scanning the line, looking for pound (#), semicolon (;), and left brace ({) characters that are not inside strings.

  1. At the beginning of the line, and immediately after semicolon (;) or left brace ({) characters not inside strings, the debugger checks for the occurrence of an alias identifier.

  2. If it finds an alias identifier, it associates the formal parameters of the alias with the specified actual parameters.

    If the alias has no formal parameters, this match consumes no more of the input.

    1. If there are formal parameters, white space is skipped, and then a '(' character is checked for and skipped. The characters following the '(' up to the first non-nested ',' or ')' character 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 formal parameters, the ',' character is treated as the terminator of the actual parameter. It is skipped and processing continues as for the first parameter.

  3. After 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.

  4. Within the definition of the alias, all occurrences of the formal parameter are replaced by the actual parameter, regardless of whether or not it is in a string.

4.3.3 Environment Variable Expansion

The debugger expands environment variables and the leading tilde (~) in the following cases: As in any shell, you can group an environment variable name using a pair of curly braces ({...}), and quote a dollar sign ($) by preceding it with a backslash (\).

The following table shows how various environment variables expand. It assumes that the home directory is /usr/users/hercules and the environment variable BIN is /usr/users/hercules/bin.

Command with Environment Variable
Expands into:
load ~/a.out load /usr/users/hercules/a.out
load $BIN/a.out load /usr/users/hercules/bin/a.out
load ${BIN}2/a\$b load /usr/users/hercules/bin2/a$b
map source directory $BIN ${BIN}2
map source directory \
/usr/users/hercules/bin/usr/users/\
hercules/bin \
/usr/users/hercules/bin/usr/users/\
hercules/bin2
stop at "$BIN/a.out":20 stop at "/usr/users/hercules/\ bin/a.out":20
run $BIN/a.out ~/core
run /usr/users/hercules/bin/a.out \
/usr/users/hercules/core 

4.4 Syntax of Commands

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

4.4.1 Lexical Elements of Commands

For the debugger to parse the line, it must first turn the line into a sequence of tokens, a process called "tokenizing" or "lexical analysis". Tokenizing is done with a state machine.

As the debugger starts tokenizing a line into a command, it starts processing the characters using the lexical state LKEYWORDS. It uses the rules for lexical tokens in this state, recognizing the longest sequence of characters that forms a lexical token.

After the lexical token is recognized, the debugger appends it to the tokenized form of the line, perhaps changes the state of the tokenizer, and starts on the next token.

For more detailed information on lexical elements, see Lexical Elements of Commands in Part III.

4.4.2 Grammar of Commands

Each command line must parse as one of the following:
input
        : command_list
        | comment
A command_list is a sequence of commands that are executed one after the other.
command_list
        : command ;...
        | command ;
        | command
A comment is a line that begins with a pound (#) character.
comment
        : #

Any text after an unquoted pound character is ignored by the debugger. If the first non-whitespace character on a line is a pound character, the whole line is ignored.

NOTE: The difference between a blank command line and a command line that is a comment is that a blank line entered from the keyboard will cause the debugger to repeat the previous command and the comment line will not. Blank lines not entered from the keyboard are treated as comment lines.

4.4.3 Categories of Commands

Commands usually start with, and often contain, keywords. These keywords must be lowercase.

Following is a list of debugger command categories:

command
        : alias_command
        | attach_command
        | braced_command_list
        | breakpoint_command
        | browse_source_command
        | call_stack_command
        | command_repetition_command
        | continue_command
        | detach_command
        | dbgvar_command
        | edit_file_command
        | environment_variable_command
        | execute_commands_from_file_command
        | execute_shell_command
        | guion_command
        | help_command
        | history_command
	| if_command
        | kernel_debugging_command
        | kill_command
        | load_command
        | look_around_command
        | machinecode_level_command
        | modifying_command
        | multiprocess_command
        | quit_command
        | record_command
        | run_command
        | snapshot_command
        | shared_library_command
        | thread_command
        | unload_command

4.4.4 Keywords Within Commands (Ladebug Version -65 or Higher)

If the identifiers thread, in, at, and if occur within the expression in the following commands, the debugger treats them as keywords unless they are enclosed within parentheses (()). For example, if your program has thread defined as an integer, enter the following command to inspect the first thread levels of the stack. For example:
4.4.4.1 Ladebug Version -64 or Earlier
The debugger treats the identifiers thread, in, at, state, if, and with as keywords unless they are enclosed within parentheses (()). Therefore, if your program contains variables with names the same as these keywords, to print the variables names, enclose them in parentheses. For example:

4.4.5 Using Braces to Make a Composite Command

It is possible to surround a command_list with braces to make it work like a single command. Some places require a braced_command_list just for readability, or to assist the debugger in understanding your input.

braced_command_list
        : { command_list }

4.4.6 Conditionalizing Command Execution

The debugger provides the if command whose behavior depends on the value of an expression.
if_command
	: if expression braced_command_list [ else_clause ]

else_clause
        : else braced_command_list
In this command, the first braced_command_list is executed if expression evaluates to a non-zero value, otherwise the braced_command_list in the else_clause is executed, if specified. For example:

4.4.7 Debugger Variables

Debugger variables are pseudovariables that exist within the debugger instead of within your program. They have the following uses:

The following table lists the three different varieties of debugger variables:

User-defined variables You create these and can set them to a value of any type.
Preference variables You modify these to change debugger behavior. You can only set a preference variable to a value that is valid for that particular variable.
Display/state variables These variables display the parts of the current debugger state. You cannot modify them.

For more information about debugger variables, see Appendix 1—Debugger Variables.

The following commands deal specifically with debugger variables:

dbgvar_command
        : set dbgvar_name = expression
        | set dbgvar_name
        | set
        | unset dbgvar_name

The dbgvar_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. It is strongly recommended that you follow the same practice; in a future Ladebug release, all debugger variables will be required to start with a dollar sign.

NOTE: If a debugger variable exists that shares a name with a program variable, and you print an expression involving that name, which of the two variables the debugger finds is undefined.

The first form creates the debugger variable if it does not already exist. It then sets the value of the debugger variable to the result of evaluating the expression. For example:

The second form is equivalent to the command set dbgvar_name = 1. For example:

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

To see the value of just one debugger variable, print it. For example:

The unset form deletes the debugger variable. Some predefined debugger variables either cannot be deleted or are automatically recreated in the future when needed. For example:

4.5 Scripting or Repeating Previous Commands

To repeat the last command line, enter two exclamation points or press the Return key. You can also enter !-1.

command_repetition_command
        : !!
        | ! integer
        | !- integer
        | ! string

To repeat a command line entered during the current debugging session, enter an exclamation point followed by the integer associated with the command line. (Use the history command to see a list of commands used.) For example, to repeat the seventh command used in the current debugging session, enter !7. Enter !-3 to repeat the third-to-the-last command. See also History replacement of the line.

To repeat the most-recent command starting with a string, use the last form of the command. For example, to repeat a command that started with bp, enter !bp.

Following are other ways to reuse old commands and save typing effort:

If you place commands in a file, you can execute them directly from the file rather than cutting and pasting them to the terminal. For example:

execute_commands_from_file_command
        : source filename
        | playback input filename

Use the source command to read and execute commands from a file. (Note that you can also execute debugger commands when you invoke the debugger by creating an initialization file named .dbxinit.) These commands can be nested, and as each comes to an end, reading resumes from where it left off in the previous file.

Be aware, however, that blank lines in these files do not repeat the last command, unlike what blank lines do when entered from the terminal. Format the commands as if they were entered at the debugger prompt.

Use the pound character (#) to create comments to format your scripts.

The following is an example debugger script: Given the following script:

The following example shows how to execute it: When a command file is executed, the value of the $pimode debugger variable determines whether the commands are echoed. If the $pimode variable is set to 1, commands are echoed; if $pimode is set to 0 (the default), commands are not echoed. The debugger output resulting from the commands is always echoed.

4.5.1 Recording Input and Output

To help you make command files, as well as to help you see what has happened before, the debugger can write both its input and its output to files, as follows:
record_command
        : record io  [ filename ]
        | record input [ filename ]
        | record output [ filename ]
        | unrecord io
        | unrecord input
        | unrecord output

Use record input to save Ladebug commands to a file. The commands in the file can be executed using the source command or the playback input command.

If no file name is specified, the debugger creates a file with a random file name in /tmp as the record file. The debugger issues a message giving the name of that file.

To stop recording debugger input or output, redirect as shown in the following example, use the appropriate version of the unrecord command, or exit the debugger:

The following example shows how to use the record input command to record a series of debugger commands in a file named myscript: This example results in the following recorded input in myscript: The record output command saves the debugger output to a file. The output is simultaneously written to stdout (normal output) or stderr (error messages). For example: After the above commands are executed, myscript contains the following:

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

If input or output is already being recorded, a new record input command will close the old file and record to a new one, rather than record simultaneously to two files. In that connection, note that record io is equivalent to the combination of record input and record output, and will cause any open recording files to be closed.

Note that the Ladebug prompt itself is only recorded for record io.

4.5.2 Viewing the Command History

You can see all the commands you have already entered by using the history command. Use history_number to indicate how many commands to show, starting with the most recent. If you do not specify $historylines, the 20 previous commands are shown. See also History replacement of the line.
history_command
        : history [ integer_constant ]
For example:

4.6 Defining 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 ]
        | alias alias_name [ (argument_name ,...) ] string
        | unalias alias_name

The following example shows how to define and use an alias:

The following example further modifies the cs alias to specify the breakpoint's line number when you enter the cs command:

Note that no warning is given should the alias_name already have a definition as an alias. The old definition will be replaced by the new one.

Use the unalias command followed by an alias name to delete the specified alias.

4.7 Executing Shell Commands

You can have the debugger execute a call to the UNIX system function. This function is documented in the system (3) reference page. The call results in the sh shell executing the string you specify:
execute_shell_command
        : sh string
For example, you can execute a system command through a shell from the debugger by issuing: To execute more than one command at the specified shell, spawn a shell as follows, for example:

4.8 Invoking Your Editor

You can use the edit command to invoke the editor defined by the EDITOR environment variable.
edit_file_command
        : edit [ string ]
The editor is given the string as the file name to edit. If no file name is specified, the editor is given the current file. If no current file exists, the editor is started without a file.

If the EDITOR environment variable is undefined, the debugger invokes the vi editor.

The following example invokes the Emacs editor on the file chars.c:

The following example invokes the nedit editor on the file ~/foo/bar.f:

Chapter 5 - Context for Executing Commands

This chapter discusses the following topics:

5.1 Multiple Processes

The debugger supports debugging multiple processes at a time, but at any given time is only operating on a single process, known as the current process. The debugger variable $curprocess contains the process id for this process. Naming and switching the debugger between processes is described in Multiprocess Debugging.

5.2 Creating Processes

The debugger can find and control the following:

Specifying an executable file on the shell command line or executing the load command causes the debugger to gain control of a process that you may request it to create later.

NOTE: In the background, the debugger immediately creates a process executing the program, stalls it, and uses it to answer questions about which shared libraries are mapped, and so on. This process never continues, and is killed when:

Using the run command on such a potential process causes the debugger to create a process that is identified as currently running and recreatable.

Specifying a pid on the shell command line or executing the attach command causes the debugger to know about the process as currently running and not recreatable.

Catching a fork() causes the new child process to be identified as currently running and not recreatable.

5.3 Multiple Call Frames, Threads, and Sources

Processes contain one or more threads of execution. The threads execute functions. Functions are sequences of instructions that are generated by compilers from source lines within source files.

As you enter the debugger commands to manipulate your process, it would be very tedious to have to repeatedly specify which thread, source file, and so on, you wish the command to be applied to. To prevent this, each time the debugger stops the process, it re-establishes a static context and a dynamic context for your commands. The components of the static context are independent of this run of your program; the components of the dynamic context are dependent on this run.

Some pieces of these contexts are available as debugger variables.

You can switch most of these individually to point to other instances, as described in the relevant portions of this manual, and the debugger will modify the rest of the static and dynamic context to keep the various components consistent.

Chapter 6 - Running the Program Under Debugger Control

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

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

Hence, the following situations are possible:

6.1 Running the Program as a Child Process

If your program has a simple command line, and only requires stdin, stdout, and stderr connected, you can run it as a child process of the debugger process. For example:
     % ladebug a.out
or
     % ladebug
        (ladebug) load a.out

6.2 Attaching to a Process

If your program is any of the following, you can use the debugger's ability to attach to any process to which it has access:

For example:

     % ladebug -pid process_id a.out
or
     % ladebug
        (ladebug) attach process_id a.out

When you do this, the process continues execution until it raises a signal that the debugger intercepts, for example, SEGV. If you have set the $stoponattach preference variable, it stops immediately.

One method you can use to make attaching to a process work in a predictable way is to modify your program to loop in a known function until the debugger interrupts it, for example, when you use Ctrl/C:

  1. Add some code such as the following to your application:
  2. Run this version of your program.
  3. Attach the debugger to the running process as described above.
  4. Stop the program with Ctrl/C or by use of $stoponattach.
  5. Use the debugger to assign to the stallForDebugger variable, and continue the execution of the process, so that it exits from the loop:

6.3 The load and unload commands

Using the load command, you can tell the debugger which executable file you intend to execute in some process. The load command reads the symbol table information of an executable file and, optionally, a core file. (This is done automatically when you give the debugger a file name on the shell command line.)
load_command
        : load filename [ filename ]
For example: or: The second file name is used to specify a core file. If you specify a core file, the debugger acts as though it is attached to the process at the point just before it died, except that you cannot execute commands that require a runnable process, such as commands that try to continue the process or evaluate function calls.

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

The opposite of loading an executable file is unloading an executable file:

unload_command
        : unload pid ,...
        | unload filename

pid 	
 	: integer_constant 	
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 an executable file. For example:

6.4 The run and rerun commands

After 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   [ argument_string ] [ io_redirection ... ]
        | rerun [ argument_string ] [ io_redirection ... ]
The rerun command is a synonym for the run command.

If the rerun command is specified without arguments, the arguments and io_redirection argument of the most recent run command entered with arguments are used. If there was no previous run command, the rerun command defaults to run. If the last modification time or size of the binary file or any of the shared objects used by the binary file has changed since the last run or rerun command was issued, the debugger automatically rereads the symbol table information. If this happens, the old breakpoint settings may no longer be valid after the new symbol table information is read.

The argument_string provides both the argc and argv for the created process in the same way a shell does.

The debugger breaks up the argument_string into words, and supports several shell features, including tilde (~) and environment variable expansion, wildcard substitution, single quote ('), double quote ("), and single character quote (\).

The io_redirection argument allows you to change stdin, stdout, and stderr, which are otherwise inherited from the debugger process. For example:

io_redirection
        : <  filename
        | >  filename
        | 1> filename
        | 2> filename
        | >& filename
The various forms have the same effect as in the csh (1) shell.

NOTE: Although the grammar currently allows more than the following forms of redirection, you should only use the following forms because the grammar may change in a future release of the debugger.

     > 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
For example:

6.5 The kill command

You can kill the current process:

kill_command
        : kill
Killing a process leaves the debugger running. Any breakpoints previously set are retained. You can later rerun the program. For example:

6.6 The attach command

If a process already exists, you can have the debugger attach to it:
attach_command
        : attach pid [ filename ]
The process is specified by its pid:
pid
        : expression
For example: The file name must be an executable file that the process is executing, or a copy of it, or an unstripped copy of it. If file name is not specified, the current executable is used.

Attaching to a process both creates the debugger's knowledge of it and makes it the current process that the debugger is controlling. When you do this, the process continues execution until it raises a signal that the debugger intercepts. Usually you do this by pressing Ctrl/C or by using the shell command kill in another window. Any other mechanism for raising a signal within the process will also do. You can set the debugger variable $stoponattach to 1 to direct the debugger to immediately stop any process that it attaches to:

The opposite of attaching to a process is detaching from a process. When you detach the debugger from a process, all breakpoints are removed and the process continues to run, but the debugger can no longer identify or control it:

detach_command
        : detach pid ,...
For example:

6.7 Controlling the Process Environment

You can set and unset environment variables for processes created in the future to set up an environment different from the environment of the debugger and from the shell from which the debugger was invoked. When set, the environment variables apply to all new processes you debug.

NOTE: The environment commands have no effect on the environment of any currently running process. The environment commands do not change or show the environment variables of the debugger or of the current process. They only affect the environment variables 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 are currently set, or a specific one, use a show_environment_variable_command. For example:
show_environment_variable_command
        : printenv [ environment_variable_name ]
        | export
        | setenv
NOTE: The export and setenv commands without any arguments are equivalent.

To add or change an environment variable, use a set_environment_variable_command. If the environment_variable_value is not specified, the environment variable value is set to "".

set_environment_variable_command
        : export environment_variable_name  = environment_variable_value
        | setenv environment_variable_name  environment_variable_value
environment_variable_value
        : string
For example:

To remove an environment variable, use the unsetenv command:

unset_environment_variable_command
        : unsetenv environment_variable_name
        | unsetenv *

If an asterisk (*) is specified, all environment variables are removed.

NOTE: There is no command to simply return to the initial state the environment variables had when the debugger started. You must use set_environment_variable commands and unset_environment_variable commands appropriately.

6.8 Multiprocess Debugging

The debugger can find and control more than one process at a time. The debugger can find and control a process for one of three reasons:

At any one time, you can control only one of the processes that the debugger controls. The rest are stalled. You must explicitly switch the debugger to the process you want to work with, stalling the one it was controlling:

multiprocess_command
        : show_process_command
        | switch_process_command

You can show the processes the debugger controls:

show_process_command
        : show process [ all ]
        | process

all
        : all
        | *
 
For example:

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 remains stalled until either the debugger exits or until you switch to it and continue it.

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

Both the run command and the attach command switch the debugger to the process on which they operate.

6.9 Processes That Use fork()

The debugger has the following predefined variables that you can set for debugging a program that forks:

When a fork occurs, the debugger sets the debugger variables $childprocess and $parentprocess to the child and parent process IDs, respectively.

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

In the preceding example, note the following:

Continuing the previous example, the following shows how to switch the debugger to the child process. Listing the source code shows the source for the child process.

In the preceding example, note the following:

Note: If you catch the child but not the parent, and the parent code tries to execute a wait on the child, the target will get stuck if you don't let the child run to completion. This happens because the parent will be running but making no progress, and the child is stopped by the debugger. For example:

6.10 Processes That Use exec()

Set $catchexecs to 1 to instruct the debugger to stop the process and notify you when an exec occurs. The process stops before executing any user program code or static initializations. You can debug the newly executed process. The debugger keeps a history of the progression of the executed files.

In the following scenario, you set the predefined variables $catchforks and $catchexecs to 1. The debugger will notify you when an execution occurs. Because $catchforks is set, you will also be tracking the child process and, therefore, you will be notified of any exec in the child process.

The following example shows an exec occurring on the current context and the child process stopped on the runtime-loader entry point:

Note the following:

6.11 Core File Debugging

When the operating system encounters an unrecoverable error, for example, a segmentation violation (SEGV), the system creates a file named core and places it in the current directory. The core file is not an executable file; it is a snapshot of the state of your process at the time the error occurred. It allows you to analyze the process at the point it crashed. For more information on core file debugging, see Core File Debugging in Part III.

6.12 Kernel Debugging

The debugger supports kernel debugging, which is a task normally performed by systems engineers or system administrators. Debugging the kernel is a unique practice, and can take several forms. This debugger supports local and remote kernel debugging, and crash dump analysis. For more information on kernel debugging, see Kernel Debugging in Part III.

Chapter 7 - Locating the Site of a Problem

To determine why a problem is happening, you usually want to execute your program up to or just before the point at which you observe the first evidence of the problem. Then you can examine the internal state of your program and try to identify something that explains the visible problem. Possibly you will see right away how the problem occurs, in which case you are finished debugging. You then correct your program, recompile, relink, and confirm that the correction works as intended.

Often, you will see something about the program state that is wrong but you will not see how it got that way. In that case, you need to make a guess at where the mistake might have occurred. Then, repeat this whole process, trying to stop at or just before the possible trouble point.

For simple problems, it may be easy to describe the conditions under which you want to stop the program; for example, "the first time traverse is called" or "when division_by_zero occurs". Other situations may require either more complex descriptions or repeated trial-and-error attempts to discover the critical information needed to solve your problem. Breakpoints provide the means by which you specify to the debugger an event or condition under which you want to intervene in the execution of your program and what actions you want the debugger to take when that event is detected.

You can define breakpoints based on:

You can also enable, disable, or delete breakpoints.

Breakpoint commands include the following:

breakpoint_command
        : breakpoint_definition_command
        | simple_stop_command
        | signal_command
        | obsolete_breakpoint_definition_command
        | breakpoint_table_command
This chapter discusses the following topics:

7.1 Breakpoint Definitions

The following is a particularly common breakpoint:

This command tells the debugger that when execution enters the function main, you want the debugger to suspend execution and return control to you.

The debugger responds to a breakpoint command by displaying how it recorded the request internally. The debugger assigns a number to the breakpoint (in this case, it is 1), which it uses later to refer to that breakpoint. The debugger does not just repeat the command as you entered it; it provides a more complete description of the function main to help you confirm that it has correctly identified the function you meant.

Later, after you cause the program to execute, if that event occurs, the debugger reports the event and then prompts you for what to do next. For example:

Both the event part and the action part of a breakpoint definition command consist of several subparts:

breakpoint_definition_command
        : disposition
            [ quiet ]
              detector
            [ thread_filter ]
            [ logical_filter ]
            [ breakpoint_actions ]
where the detector, thread_filter (if specified), and logical_filter (if specified) collectively specify the event part, and the disposition, quiet (if specified) and breakpoint_actions (if specified) collectively specify the action part.

NOTE: Additional obsolete forms of breakpoint definition are retained only for backward compatibility with earlier versions of the debugger. These forms are explained later. The obsolete forms may be eliminated in a future release.

There are three distinct points in time at which a breakpoint definition has an effect:

  1. When the command is entered

    The command is parsed, names and expressions that occur in any of the event parts are evaluated, and the breakpoint actions are parsed and checked for correctness (but not evaluated).

  2. When the debugger initiates program execution

    For each breakpoint that is not disabled, appropriate modifications are made to the program to enable detection of the specified event.

  3. When a detector triggers during program execution

    The thread filter specification (if present) and logical filter (if present) are evaluated to determine whether the breakpoint as a whole has triggered. If not, then execution is resumed (silently). If so, the breakpoint actions are performed, after which execution stops or resumes according to the specified disposition.

7.1.1 Disposition

disposition
        : stop
        | when
The stop command specifies that when the event specified by the breakpoint occurs and all processing for that breakpoint has been completed, the debugger should prompt for further commands.

The when command specifies that when the event specified by the breakpoint occurs and all processing for that breakpoint has been completed, the debugger may resume execution of the program. See the section When Multiple Breakpoints Trigger at Once for an explanation of how the debugger determines when to resume execution.

7.1.2 The quiet Specifier

By default, when an event is detected and the debugger determines that the breakpoint actions should be performed, the debugger prints a line that identifies the breakpoint, for example:

The optional quiet specifier tells the debugger to omit this information.

7.1.3 Detectors

The debugger uses several kinds of detectors, each corresponding to a particular kind of event:

detector
        : place_detector
        | watch_detector
        | signal_detector
        | unaligned_detector

A place detector specifies a place or location in your program. It can refer to the beginning of a function, a particular line in one of your source files, a specific value of the PC (program counter), or certain sets of these.

A watch detector specifies a variable or other memory locations that should be monitored to detect certain kinds of access (read, write, and so on).

A signal detector specifies a set of UNIX signals to be monitored.

An unaligned access detector specifies any kind of memory access using an unaligned access.

This section describes each type of detector.

7.1.3.1 Place Detectors
A place detector is used to determine when execution reaches a particular place or location in your program:
place_detector
        : in function_name
        | in all function_name
        | pc address_expression
        | at line_specifier
        | every proc entry
        | every procedure entry
        | every instruction
        | expression

The in function_name detector specifies the event where execution reaches the entry of the named function. For example:

If the function name is ambiguous (there can be more than one function that matches the name in some languages, including C++), the debugger prompts you with a list of alternatives from which to choose.

If you choose the last option ("None of the above"), then no function is selected and no breakpoint is defined.

The in all function_name detector is the same as in function_name except that it specifies all of the functions that match the given name, whether one or more.

The pc address_expression detector specifies the event where execution reaches the given machine address:

The at line_specifier detector specifies the event where code associated with a particular line of the source is reached: If no code is associated with the given line number, the debugger finds and substitutes the closest higher line number that has associated code.

The every procedure entry detector specifies that a breakpoint should be established for every function entry point in the program.

NOTE: This command can be very time-consuming because it searches your entire program — including all shared libraries that it references — and establishes breakpoints for every entry point in every executable image. This can also considerably slow execution of your program as it runs.

A disadvantage of this command is that it establishes breakpoint for hundreds or even thousands of entry points about which you have little or no information. For example, if you use stop every proc entry immediately after loading a program and then run it, the debugger will stop or trace over 100 entry points before reaching your main entry point. About the only thing that you can do if execution stops at most such unknown places is continue until some function relevant to your debugging is reached.

The every instruction detector specifies a breakpoint for every instruction in your entire program:

When used with the stop disposition, a subsequent continue behaves essentially the same as a step by instruction command (see stepi).

When used with the when disposition, subsequent next and step commands allow you to trace all of the instructions that are executed as a result of those stepping commands. Beware that even when next is used to step over a called routine, the trace output includes all of the instructions that are executed within the called routine (and any routines that it calls).

NOTE: This command will slow execution of your program considerably.

The detector expression (that is, an expression not preceded by one of the keywords in, at, or pc) specifies either a function name or line number depending on how the expression is parsed and evaluated. An expression that evaluates to the name of a function is handled just like the equivalent command that uses in in the detector; otherwise, it is handled like the equivalent command that uses at in the detector.

7.1.3.2 Watch Detectors
A watch detector is used to determine when a variable or other memory location is read or written and/or changed. Breakpoints with watch detectors are also known as watchpoints.
watch_detector
        : basic_watch_detector watch_detector_modifiers

basic_watch_detector
        : variable expression
        | memory start_address_expression
        | memory start_address_expression , end_address_expression
        | memory start_address_expression : byte_count_expression

watch_detector_modifiers
        : [ access_modifier ] [ within_modifier ]

access_modifier
        : write
        | read
        | changed
        | any

within_modifier
        : within function_name

You can specify a variable whose memory is to be watched, or specify the memory directly. The accesses that are considered can be limited to those that write (the default), read, write and actually change the value, or can include all accesses.

If a variable is specified, the memory to be watched includes all of the memory for that variable, as determined by the variable's type. The following example watches for write access to variable _nextNode, which is allocated in the 8 bytes at the address shown in the last line of the example.

It is the variable specified which is watched. If "p" is a pointer, watch variable p will watch the content of the pointer, not the memory pointed to by "p". Use watch memory *p to watch the memory pointed to by "p".

If memory is specified directly in terms of its address, the memory to be watched is defined as follows:

If a within_modifier is specified, then only those accesses that occur within the given function (but not any function it calls) are watched. For example:

7.1.3.3 Signal Detectors
A signal detector is used to determine when a particular UNIX signal is raised:
signal_detector
        : signal signal_id ,...

signal_id
        : integer_constant
        | signal_name
Signals may be specified by numeric value or by their conventional UNIX names, without or without the leading "SIG": Note that if the debugger catches a signal event, then a subsequent simple continue will resume execution without raising the signal again in your process. However, a signal can be specified as part of the continue command to send the signal to your process when it resumes.
7.1.3.4 Unaligned Access Detectors (Tru64 UNIX Only)
An unaligned access detector is used to determine when an unaligned memory access occurs:
unaligned_detector
        : unaligned

Unaligned accesses are automatically handled by the Tru64 UNIX operating system. By default, an unaligned access results in an information message and then is corrected so that your program can continue. (You or your system administrator can choose a different default. See the uac(1) reference page for details.) This message looks like this:

Unaligned access pid=30231  va=0x11ffff791 pc=0x120001af4 ra=0x120001b84 inst=0xa0220000

You can request the debugger to detect unaligned accesses:

7.1.3.5 Unaligned Access Detector (Linux Only)
Unaligned accesses are automatically handled and quietly corrected on Linux. The debugger cannot detect these events.

7.1.4 Thread Filter

A thread filter determines whether a detected event should be further considered for breakpoint processing.
thread_filter
        : thread thread_id ,...
The thread_id expressions are evaluated at the time the breakpoint command is entered, and each must yield an integer value.

A detected event is retained for further consideration only if the thread in which the event occurs matches one of the given threads. If not, the detection is quietly ignored.

Note that if the thread_filter does not indicate a match, then any related logical filter is not evaluated.

7.1.5 Logical Filter

A logical filter determines whether a detected event should be further considered for breakpoint processing:
logical_filter
        : if expression
A detected event is retained for further consideration only if the given expression evaluates to true. If not, the detection is quietly ignored.

The expression is checked syntactically in the context of the place where the breakpoint command is given: it must be syntactically valid according to the language rules that apply there. However, the expression is not evaluated and names that occur in the expression need not be visible. After the syntax check, the expression is remembered in an internal form and is not rechecked later when it is evaluated.

If an error occurs when the expression is evaluated, for example, because a name in the expression is not defined, then the error is reported and the value of the expression is assumed to be true.

Note that an error in the expression does not change the disposition. If continuation was specified, then that is still what occurs. For example:

It is valid for a logical filter expression to contain a call to another routine in your program. Such a call is evaluated in the same way as if it occurred in a call or print command. However, execution of the called routine might result in triggering a breakpoint; this is called a recursive breakpoint.

7.1.6 Breakpoint Actions

The action part of a breakpoint command specifies actions to be performed when the event part has triggered (including passing any thread and/or logical filters):
breakpoint_actions
        : { action_list }

action_list
        : command
        | command ;
        | command ;...
7.1.6.1 Special Commands
The following debugger commands behave differently in some fashion when used within a breakpoint action list:
7.1.6.2 Commands to Use with Caution
You must be very careful when using some commands in breakpoint action lists. The following commands cause the debugger to resume execution of your program in the midst of action list processing:

It is easy in such cases to lose track of just what state breakpoint processing is really in or where you really are in your program. Such confusion may mislead or misdirect your debugging effort. For further discussion, see the section on Recursive breakpoints.

7.1.6.3 Commands to Avoid
You should avoid altogether some commands in breakpoint action lists. The following are commands that directly or indirectly change the process that the debugger is controlling:

The debugger does not explicitly prohibit these commands, but their behavior within action lists is implementation-defined and subject to change from release to release. In very specialized cases, you may be able to obtain useful results by using them in action lists, but do not expect the same behavior over the long term.

7.1.7 When Multiple Breakpoints Trigger at Once

It is possible for multiple breakpoints to specify the same event, or possibly overlapping events. Thus, more that one breakpoint detector may trigger at the same time.

When more than one breakpoint detector triggers, the thread filters and logical filters of all the breakpoints involved are processed before the action part of any breakpoint is performed.

After the set of breakpoints that trigger is determined, the action parts of each of them are performed in an undefined order.

After all action parts are performed, execution of the program is resumed only if all of the breakpoints so specify in their disposition. If any one of them specifies a break, the debugger prompts you for further commands.

7.1.8 Recursive Breakpoints

The following commands cause the debugger to resume execution of your program while in the midst of action list processing:

In all of these cases, the debugger temporarily suspends processing of the current breakpoint to start your program executing again and then waits for that execution to complete. As long as no new breakpoint is triggered during that execution, all will be fine. However, if a new breakpoint triggers, in particular one with the stop disposition, then you may be prompted for new command input for the recursive breakpoint even before the initial breakpoint has completed. Further, continuing execution may ultimately allow the original breakpoint to complete, at which time its disposition will come into play.

It is easy in such cases to lose track of just what state breakpoint processing is really in or where you really are in your program. Such confusion may mislead or misdirect your debugging effort. See the call command example which locates suspended execution in nested function calls.

7.1.9 Breakpoints and C++

This section describes the use of breakpoints when debugging C++ programs.
7.1.9.1 Member Functions
Setting breakpoints in C++ member functions is illustrated using the following program:

Member functions must be named in a way that makes them visible at the current position, according to the normal C++ visibility rules. For example:

If not positioned within a member function of a class, it is generally necessary to name the desired member function using type qualification, an object of the class type, or a pointer to an object of the class type. For example:

You can avoid the ambiguity associated with an overloaded function by specifying a complete signature for the function name. For example:

7.1.9.2 Templates and Instantiations
The debugger has no knowledge of templates that may occur in your program. However, you can usually debug template instantiations the same way as the equivalent non-instantiated class or function.

Debugging of template instantiations is illustrated using the following source text:

Normal debugging commands then apply to the instantiation (not the template as such):

7.1.9.3 Exception Handlers
When working with exception handlers, you can set a breakpoint at the appropriate line to determine if an exception is thrown. In addition, you can set breakpoints in these functions that are part of the C++ library support for exceptions:

terminate Gains control when any unhandled exception occurs, which will result in program termination.
unexpected Gains control when a function containing an exception specification tries to throw an exception that is not included in that specification.

These special library functions are illustrated using the following source:

You can trace the flow of execution, as in the following:

7.1.10 Special Signal Breakpoints

UNIX signals are operating-system-defined events that can be handled by the debugger.
7.1.10.1 The catch and ignore Commands
Two special breakpoint commands, catch and ignore, can be used to handle UNIX signal events:
signal_command
        : catch_command
        | ignore_command

catch_command
        : catch [ signal_id ]

ignore_command
        : ignore [ signal_id ]

A catch command with an operand specifies that the debugger should catch and handle the given UNIX signal. The signal can be specified by integer number or by standard signal name, with or without the leading "SIG". The catch command is equivalent to the breakpoint command:

or with these exceptions:

An ignore command with an operand specifies that the given UNIX signal should not be caught or handled by the debugger; rather such a signal is passed to your program. The ignore command is equivalent to deleting the breakpoint created by a catch command for that signal.:

A catch command without an operand lists all signals that are currently being handled. Similarly, an ignore command without an operand lists the signals that are currently being ignored. Together, the two lists show all signals known to the debugger.

You can issue these commands immediately after the debugger starts to show which signals are caught and which are ignored by default:

NOTE:: Signals RTMIN, RTMIN1,...,RTMIN7, RTMAX, and RTMAX7,...,RTMAX1 apply only on Tru64 UNIX.
7.1.10.2 Unaligned Accesses (Tru64 UNIX Only)
You can request the debugger to catch unaligned accesses: This command is very much like the stop unaligned command:

Although this looks like a normal catch command, it differs in several respects:

Like other catch commands, the following rules apply:

NOTE: You cannot specify unaligned in a signal detector of a normal breakpoint definition.

You can request the debugger to ignore unaligned accesses when catch unaligned is in effect (the default) by using the following command:

However, if a breakpoint was defined using an unaligned access detector, then it must be disabled using a disable or delete breakpoint command.
7.1.10.3 Unaligned Accesses (Linux Only)
Unaligned accesses are automatically handled and quietly corrected on Linux. The debugger cannot catch these events.
7.1.10.4 Ctrl/C
If your program seems to be caught in a loop, you can press Ctrl/C. The debugger interprets this as a command to send a signal interrupt (SIGINT) to your program. Because the debugger itself catches signal SIGINT by default, this interrupts your program and returns control to the Ladebug prompt.

If you give the command ignore SIGINT, then it is no longer possible to regain control of your program using Ctrl/C. In that case, signal SIGINT is delivered directly to your program. Unless your program has explicitly arranged otherwise, SIGINT will result in program termination.

7.1.11 Breakpoint Interactions with exec(), fork(), dlopen() and dlclose() System Calls

A process starts with a copy of its parent's memory as the result of a fork() system call; after running for a while within that memory, the process will often make an exec() system call to start a new executable file within that process.

The debugger keeps track of the exec() calls that occur so that it can keep track of various properties associated with each executable file. In particular, the breakpoint table is one of those properties. Thus, if you run or rerun your program, the same breakpoints can be re-established, even though a new process is initiated. Similarly, if you work with more than one process, each process has a distinct breakpoint table associated with it.

When a dlopen() system call occurs, the debugger reprocesses the current breakpoint table and automatically sets up the means to detect any events that apply to the newly loaded image.

When a dlclose() system call occurs, the debugger also reprocesses the breakpoint and de-activates any events that apply to the unloaded image.

7.1.12 Obsolete Breakpoint Commands

The following forms of breakpoint commands are obsolete, but are still supported for backward compatibilty with earlier versions of the debugger:
obsolete_breakpoint_definition_command
        : obsolete_watch_breakpoint_definition_command
        | obsolete_trace_breakpoint_definition_command
        | obsolete_stopi_breakpoint_definition_command
        | obsolete_wheni_breakpoint_definition_command
        | obsolete_tracei_breakpoint_definition_command
7.1.12.1 Obsolete Watchpoint Definition
An obsolete watchpoint definition is similar to a stop variable or stop memory breakpoint:
obsolete_watch_breakpoint_definition_command
        : watch obsolete_watch_detector
            [ obsolete_watch_modifiers ]
            [ breakpoint_actions ]

obsolete_watch_detector
        : variable variable_name
        | [ memory ] start_address_expression
        | [ memory ] start_address_expression , end_address_expression
        | [ memory ] start_address_expression : byte_count_expression

obsolete_watch_modifiers
        : [ access_modifier ]
          [ thread_filter ]
          [ within_modifier ]
          [ logical_filter ]
An obsolete watchpoint and a stop command differ in the following respects: These differences are purely syntactic; the semantics are the same.
7.1.12.2 Obsolete Tracepoint Definition
An obsolete tracepoint definition is similar to a when in or when at breakpoint, possibly combined with watching for a change of a variable's value:
obsolete_trace_breakpoint_definition_command
        : trace [ variable_name ]
            [ thread_filter ]
            [ where_modifier ]
            [ logical_filter ]
            [ breakpoint_actions ]
        | trace function_name [ logical_filter ] [ breakpoint_actions ]
        | trace line_specifier [ logical_filter ] [ breakpoint_actions ]

where_modifier
        : in function_name
        | at line_specifier
	
line_specifier
        : filename:line_number 
        | line_number
Following are the differences between an obsolete tracepoint and a when command: For example:

If the trace command is given with no arguments, the debugger prints a trace identification line when each function in your program is entered. For example:

This is equivalent to the when every proc entry command (with equivalent performance degradation).
7.1.12.3 Instruction-related breakpoint commands
The following commands control obsolete instruction-related breakpoints:
obsolete_stopi_breakpoint_definition_command
        : stopi [ expression ]
            [ thread_filter ] [ match_address ] [ logical_filter ]

obsolete_tracei_breakpoint_definition_command
        : tracei [ expression ]
            [ thread_filter ] [ match_address ] [ logical_filter ]

obsolete_wheni_breakpoint_definition_command
        : wheni [ expression ]
            [ thread_filter ] [ match_address ] [ logical_filter ]
            breakpoint_actions

match_address
        : at address_expression

The stopi, tracei, and wheni forms of breakpoint definition are similar to the corresponding stop, trace, and when forms, with the following differences:

7.2 Breakpoint Tables

As breakpoints are defined, they are recorded in a breakpoint table, associated with the current program. You can display and modify this table in certain limited ways.
breakpoint_table_command
        : show_all_breakpoints_command
        | delete_breakpoint_command
        | enable_breakpoint_command
        | disable_breakpoint_command
Each entry in the breakpoint table has the following properties:

In addition to the main effects of a breakpoint definition, as discussed in Breakpoint Definitions, a breakpoint definition also sets the debugger variable $lasteventmade to the breakpoint number of the breakpoint just defined. This value can be recalled for later use if desired. For example:

If an error occurs in a breakpoint command, the variable $lasteventmade is not changed.

7.2.1 Showing Breakpoint Status

Use the status command to display the current breakpoint table:
show_all_breakpoints_command
        : status
Each entry in the current breakpoint table is displayed showing all of its properties. For example:

When large or complex values are passed by value to the routine in the status line, the output can be voluminous. You can set the control variable $statusargs to 0 to suppress the output of argument type information in the status line.

7.2.2 Enabling, Disabling, and Deleting Breakpoints

When a breakpoint is defined, it is enabled by default. When the debugger starts or resumes process execution, it first adapts the process so that it can detect when the given events occur. A breakpoint can be disabled so it is not involved in determining when the process should next stop. A breakpoint that is no longer required can be deleted entirely.
disable_breakpoint_command
        : disable all
        | disable breakpoint_number_expression ,...

enable_breakpoint_command
        : enable all
        | enable breakpoint_number_expression ,...

delete_breakpoint_command
        : delete all
        | delete breakpoint_number_expression ,...
For example:

Chapter 8 - Looking Around at the Code, the Data, and Other Process Information

This chapter describes how to look at the following components of a running process:

8.1 Looking at the Source Files

The debugger supports commands to perform the following operations with source files:
browse_source_command
        : source_directory_mapping_command
        | source_searchlist_command
        | select_source_file_command
        | list_source_file_command
        | search_source_file_command
Special debugging information that the compiler puts in the .o files correlates the machine instructions and data back to the source files and the positions they came from.

Source files are compiled and linked into executable files. During debugging, the debugger tries to find these source files to display them for you. If the source files have moved, or if the paths to them are relative, the debugger may not be able to locate them. All the information the debugger needs comes from the executable files or shared libraries, not the source files.

8.1.1 How the Debugger Finds Source Files

The debugger searches for a source file (dir_name/base_name) using the following algorithm:

  1. If dir_name is mapped to another source directory (mapped_dir_name), look for mapped_dir_name/base_name. Otherwise, look for the original file dir_name/base_name.
  2. If Step 1 fails to find a readable file, for each entry use_dir in use_list, look for use_dir/dir_name/base_name. Note that the use_list entries are tried in the order they appear in the use_list.
  3. If Step 2 fails, for each entry use_dir in use_list, look for use_dir/base_name. Just as in Step 2, the use_list entries are tried in the order they appear in the use_list.
  4. If Step 3 fails, the debugger cannot find any source file.
The debugger uses the first-found readable file as the source file.

The debugger has source directory mapping commands that:

The following example shows how to use source directory mapping. Suppose you compile x_solarSystem as follows:
     % pwd
        /usr/users/ladebug/sandbox/test/src/common/Examples
        % ls -R
        bin/ src/

        ./bin:
        x_solarSystem*

        ./src:
        solarSystemSrc/

        ./src/solarSystemSrc:
        base_class_includes/    main/                   star.cxx
        derived_class_includes/ orbit.cxx
        heavenlyBody.cxx        planet.cxx

        ./src/solarSystemSrc/base_class_includes:
        heavenlyBody.h  orbit.h

        ./src/solarSystemSrc/derived_class_includes:
        planet.h  star.h

        ./src/solarSystemSrc/main:
        solarSystem.cxx
        % cd src
        % cc -g -o ../bin/x_solarSystem \
          -IsolarSystemSrc/base_class_includes \
          -IsolarSystemSrc/derived_class_includes \
          main/solarSystem.cxx heavenlyBody.cxx orbit.cxx planet.cxx star.cxx
Then you move the directory solarSystemSrc elsewhere:
     % mv solarSystemSrc movedSolarSystemSrc
Now debug x_solarSystem in /usr/users/ladebug/sandbox/test/src/common/Examples/bin: The debugger cannot find the file because it has been moved to another directory.

The following command displays a summary of the source directories in a.out. The ellipsis (...) here means that solarSystemSrc contains one or more source directories.

The following command directs the debugger to look for source files originally in solarSystemSrc in movedSolarSystemSrc instead. This time, the debugger finds the source file. The following command gives a complete list of source directories. As you can see, solarSystemSrc is mapped to movedSolarSystemSrc. As a side effect of mapping solarSystemSrc to movedSolarSystemSrc, the subdirectories in solarSystemSrc are mapped to their counterparts under movedSolarSystemSrc.

To summarize, the debugger provides the following four commands for checking and setting source directory mappings:

source_directory_mapping_command
        : show source directory [ directory_name ]
        | show all source directory [ directory_name ]
        | map source directory from_directory_name to_directory_name
        | unmap source directory  from_directory_name
Use the show source directory command to display the directory mapping information of directory_name and its child directories (or immediate subdirectory). If directory_name is not specified, the mapping information of all the source directories whose parent is not a source directory is displayed.

The show all source directory command is identical to the show source directory command except that the mapping information of all the descendants of directory_name is displayed:

When you further expand ellipsis points (...) where directory is the directory on the line above the ellipsis points:

Use the map source directory command to tell the debugger that the source files in the directory from_directory_name can now be found in to_directory_name.

The unmap source directory command maps from_directory_name back to itself; in other words, if from_directory_name has been mapped to some other directory, this command will restore its default mapping. For example:

NOTE: The symbol *=> means that you are setting the mapping explicitly using the map source directory command, whereas => means that the mapping is derived from an existing explicit mapping.

By default, the use_list is: (1) the current directory and (2) the directory containing the executable file. Each process has its own use_list. You can also use the ladebug command -I option to specify search directories.

The following commands let you view and modify the use_list.

source_searchlist_command
        : use_command
        | unuse_command
Enter the use command without an argument to list the directories the debugger searches for source code files. Specify a directory argument to make source code files in that directory available to the debugger. You can also use the ladebug command -I option to specify search directories, which puts those directories in the use_list.

You can customize your debugger environment source code search paths by adding commands to your .dbxinit file that use the use command:

use_command
        : use [directory_name ...]

If the directory_name is specified, it is either appended to or replaces the use_list, depending on whether the value of the $dbxuse debugger variable is zero (append) or non-zero (replace).

The unuse command removes entries from the use_list:

unuse_command
        : unuse [directory_name ...]
        | unuse *

Enter the unuse command without the directory_name to set the search list to the default (the home directory, the current directory, and the directory containing the executable file). Include the directory names to remove them from the search list. The asterisk (*) argument removes all directories from the search list.

8.1.2 How the Debugger Chooses Which Source File to List

The debugger has a concept of current source file, so you do not have to explicitly specify a source file in many commands. Whenever the process stops, the current source file is set to the source file for the code currently executing. The commands up, down, class, and file also set the current source file.

You can see and modify the current source file selection:

select_source_file_command
        : file [ filename ]
        : fileexpr [ expression ]

Use the file command without a file name to display the name of the current file scope. Include the file name to change the file scope. Change the file scope to set a breakpoint in a function not in the file currently being executed.

To see source code for or set a breakpoint in a function not in the file currently being executed, use the file command to set the file scope.

If the file name is not a literal, use the fileexpr command. For example, if you have a script that calculates a file name in a debugger variable or in a routine that returns a file name as a string, you can use fileexpr to set the file.

The following example uses the file command to set the debugger file scope to a file different from the main program, and then stops at line number 26 in that file. This example also shows the fileexpr command setting the current scope back to the original file which is solarSystem.cxx.

8.1.3 Listing Source Files

The simplest way to see a source file is to use a text editor. The edit command will display an editor on the current file, using the current definition of the EDITOR environment variable, if there is one.

However, some primitive inspection capabilities are built into the debugger. The list command displays source lines, beginning with the source code line corresponding to one of the following:

list_source_file_command
        : list [ line_expression ]
        | list line_expression , line_expression
        | list line_expression : line_expression

line_expression
        : expression

If specified, the first expression must evaluate to either an integer (the line number of the first line to display within the current source file) or a function (the first line of the function).

Specify the exact range of source lines as either a comma followed by the expression for the last line, or a colon followed by the expression for the the number of lines. This second expression must evaluate to an integer value.

If a second expression is not given, the debugger shows 20 lines, fewer if the end of source file is reached.

For example, to list lines 16 through 20:

For example, to list 6 lines, beginning with line 16:

8.1.4 Searching the Content of Source Files

The following search commands search through the current source file to help you find the lines to list:
search_source_file_command
        : / [ string ]
        | ? [ string ]

NOTE: The string is actually just the rest of the line, not a string literal. The rest of the line is still having alias expansion done on it.

Use a slash (/) to search forward from the most recently listed line; use a question mark (?) to search backward. Like most searches, it will stop at the end (or beginning) of the file being searched, and will wrap if the command is repeated at that point.

When the string is omitted, the previous search continues from where it found the string. When the string is present, the search starts from either the start (/) or the end (?) of the current line.

When a match is found, the debugger lists the line number and the line. That line becomes the starting point for any further searches, or for a list command. For example:

  1. To locate _firstNode:
  2. Then to locate append before line 69:
  3. Then to locate append after line 65:
The debugger provides parameterized aliases and debugger variables of arbitrary types. You can use these to do list traversal (see the array navigation example).

8.2 Looking at the Threads (Tru64 UNIX Only)

A thread is a single, sequential flow of control within a process. Each thread contains a single point of execution. Threads execute within (and share) a single address space; therefore, a process's threads can read and write the same memory locations. For further information, see the Tru64 UNIX Guide to the POSIX Threads Library.

8.2.1 Thread Levels

The debugger supports two levels of threads:

To specify the thread level, set the $threadlevel debugger variable to one of the following strings:

For example:

For core file debugging, the $threadlevel is always set to "native".

8.2.2 Thread Manipulation Commands

You can use 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

8.2.3 Thread Display Commands

You can use the following commands to display threads:
show_thread_command
        : show thread [ thread_id_list ] [ thread-state-filter ]

thread_id_list
        : thread_id ,...
        | *
	
thread_id
        : expression 

thread_state_filter
        : with state eq thread_state

eq
        : ==               (for Ada, C, and C++)
        | .eq.             (for Fortran)
        | =                (for Cobol)
        | equal [ to ]     (for Cobol)

thread_state
        : ready
        | running
        | terminated
        | blocked

Use the show thread command without parameters to list all the threads known to the debugger.

If you specify one or more thread identifiers, the debugger displays information about the threads you specify, if the thread matches what you specified in the list. If you omit a thread specification, the debugger displays information for all threads.

Use the show thread commands to list threads that have specific characteristics, such as threads that are currently blocked. For example:

NOTE: In the output, the right bracket indicator (>) marks the current thread, whereas the asterisk (*) indicator marks the thread with the event that stopped the application.

You can switch to a different thread as the current thread. The debugger variable $curthread contains the thread identifier of the current thread.

switch_thread_command
          : thread [ thread_id ]
The $curthread value is updated when program execution stops or completes. You can modify the current thread by assigning $curthread a valid thread identifier. This is equivalent to issuing the thread thread_id command. When there is no process or program, $curthread is set to 0.

Use the thread command without a thread identifier to identify the current thread. Supply a thread identifier to make another thread the current thread.

8.2.4 Mutex Queries

A mutex (mutual exclusion) semaphore is a programming flag that allows multiple pthreads to synchronize access to shared resources, to ensure the following: Use the show mutex command to list information about currently available pthread mutexes:
show_mutex_variable_command
        : show mutex  [ mutex_id_list ] [ mutex_state_filter ]

mutex_id_list
        : mutex_id  ,...
        | (mutex_id ,...)

mutex_state_filter
        : with state eq mutex_state

eq
        : ==               (for Ada, C, and C++)
        | .eq.             (for Fortran)
        | =                (for Cobol)
        | equal [ to ]     (for Cobol)
	
mutex_state
        : locked
If you specify one or more mutex identifiers, the debugger displays information about only those mutexes specified, provided that the list matches the identifiers of currently available mutexes. If you omit the mutex identifier specification, the debugger displays information about all mutexes currently available.

Use the show mutex with state == locked command to display information exclusively for locked mutexes.

If $verbose is set to 1, the sequence numbers of the threads locking the mutexes are displayed.

The following example shows the output from a simple show mutex command:

If the application being debugged has no pthreads, or if the $threadlevel is set to native, an appropriate message is issued.

8.2.5 Condition Variable Queries

A condition variable is a pthread synchronization object used in conjunction with a mutex. A condition variable is used when a thread has locked a mutex to gain access to data and then finds it must wait for some other thread to change some aspect of the data before it can continue.
show_condition_variable_command
        : show condition [ condition_id_list ] [ condition_state_filter ]

condition_id_list
        : condition_id ,...
        | (condition_id ,...)
	
condition_id
        : integer_constant 
        
condition_state_filter
        : with state eq condition_state

condition_state
        : wait

Use the show condition command to list information about currently available condition variables. If you supply one or more condition identifiers, the debugger displays information about the condition variables you specify, provided that the list matches the identities of currently available condition variables. If you omit the condition variable specification, the debugger displays information about all the condition variables currently available.

Use the show condition with state == wait command to display information only for condition variables that have one or more threads waiting. If $verbose is set to 1, the sequence numbers of the threads waiting on the condition are displayed.

The following example shows output from a simple show condition command:

If the application being debugged has no pthreads, or if the $threadlevel is set to native, an appropriate message is issued.

8.2.6 Other Thread Commmands

You can use the where command to display the stack trace of current threads. You can specify one or more threads or all threads.

The print command evaluates an optional expression in the context of the current thread and displays the result.

The call command evalutes an expression in the context of the current thread and makes the call in the context of the current thread.

The printregs command prints the registers for the current thread.

8.2.7 Undocumented pthread Support

You can pass an undocumented string directly into the undocumented pthread debugging support. This is an internal debugging aid, not intended for general use.

pthread_command
        : pthread string

8.3 Looking at the Call Stack

Most programming languages have some concept of functions, routines, or subroutines, capturing the notion of code that is invoked from many places. A running program needs a call stack of call frames for the called functions. Each call frame contains both the information needed to return to its caller and the information needed to contain the local variables of the function.

The machine code generated for these functions maintains this call stack. Some of this maintenance is done before the call, some at the start of the called function, some at the end of the called function, and some after the call.

Non-optimized machine code is usually very easy to correlate with the source code, but optimized machine code can be tricky. Details of this are given later in this section.

The debugger controls the call stacks of all the threads; you can use it to examine and manipulate call stacks, and use them as a basis for further queries:

call_stack_command
        : show_stack_command
        | change_stack_frame_command
        | pop_stack_frame_command

When your process is stopped by the debugger, you can show the call stack of the thread that caused the stoppage, or the call stack of any other thread.

The following commands show the most recent call frames on the call stack of the current or specified threads:

show_stack_command
        : where [ expression ] [ thread_specifier ]

thread_specifier
        : thread thread_id ,...
        | thread all
	
thread_id
        : expression	

If specified, the expression must evaluate to a nonnegative integer. You can specify the number of call frames to show. If not specified, all the call frames for the thread are shown.

If specified, the thread_specifier specifies the threads whose call stacks are to be shown. If not specified, just the current thread is used.

When large and complex values are passed by value to a routine on the stack, the output of the where command can be voluminous. You can set the control variable $stackargs to 0 to suppress the output of argument values in the where command.

The stack trace provides the following information for each call level:
Call level The number used to refer to a call level on the stack. The function entered most recently is at level 0. Its caller is at level 1.
Memory address The address of the next instruction to be executed at this level.
Function name The name of the function for the memory address.
File name The source file for the memory address.
Line number The number of the next source line of the memory address.

If your call stack has become corrupt, you may want to use the history tool to locate the problem.

8.3.1 Navigating the Call Stack

You can select one of the call frames as the starting point for examining variables. This call frame provides the current scope in the program for which variables exist, and tells the debugger which instance of those variables whose values you want to see.
change_stack_frame_command
        : up   [ expression ]
        | down [ expression ]
        | func [ loc ]

Use the up command or the down command without the expression to change to the call frame located one level up or down the stack. Specify an expression that evaluates to an integer to change the call frame up or down the specified number of levels. If the number of levels exceeds the number of active calls on the stack in the specified direction, the debugger issues a warning message and the call frame does not change.

When the current call frame changes, the debugger displays the source line corresponding to the last instruction executed in the function executing the selected call frame.

When large and complex values are passed by value to a routine on the stack, the output of the up and down commands can be voluminous. You can set the control variable $stackargs to 0 to suppress the output of argument values in the up and down commands.

Use the func command without the loc to display the current function. To change the function scope to a function that has a call frame in the call stack, specify the loc either as the name of the function or as an integer expression evaluating to the call level. If you specify the name, the most-recently entered call frame for that function becomes the current call frame.

If no frames are available to select from, the debugger context is set to the static context of the named function. The current scope and current language are set based on that function. Types and static variables local to that function are now visible and can be evaluated.

If you enter an integer expression, the debugger moves to the frame at level n, just as if you had entered up n at the 0th level function.

In the following example, the current call frame is changed to one for method Planet::print so that a variable in that instance of print() can be displayed:

In the previous example, instead of entering func Planet::print, you can enter down 2. (You would use down in this case because the current call frame at the start of the example was not the bottommost frame.) Note that the final stack trace in this example lists a call frame for function Planet::print as the current call frame (denoted by the > character).

8.3.2 The pop command

The pop command removes one or more call frames from the call stack:
pop_stack_frame_command
        : pop [ expression ]
The default is one call frame. The pop command undoes the work already done by the removed execution frames. It does not, however, reverse side effects, such as changes to global variables.

NOTE: Because it is extremely unlikely this will fix all the effects of a half-executed call, this command is not recommended for general use. Furthermore, the pop command does not provide a way to specify a return value when the frame being discarded corresponds to a function that should return a value. You may need to use the assign command to restore the values of global variables.

Instead of the pop command, you may want to use the return command, which finishes the call corresponding to the selected frame.

8.3.3 Call Frames and Optimized Code

When optimized machine code is generated by the compilers, the compiler generates code that maintains the call stack, but sometimes the function boundaries are changed in one of two ways:

Depending on the information the compiler makes available to the debugger, inlined calls may or may not show up in the call stack display. Outlined calls will show up, and will be correlated to the code they came from. The compiler will probably have supplied the debugger with some invented name for the function.

8.3.4 Call Frames and Machine Code Correlation

On a RISC processor, such as an Alpha processor, the following is the machine code typically generated for a call to a function: When the thread is part way through the call frame creation or tear-down, the debugger will still show the call frame, but will not be able to show correct values for the variables or parameters.

8.3.5 Special C++ Issues

For nonstatic member functions, the implicit this pointer is displayed as the address on the stack trace along with the class type of the object, as shown in the following example:

8.4 Looking at the Data

After you have seen the call stack (show_stack_command), selected the call frame containing the variables you wish to examine (change_stack_frame_command), and looked at the source this function is executing (looking at the source) , you usually want to examine some of the variables or even evaluate some expressions. You can use the print command and the call command to do this. You can also use the following commands to help you determine what to look at and what you are seeing:
look_around_command
        : various_print_command
        | c++_look_around_command
        | call_command
        | whatis_command
        | whereis_command
        | which_command
various_print_command
        : print_command
        | printf_command
        | print_registers_command
        | dump_command

8.4.1 The print Command

You can print the values of one or more expressions or all local variables. You can also use the print command to evaluate complex expressions involving typecasts, pointer dereferences, multiple variables, constants, and any legal operators allowed by the language of the program you are debugging:
print_command
        : print [ expression ,... ]
        | print rescoped_expression
        | print printable-type
        | printb [ expression ,... ]
	| printd [ expression ,... ]
	| printo [ expression ,... ]
	| printx [ expression ,... ]
	    
rescoped_expression
        : filename ` qual_symbol
        | ` qual_symbol

qual_symbol
        : expression
        | qual_symbol ` expression
For an array, the debugger prints every cell in the array if you do not specify a specific cell.

Use the $hexints, $decints, or $octints variables to select a radix for the output of the print command. If you do not want to change the radix permanently, use the printx, printd, printo , and printb commands to print expressions in hexadecimal, decimal, octal, or binary base format, respectively.

Consider the following declarations in a C++ program:

The following example uses the print command to display a nonstring array: The following example shows how to print individual values of an array:
8.4.1.1 Dereferencing Pointers
Pointers are variables that contain addresses. By dereferencing a pointer in the command interface, you can print the value at the address pointed to by the pointer. In C and C++ programs, variables containing a pointer are dereferenced using the * operator. The following example shows how to dereference a pointer in C++ programs:
8.4.1.2 Printing C Strings
The debugger does not print more than the first $maxstrlen characters of a null-terminated string. Change this debugger variable if it is showing either more or less than you wish to see.
8.4.1.3 Restrictions on the print Command
Expressions containing labels are not supported. Variables involving static anonymous unions and enumerated types may not be able to be printed. Printing a structure that is declared but not defined in a compilation unit may generate an error message indicating that the structure is opaque.

8.4.2 The printf Command

Use the printf command to format and display a complex structure. The first argument is a string expression of characters and conversion specifications using the same format specifiers as the printf C function. The printf command requires a running target program because it uses libc.
printf_command
        : printf [ format_string [ , expression  ,... ] ]
For example:

8.4.3 The printi Command

The printi command takes one or more numerical expressions and interprets each one as an assembly instruction, printing out the instruction, and its arguments when applicable. This command is typically used by engineers performing machine level debugging.
printi_command
	: printi [ expression  ,... ]
For example:

8.4.4 The printregs Command

Use the printregs command to display the values of all the hardware registers. The list of registers displayed by the debugger is machine-dependent. By default, most values are displayed in decimal radix. To display the register values in hexidecimal radix, set the $hexints variable to 1.
print_registers_command
        : printregs
For example:

8.4.5 The printt Command

The printt command takes one or more numerical expressions and interprets each one as the number of seconds since the Epoch (00:00:00 UTC 1 Jan 1970; see the ctime(3) reference page for more details).
printt_command
        : printt [ expression  ,... ]
For example:

8.4.6 The dump Command

Use the dump command without an argument to list the parameters and local variables in the current function. To list the parameters and local variables in an active function, specify it as an argument.

Use the dump . command (include the dot) to list the parameters and local variables for all functions active on the stack.

dump_command
        : dump qual_symbol
        | dump .
For example:

When large and complex values are passed by value to a routine on the stack, the output of the dump command can be voluminous. You can set the control variable $stackargs to 0 to suppress the output of argument values in the dump command.

8.4.7 The call Command

After a breakpoint or a signal suspends program execution, you can execute a single function in your program by using the call command, or by including a function call in the expression argument of a debugger command. Calling a function lets you test the function's operation with a specific set of parameters.
call_command
        : call call-expression
Specify the function as if you were calling it from within the program. If the function has no parameters, specify empty parentheses (()). For multithreaded applications, the call is made in the context of the current thread. For C++ applications, when you set the $overloadmenu debugger variable to 1 and call an overloaded function, the debugger lists the overloaded functions and calls the function you specify. When the function you call completes normally, the debugger restores the stack and current context that existed before the function was called.

While the program counter is saved and restored, calling a function does not shield the program state from alteration if the function you call allocates memory or alters global variables. If the function affects global program variables, for instance, those variables will be permanently changed.

Functions compiled without the debugger option to include debugging information may lack important parameter information and are less likely to yield consistent results when called.

The call command executes the specified function with the parameters you supply and then returns control to you (at the Ladebug prompt) when the function returns. The call command discards the return value of the function. If you embed the function call in the expression argument of a print command, the debugger prints the return value after the function returns. The following example shows both methods of calling a function:

In the previous example, the call command results in the return value being discarded while the embedded call passes the return value of the function to the print command, which in turn prints the value. You can also embed the call within a more involved expression, as shown in the following example: All breakpoints or tracepoints defined and enabled during the session are active when executing a called function. When program execution halts during function execution, you can examine program information, execute one line or instruction, continue execution of the function, or call another function.

When you call a function when execution is suspended in a called function, you are nesting function calls, as shown in the following example:

Restrictions on the call Command

The debugger supports function calls and expression evaluations that call functions, with the following limitations:

8.4.8 The whatis Command

You can print information about the basic nature of a whatis_expression. The expression can be a normal language expression or the name of a type, function, or other language entity. The debugger shows you information about the entity rather than evaluating it. However, it will evaluate any contained expressions, such as pointers, needed to determine the entity to which you are referring.
whatis_command
        : whatis whatis_expression
The following example uses the whatis command to determine the storage representation for the data member _classification:

8.4.9 The whereis Command

The whereis command lists all declarations of a variable and each declaration's fully qualified scope information.

The scope information of a variable usually consists of the name of the source file that contains the function in which the variable is declared, the name of that function, and the name of the variable. The components of the scope information are separated by back-quotes (`).

whereis_command
        : whereis whereis_name

whereis_name
        : identifier_or_typedef_name
        | ( identifier_or_typedef_name )
The whereis command is useful for obtaining information needed to differentiate overloaded identifiers that are in different units, or within different routines in the same unit. The following example shows how to set breakpoints in two C++ methods, both named print:

8.4.10 The which Command

Use the which command to determine which declaration an identifier resolves to. The which command shows the fully qualified scope information for the instance of the specified expression in the current scope.

The scope information of a variable usually consists of the name of the source file that contains the function in which the variable is declared, the name of that function, and the name of the variable. The components of the scope information are separated by back-quotes (`).

which_command
        : which which_name

which_name
        : identifier_or_typedef_name
        | ( identifier_or_typedef_name )
The following example shows how to use the whereis and which commands to determine a variable's scope:

8.4.11 Notes on C++ Debugging

The following sections describe the debugger commands specific to debugging C++ programs.
8.4.11.1 Setting the Class Scope Using the class Command
The debugger maintains the concept of a current context in which to perform lookup of program variable names. The current context includes a file scope and either a function scope or a class scope. The debugger automatically updates the current context when program execution suspends.

The class command lets you set the scope to a class in the program you are debugging:

c++_look_around_command
        : class [ class_name ]
If class_name is not specified, the class command displays the current class context.

Setting the class scope nullifies the function scope and vice versa. To return to the default (current function) scope, use the command func 0.

Explicitly setting the debugger's current context to a class enables you to view a class to:

After the class scope is set, you can set breakpoints in the class's member functions and examine data without explicitly mentioning the class name. If you do not want to affect the current context, you can use the scope resolution operator (::) to access a class whose members are not currently visible. Use the class command without an argument to display the current class scope. Specify an argument to change the class scope. After the class scope is set, refer to members of the class by omitting the classname:: prefix.

The following example shows the use of the class command to set the class scope to List in order to make member function append visible so a breakpoint can be set in append:

8.4.11.2 Displaying Class Information
The whatis and print commands display information on a class. Use the whatis command to display static information about the classes. Use the print command to view dynamic information about class objects.

The whatis command displays the class type declaration, including the following:

For classes that are derived from other classes, the data members and member functions inherited from the base class are not displayed. Any member functions that are redefined from the base class are displayed.

The print command lets you display the value of data members and static members. Information regarding the public, private, or protected status of class members is not provided, because the debugger relaxes the related access rules to be more helpful to users.

The type signatures of member functions, constructors, and destructors are displayed in a form that is appropriate for later use in resolving references to overloaded functions.

The following example shows the whatis and print commands in conjunction with a class:

8.4.11.3 Displaying Object Information
The whatis and print commands display information on instances of classes (objects). Use the whatis command to display the class type of an object. Use the print command to display the current value of an object.

You can also display individual object members using the member access operators, period (.) and right arrow (->), in a print command.

You can use the scope resolution operator (::) to refer to global variables, to refer to hidden members in base classes, to explicitly refer to a member that is inherited, or otherwise to name a member hidden by the current context.

When you are in the context of a nested class, you must use the scope resolution operator to access members of the enclosing class.

The following example shows how to use the whatis and print commands to display object information:

8.4.11.4 Displaying Static and Dynamic Type Information
When displaying object information for C++ class pointers or references, you have the option of viewing either static type information or dynamic type information.

The static type of a class pointer or reference is its type as defined in the source code, and thus cannot change. The dynamic type is the type of the object being referenced, before any casts were made to that object, and thus may change during program execution.

The debugger provides a debugger variable, $usedynamictypes, which allows you to control which form of the type information is displayed. The default value for this variable is true (1), which indicates that the dynamic type information is displayed. Setting this variable to false (0) instructs the debugger to display static type information. The output of the print, trace, tracei, and whatis commands are affected.

The display of dynamic type information is supported for C++ class pointers and references. All other types display static type information. In addition, if the dynamic type of an object cannot be determined, the debugger defaults to the use of static type information.

This debugger functionality does not relax the C++ visibility rules regarding object member access through a pointer/reference (only members of the static type are accessible). For more information about the C++ visibility rules, see The Annotated C++ Reference Manual (by Margaret E. Ellis and Bjarne Stroustrup, 1990, Addison-Wesley Publishing Company).

In order for dynamic type information to be displayed, the object's static type must have at least one virtual function defined as part of its interface (either one it introduced or one it inherited from a base class). If no virtual functions are present for an object, only the static type information for that object is available for display.

The following example shows debugger output with $usedynamictypes set to 0 (false):

The following example displays debugger output with $usedynamictypes set to 1 (true). The output is for the same object as the previous example, at the same point in program execution:
8.4.11.5 Displaying Virtual and Inherited Class Information
When you use the print command to display information on an instance of a derived class, the debugger displays both the new class members as well as the members inherited from a base class. Pointers to members of a class are not supported.

When you use the print command to display the format of C++ classes, the class name (or structure/union name) is displayed at the top of the output. Data members of a class that are inherited from another class are commented using a double slash (//). Only those data members that are inherited within the current class being printed are commented.

The following example shows how the debugger uses C++ style comments to identify inherited class members. In the example, class CompoundNode inherits from class IntNode, which inherits from class Node. When printing a class CompoundNode object, the data member _data is commented with "// class IntNode", signifying that it is inherited from class IntNode. The member _nextNode is commented with "// class IntNode::Node", showing that it is inherited from class IntNode, which inherits it from class Node. This commenting is also provided for C++ structs.

If two members in an object have the same name but different base class types (multiple inheritance), you can refer to the members using the following syntax:
     object.class::member
or
     object->class::member
This syntax is more effective than using the object.member and object->member syntaxes, which can be ambiguous. In all cases, the debugger uses the C++ language rules as defined in The Annotated C++ Reference Manual to determine which member you are specifying.

The following example shows a case in which the expanded syntax can be used:

8.4.11.6 Member Functions on the Stack Trace
The implicit this pointer, which is a part of all nonstatic member functions, is displayed as the address on the stack trace. The class type of the object is also given.

Sometimes the debugger does not see class type names with internal linkage. When this happens, the debugger issues the following error message:

     Name is overloaded.
Trying to examine an inlined member function that is not called results in the following error:
     Member function has been inlined.
The debugger will report this error regardless of the setting of the -noinline_auto compilation flag. As a workaround, include a call to the given member function somewhere in your program. (The call does not need to be executed.)

If a program is not compiled with the -g flag, a breakpoint set on an inlined member function may confuse the debugger.

8.4.11.7 Resolving Ambiguous References to Overloaded Functions
In most cases, the debugger works with one specific function at a time. In the case of overloaded function names, you must specify the desired overloaded function. Following are two ways to resolve references to overloaded function names, both under the control of the $overloadmenu debugger variable (the default setting of this debugger variable is 1):
8.4.11.8 Advanced Program Information —Verbose Mode
By default, the debugger gives no information on virtual base class pointers for the following: By setting the $verbose debugger variable to 1, you can request that this information be printed in subsequent debugger responses. When the $verbose debugger variable is set to 1 and you display the contents of a class using the whatis command, several of the class members listed are not in the source code of the original class definition. The following line shows specific output from the whatis command for one of the additional members: The __vptr variable contains the addresses of all virtual functions associated with the class. The compiler generates several other class members for internal use.

The compiler generates additional parameters for nonstatic member functions. When the $verbose debugger variable is set to 1, these extra parameters are displayed as part of each member function's type signature. If you specify a version of an overloaded function by entering its type signature and the variable is set to 1, you must include these parameters. Do not include these parameters if the variable is set to 0.

When the $verbose variable is set to 1, the output of the dump command includes not only standard program variables but also compiler-generated temporary variables.

The following example prints class information using the whatis command under different settings of the $verbose variable:

8.5 Looking at the Generated Code

8.5.1 Memory Display Commands

You can use the following commands to read arbitrary memory locations in your program.
machinecode_level_command
     : examine_command

examine_command
     : address_expression / [ count ] [ mode ]
     | address_expression ? [ count ] [ mode ]
     | address_expression , address_expression / [ mode ]
count
     : integer_constant	
The first command displays the count number of memory values in the requested format, starting at address_expression. If count is not specified, 1 is assumed. The count value must be a positive value.

If you wish to see memory values leading up to the address_expression, use the second command. The second command displays count number of memory values in the requested format ending at the address_expression. If count is not specified, 1 is assumed. The count value must be a positive value.

The third command displays memory values in the requested format starting at the smaller of the two address_expressions and ending at the larger address_expression.

You can display stored values in the following formats by specifying mode:

mode
        : d       Print a short word in decimal
        | dd      Print a 32-bit (4-byte) decimal display
        | D       Print a long word in decimal
        | u       Print a short word in unsigned decimal
        | uu      Print a 32-bit (4-byte) unsigned decimal display
        | U       Print a long word in unsigned decimal
        | o       Print a short word in octal
        | oo      Print a 32-bit (4-byte) octal display
        | O       Print a long word in octal
        | x       Print a short word in hexadecimal
        | xx      Print a 32-bit (4-byte) hexadecimal display
        | X       Print a long word in hexadecimal
        | b       Print a byte in hex
        | c       Print a byte as a character
        | s       Print a string of characters (a C-style string ending in null)
        | C       Print a wide character as a character
        | S       Print a null terminated string of wide characters
        | f       Print a single precision real number
        | g       Print a double precision real number
        | L       Print a long double precision real number
        | i       Disassemble machine instructions
If mode is not specified, the mode used in the previous / command is assumed. If no previous / command exists, X is assumed.

When disassembling machine instructions, use the $regstyle variable to customize how the registers get displayed.

8.5.2 Machine-Level Debugging

The debugger lets you debug your programs at the machine-code level as well as at the source-code level. Using debugger commands, you can examine and edit values in memory, print the values of all machine registers, and step through program execution one machine instruction at a time.

Only those users familiar with machine-language programming and executable-file-code structure will find low-level debugging useful.

For more information on machine-level debugging, see Machine-Level Debugging in Part III.

8.6 Looking at Shared Libraries

shared_library_command
        : listobj
        | readsharedobj filename
        | delsharedobj  filename
Use the listobj command to list all loaded objects, including the main image and the shared libraries. For each object, the information listed consists of the full object name (with pathname) and the starting and ending addresses for the .text, .data, and .bss sections.

Use the readsharedobj command to read in the symbol table information for the specified shared object. This object must be a shared library or loadable kernel module. You can use the command only when you specify the debuggee; that is, either the debugger has been invoked with it, or the debuggee was loaded by the load command.

Conversely, use the delsharedobj command to remove the symbol table information for the shared object from the debugger.

Chapter 9 - 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 target = expression
        | patch  target = expression

target
        : unary_expression

The following sections discuss these commands.

9.1 The assign Command

Use the assign command to change the value associated with a variable, memory address, or expression that is accessible according to the scope and visibility rules of the language. The expression can be any expression that is valid in the current context.

The following example shows how to deposit the value 5 into the data member _data of a C++ object:

The following example shows how to change the value associated with a variable and the value associated with an expression: For C++, use the assign command to modify static and object data members in a class, and variables declared as reference types, type const, or type static. You cannot change the address referred to by a reference type, but you can change the value at that address.
     assign [classname::]member = ["filename"] `expression
        assign [object.]member = ["filename"] `expression

NOTE: Do not use the assign command to change the PC. When you change the PC, no adjustment to the contents of registers or memory is made. This means that if you adjust the PC forward, the skipped instructions are not executed and any changes they would have made will not have happened. It means that if you adjust the PC backward, the instructions you backed up over are not undone, and any changes they made will be in effect when execution continues again.

Because most instructions change registers or memory in ways that can impact the meaning of the application, changing the PC is very likely to cause your application to do incorrect calculations and arrive at the wrong answer. Access violations and other errors and signals may result from changing the value in the PC.

The assign Command in Machine-Level Debugging

You can use the assign command to alter the contents of memory specified by an address. See Machine-Level Debugging for more information.

9.2 The patch Command

Use the patch command to correct bad data or instructions in executable disk files. You can patch the text, initialized data, or read-only data areas. You cannot patch the bss segment, or stack and register locations, because they do not exist on disk files.

Use this command exclusively when you need to change the on-disk binary. Use the assign command when you need only to modify debuggee memory. If the image is executing when you issue the patch command, the corresponding location in the debuggee address space is updated as well. (The debuggee is updated regardless of whether the patch to disk succeeded, as long as the source and destination expressions can be processed by the assign command.) If your program is loaded but not yet started, the patch to disk is performed without the corresponding assign to memory.

NOTE: When you use the patch command, the original binary is not overwritten, but is saved with the string ~backup appended to the file name. This allows you to revert to the original binary if necessary. A file with the string ~temp appended to the file name may also be created. It may be deleted after the debugging session is over.

Chapter 10 - Continuing Execution of the Process

Before continuing the process, you should decide whether or not to make a snapshot, in case you want to revert to that snapshot state and try a different set of steps. After creating the snapshot, use the following commands to continue executing the program:
continue_command
        : step_into_command
        | step_over_command
        | step_out_of_command
        | cont_command
        | cont_from_place_command

10.1 The step and stepi Commands

Use the step command to execute a line of source code. When the line being stepped contains a function call, the step command steps into the function and stops at the first executable statement.

Use the stepi command to step into the next machine instruction. When the instruction contains a function call, the stepi command steps into the function being called.

NOTE: If the instruction is a load locked instruction, special rules apply for stepi.

For multithreaded applications, use these commands to step the current thread while putting all other threads on hold.

If you supply the optional expression argument, the debugger evaluates the expression as a positive integer that specifies the number of times to execute the command. The expression can be any expression that is valid in the current context.

step_into_command
        : step  [ step_number ]
        | stepi [ step_number ]
step_number
        :  expression
In the following example, two step commands continue executing a C++ program: The following example shows stepping by instruction (stepi). To see stepping into calls, see the next example.

10.2 The next and nexti Commands

Use the next command to execute a line of source code. When the next line to be executed contains a function call, the next command executes the function being called and stops the process at the line immediately after the function call.

Use the nexti command to execute a machine instruction. When the instruction contains a function call, the nexti command executes the function being called and stops the process at the instruction immediately after the call instruction.

For multithreaded applications, use these commands to step the current thread while putting all other threads on hold.

If you supply the optional expression argument, the debugger evaluates the expression as a positive integer that specifies the number of times to execute the command. The expression can be any expression that is valid in the current context.

step_over_command
        : next  [ step_number ]
        | nexti [ step_number ]

step_number
        :  expression
For example: The following example shows the difference between stepi and nexti over the same call:

10.3 The return Command

Use the return command without an argument to continue execution of the current function until it returns to its caller. If you include a function name, execution continues until control is returned to the specified function. The function must be active on the call stack.
step_out_of_command
        : return
        | return [qual_symbol_opt]

qual_symbol_opt
	: expression
	| qual_symbol_opt ` expression
In the following example, the next command is used to step through process execution in the append method. The return command finishes the append method and returns control to the caller. The return command is sensitive to the user's location in the call stack. Suppose function A calls function B, which calls function C. Execution has stopped in function C, and you entered the up command, so you were now in function B, at the point where it called function C. Using the return command here would return you to function A, at the point where function A called function B. Functions B and C will have completed execution.

10.4 The cont Command

Use the cont command without a parameter value to resume process execution until a breakpoint, a signal, an error, or normal process termination is encountered. Specify a signal parameter value to send an operating system signal to the process.
cont_command
        : cont [ in loc ]
        | cont [ signal ] [ to_source_line ]
        | number_expression cont [ signal ]
        | conti to address_expression

to_source_line
        : to [filename_string :] line_number

number_expression
        : expression
	
signal
        : integer_constant
	| signal_name
	
When you use the cont command, the debugger resumes execution of the entire process.

In the following example, a cont command resumes process execution after it was suspended by a breakpoint.

The signal parameter value can be either a signal number or a string name (for example, SIGSEGV). The default is 0, which allows the process to continue execution without specifying a signal. If you specify a signal parameter value, the process continues execution with that signal.

Use the in argument to continue until the named function is reached. The function name must be valid. If the function name is overloaded and you do not resolve the scope of the function in the command line, the debugger prompts you with the list of overloaded functions bearing that name from which to choose.

Use the to parameter value to resume execution and then halt when the specified source line is reached. The form of the optional to parameter must be either:

You can repeat the cont command (n +1) times by entering n cont.

You can set a one-time breakpoint on an instruction address before continuing by entering conti to address_expression.

10.4.1 The goto Command

The goto command is intended for advanced users who want to 'skip over' the execution of a portion of source code. In general, its usage is not recommended.
cont_from_place_command
        : goto line_expression

line_expression
	: expression

Chapter 11 - Using Snapshots as an Undo Mechanism

You can save the current state of the debuggee process in a snapshot, and later revert to that state and try a different set of steps. Conceptually speaking, this feature is similar to the undo function in text editors, except that with snapshots you have control of the granularity of each undo. See the Introduction for a quick overview.
snapshot_command
        : save_snapshot_command
        | clone_snapshot_command
        | show_snapshot_command
        | delete_snapshot_command
The following sections discuss these commands and address the limitations of snapshots.

11.1 The save snapshot Command

Use the save snapshot command to save the state of the current process in a snapshot. Snapshots are numbered sequentially starting from 1.
save_snapshot_command
        : save snapshot 
In the following example, the first line of the save snapshot message shows the snapshot_number (1), the time it is saved, and the ID number of the process that implements the snapshot. The next two lines show the status of the snapshot.

11.2 The clone snapshot Command

Use the clone snapshot command to revert the state of the debuggee process to that of a previously saved snapshot. By doing this, you can conveniently return to the state saved in the snapshot as opposed to rerunning the process and re-entering the debugger command sequence that brought you to that state.

Note that rerun and clone snapshot are different in that rerun always executes the process from the beginning, whereas clone snapshot does not execute the process at all; it simply duplicates the saved snapshot (using a mechanism similar to the fork system call) and behaves as though the process execution has stopped at the point when the snapshot was saved.

The clone snapshot command clones the snapshot specified by snapshot_id. If no snapshot_id is specified, the most-recently saved existing snapshot is cloned.

clone_snapshot_command
        : clone snapshot  [ snapshot_id ]

snapshot_id
	: expression

Cloning a snapshot has two side effects:

11.3 The show snapshot Command

Use the show snapshot * and show snapshot all commands to display all the snapshots that have been saved from the current process. Use show snapshot snapshot_id_list to display the snapshots specified. If no snapshots are specified, the most-recently saved existing snapshot is displayed.
show_snapshot_command
        : show snapshot [ snapshot_id_list ]

snapshot_id_list
        : snapshot_id ,...
        | all
        | *
	
snapshot_id
	: expression	
	
For example:

11.4 The delete snapshot Command

Use the delete snapshot * and delete snapshot all commands to delete all the snapshots that have been saved from the current process. Use delete snapshot snapshot_id_list to delete the specified snapshots. If no snapshots are specified, the most-recently saved existing snapshot is deleted.
delete_snapshot_command
        : delete snapshot [ snapshot_id_list ]

snapshot_id_list
        : snapshot_id ,...
        | all
        | *
	
snapshot_id
	: expression	
For example:

11.5 Snapshot limitations

Snapshots have the following limitations:

Chapter 12 - Debugging Optimized Code (Linux Features)

You can debug optimized code under the Tru64 UNIX operating system.

If you are debugging optimized code under the Alpha Linux operating system, the following information applies:

The sections that follow provide some insight into how Compaq compilers and the debugger deal with the consequences of key optimizations and how that may influence debugging of your program.

12.1 Why Debug Optimized Code?

Highly optimizing compilers, such as those supplied by Compaq, generally transform a program in many ways to make your program run as quickly as possible. Expressions may be combined, rearranged or even eliminated if it is determined they are not needed; expressions and even complete statements may be moved out of loops; the order of statements may be changed; calls to functions may be replaced with a copy of the called function (inlining), the instructions for a single statement may be interspersed with instructions for other statements both before and after, and so on. All of these transformations greatly complicate the ability of a debugger to display information about the state and progress of your program or to control its execution. Compaq compilers and the debugger try to avoid presenting erroneous, misleading, or incomplete information when debugging optimized code, but this not always possible. For these reasons, it is almost always easier to debug an unoptimized version of your program.

Why would you ever try to debug an optimized version? The most likely reason is that the program appears to work correctly when unoptimized but somehow fails when optimized. As a result, you may have little choice but to try to isolate the problem using the optimized program.

The most common reason that a program apparently works correctly when unoptimized but fails when optimized is this:

Your program performs some action whose behavior is undefined or implementation dependent, and the optimized version is different from the unoptimized version when performing this action.

For example, your program might read and depend on the value of a variable that was not assigned a value. When executed in unoptimized form, the value that happens to be in that variable might accidently result in the desired behavior. But when optimized, the variable might have some other value that leads to different behavior. As another example, sometimes your program may be subtly dependent on the exact order in which operations are performed—and optimization can result in a different order. There are many other examples that are beyond the scope of this discussion.

It is also possible that there is a bug in the compiler. While it does happen, experience with Compaq compilers indicates that this is rare.

In any case, to determine the cause or nature of the problem requires debugging using the optimized version. Then you can determine how best to resolve the problem. (Of course, you could also choose to reduce the level of optimization, possibly to none, to obtain the desired behavior, but that may not result in acceptable performance.)

12.2 Program Preparation

For Compaq compilers, preparing a program for debugging with optimization involves compiling that program using the -g3 option. All other preparation is unchanged.

Note that the -g3 option differs from the -g option in that it does not affect the optimization level. That is, -g (equivalent to -g2) sets the optimization level to zero (that is, -O0), even overriding an explicit optimization setting; -g3 leaves the optimization level unchanged. See the reference pages for the respective compilers for further details.

12.3 Split Lifetime Variables

A variable is said to have split lifetimes if the set of fetches and stores of the variable can be partitioned such that none of the values stored in one subset are ever fetched in another subset. When such a partition exists, the variable can be split into several independent child variables, each corresponding to a partition. As independent variables, the child variables can be allocated independently. The effect is that the original variable can be thought to reside in different locations at different points in time--sometimes in a register, sometimes in memory, and sometimes nowhere at all. Indeed, it is even possible for the multiple child variables to be active simultaneously.

Split lifetime information in the debugging symbol table describes each of the child variables associated with the main variable, where it is allocated, and the exact range of addresses over which each child is valid.

Because assignments may not occur in the same order as in the source code, the split lifetime information also includes a list of all of the places where the current value may have been assigned. In general, this is a list of possibilities, because several execution paths may converge, bringing together multiple assignment possibilities; the debugger does not trace the exact execution path that reaches a stopping point, so it can only report the set of relevant alternatives.

When a variable does not have a value at the current location, the debugger cannot print a value for it and reports an error as follows:

The first error message line indicates that there is a symbol L, but that it does not currently have a value. The preceding informational line distinguishes between two cases:

If a variable is not declared at all, then the error report looks like the following:

When a variable has a value, there may also be information concerning where that variable was assigned: The value may be assigned from several places: It is possible, though unusual, for the same line to be listed more than once; this means that there is more than one instruction from the same line that assigns a value.

The following limitations apply:

12.4 Semantic Stepping

A major problem with stepping through optimized code by line using the next and step commands is that the apparent source program location "bounces" forward and back, forward and back, with the same line often appearing again and again. In large part this bouncing is due to a compiler optimization called code scheduling, in which instructions that arise from the same source line are scheduled, that is, reordered and intermixed with other instructions, for better execution performance. For example, in sample programs from a prominent benchmark suite, the average number of instructions in sequence that share the same line number is typically between 2 and 3—and typically 50 to 70 percent of those sequences consist of just 1 instruction.

Semantic stepping causes the program to execute up to, but not including, an instruction that causes a semantic effect, as well as being in a different line. Instructions that cause semantic effects are instructions that:

The visible effect of this support is that the step and next commands generally make normal progress through your program. The effects of optimization cannot be hidden entirely, however; there will be some occasional stepping backward as well as forward due to code reordering, and some lines/statements will not appear because they were optimized away, but the result will be generally more usable.

12.5 Discontiguous Scopes

In addition to the blurring of line number boundaries as described in the discussion of semantic stepping, a similar blurring occurs at the beginning and end of an inner scope boundary. That is, one instruction may be related to the beginning of a new scope (and line) in which a new variable is declared and becomes visible, while the next instruction may revert to a source location (line) before the scope begins.

Debugging optimized code information in the symbol table includes a detailed description of the possibly multiple disjoint instruction ranges that belong to or make up a scope. This helps assure that variable lookups find the right symbols at the current location.

You are not likely to directly perceive the effects and benefits of this support; just know that it is part of "getting the right answer" in the presence of optimization.

12.6 General Cautions

When debugging optimized code, some Ladebug commands should be used with caution or even avoided completely.

Use with Caution

down, dump, func, return, where, pop, up

These commands generally depend on there being a distinct call frame on the execution stack for each called function. However, inlining can merge a called routine into the caller, resulting in one frame instead of the two (or more) that might be expected. You can use these commands, but be careful to make sure that you end up in the frame you intend after each use and do not be misled.

next, nexti

For a call that is inlined, the next and nexti commands will appear to step into the called function instead of stepping over it.

Avoid Completely

assign

It is generally not possible to reliably assign to a variable before the value of the variable has been used.

goto

It is generally not possible to reliably determine where the first instruction of a line begins or to avoid repeating instructions from the destination line that may have already been executed.
if logical filter in breakpoint commands
The condition expression may not have a value at all in some places where the expression needs to be evaluated. Worse yet, the debugger sometimes attempts to cache the address of a variable, which does not correctly support split variables.
watch variable
The debugger does not support watching a split variable. This command will most likely fail because the debugger cannot watch a variable (child or otherwise) that is allocated in a register; even if it does appear to succeed, the debugger will be watching the location of just one child, even when that location is not relevant.
operator &
It is generally not possible to reliably determine whether a variable has only one lifetime and thus a unique address.

Chapter 13 - Support Limitations

This chapter contains sections which describe the limitations on support for the following languages:

13.1 Limitations on Support for Compaq C++

The debugger interprets C++ names and expressions using the language rules described in The Annotated C++ Reference Manual (Ellis and Stroustrup, 1990, Addison-Wesley). C++ is a distinct language, rather than a superset of C. Where the semantics of C and C++ differ, the debugger provides the interpretation appropriate for the language of the program being debugged.

To make the debugger more useful, it relaxes some standard C++ name visibility rules. For example, you can reference public, protected, and private class members.

The following limitations apply to debugging a C++ program:

Limitations for debugging templates include the following:

13.2 Limitations on Support for Compaq Fortran

The debugger and the Tru64 UNIX operating system support the Compaq Fortran language with certain limitations, which are described in the following sections.

Be aware of the following data-type limitations when you debug a Fortran program:

The following limitations apply only to Compaq Fortran 90:

13.2.1 Limitations on Procedure Invocations

Following are the limitations on Compaq Fortran procedure invocations:

13.3 Limitations on Support for Compaq Ada

The debugger and the Tru64 UNIX operating system support the Compaq Ada language with certain limitations, which are described in the following sections.

13.3.1 Limitations on Expressions

Expressions in Ladebug commands use C source language syntax for operators and expressions. Data is printed as the equivalent C data type. The following table shows Ada expressions and Ladebug equivalents.

Ada Expression
Debugger Equivalent
Binary operations and unary operations Only integer, floating, and Boolean expressions are easily expressed.
a+b,-,*a+b,-,*
a/ba/b
a = b /= < <= > >= a = = b != < <= > >=
a and ba&&b
a or ba | | b
a rem ba%b
not(a=b)!(a==b)
-a-a
Qualified expressionsNone. There is no easy way of evaluating subtype bounds.
Type conversions Only simple numeric conversions are supported, and the bounds checking cannot be done. Furthermore, float -> integer truncates rather than rounds.

integer -> float

(ladebug) print (float) (2147483647)
2147483648.0
(ladebug) print (double) (2147483647)
2147483647.0

AttributesNone, but if E is an enumeration type with default representations for the values, then:
E'PRED(X) is the same as x-1.
E'SUCC(X) is the same as x+1
p.all *p (pointer reference)
p.m p -> m (member of an "access record" type)

13.3.2 Limitations on Data Types

This section lists the limitation notes by data type. For more information on these types, with examples, see the Developing Ada Programs on Tru64 UNIX Systems manual. Also see the Compaq Ada release notes for detailed information on debugging.

All Types

The debugger, unlike the Ada language, allows out-of-bounds assignments to be performed.

Integer Types

If integer types of different sizes are mixed (for example, byte-integer and word-integer), the one with the smaller size is converted to the larger size.

Floating-Point Types

If integer and floating-point types are mixed in an expression, the debugger converts the integer type to a floating-point type.

The debugger displays floating-point values that are exact integers in integer literal format.

Fixed-Point Types

The debugger displays fixed-point values as real-type literals or as structures. The structure contains values for the sign and the mantissa. To display the structure's value, multiply the sign and mantissa values. For example:

Enumeration Types

The debugger displays enumeration values as the actual enumeral or its position.

You must manually convert enumeration values to 'pos values before you can use them as array indices.

Array Types

The debugger displays string array values in horizontal ASCII format, enclosed in quotation ("x") marks. A single component (character) is displayed within single quotation ('x') marks.

The debugger allows you to assign a component value to a single component; you cannot assign using an entire array or array aggregate.

Arrays whose components are neither a single bit nor a multiple of bytes are described to the debugger as structures; a print command displays only the first component of such arrays.

Records

The debugger cannot display record components whose offsets from the start of the record are not known at compile time.

For variant records, however, the debugger can display the entire record object that has been declared with the default variant value. The debugger allows you to print or assign a value to a component of a record variant that is not active.

Access Types

The debugger does not support allocators, so you cannot create new access objects with the debugger. When you specify the name of an access object, the debugger displays the memory location of the object it designates. You can examine the memory location value.

13.3.3 Limitations on Tasking Programs

When you debug Ada tasking programs, you use the debugger and the DEC Ada ada_debug routine.

13.4 Limitations on Support for Compaq Cobol

13.4.1 Limitations on Assignment

The following limitations apply to assignment in COBOL debugging:

13.4.2 Other Limitations

Other limitations when you debug COBOL programs include the following:

Part III
Advanced Topics

Part III provides information to make advanced use of the debugger by expanding on topics presented in Part II.

It is intended to be used as a reference and therefore contains a wide variation in the level of information provided in each section.

Chapter 14 - Preparing Your Program for Debugging

This chapter describes how you can modify your program to wait for the debugger and to print a stack trace.

14.1 Modifying Your Program to Wait for the Debugger

To modify your program to wait for the debugger, complete the following steps:
  1. Add some code similar to the following :
            void loopForDebugger()
            {
                if (!getenv("loopForDebugger")) return;
                static volatile int debuggerPresent = 0;
                while (!debuggerPresent);
            }
    
  2. Call this function at some convenient point.
  3. Use the debugger's attach capability to get the process under control.

When you have the process under debugger control, you can assign a non-zero value to debuggerPresent, and continue your program. For example:

        % setenv loopForDebugger ""
        % a.out arg1 arg2 &
        % ladebug -pid 1709 a.out
        ^C
        (ladebug) assign debuggerPresent = 1
        ...
        (ladebug) cont

14.2 Modifying Your Program to Print a Stack Trace

This section contains code you can use to modify your program to print a stack trace.

You can also call the debugBreak function to start the debugger at some specified point after your program starts executing.

/*% cc -g traceback.c -lexc
**% a.out
*/
struct exception_info;
/* To suppress compilation warnings from sym.h */
#include <demangle.h>
#include <excpt.h>
#include <setjmp.h>
#include <signal.h>
#include <sym.h>
#include <unistd.h>


static void printHeader(void)
{
    printf("Diagnostic stack trace ...\n");
}


static void printTrailer(void)
{
    printf("end of diagnostic stack trace.\n");
}


#define RETURNADDRREG (26)
#define FAULTING_ADDRESS sc_traparg_a0

extern unsigned long _procedure_string_table;
extern unsigned long _procedure_table_size;
extern unsigned long _procedure_table;
extern unsigned long _ftext;           /* Start address of the program text.*/
extern unsigned long _etext;           /* First address above the program text.*/



/* Turn a PC into the name of the routine containing that PC.
*/
char* programCounterToName(
    unsigned long programCounter);


/* Unwind the stack one level.
*/
void unwindOneLevel(
    struct sigcontext*  unwindSignalContext,
    void *              runtimeProcedurePtr);


void printFrame(
    void *        rpdp,
    unsigned long programCounter)
{
    char* procedureName;

    /* Get the procedure name.
    */
    procedureName = programCounterToName(programCounter);

    printf("%p %s\n",
        (void* ) programCounter,  procedureName ? procedureName : "");
}


char* programCounterToName(
    unsigned long programCounter)
{
    long int          procedureTableSize;
    char *            procedureNameTable;
    char *            procedureName;
    long int          currentProcedure;
    long int          procedure;
    pRPDR             runTimePDPtr;
    int               found;
    unsigned long int address;

    found = 0;
    procedureName = 0;

    procedureTableSize = (long int)&_procedure_table_size;
    procedureNameTable = (char *)&_procedure_string_table;
    runTimePDPtr = (pRPDR)&_procedure_table;

    /* The table of run time procedure descriptors is ordered by
     procedure start address. Search the table (linearly) for the
     first procedure with a start address larger than the one for
     which we are looking.
    */
    for (currentProcedure = 0;
       currentProcedure < procedureTableSize;
       currentProcedure++) {

        /* Because of the way the linker builds the table we need to make
         special cases of the first and last entries. The linker uses 0
         for the address of the procedure in the first entry, here we
         substitute the start address of the text segment. The linker
         uses -1 for the address of the procedure in the last entry,
         here we substitute the end address of the text segment. For all
         other entries the procedure address is obtained from the table.*/

        if (currentProcedure == 0)                             /* first entry */
            address = (unsigned long int)&_ftext;
        else if (currentProcedure == procedureTableSize - 1)   /* last entry */
            address = (unsigned long int)&_etext;
        else                                                   /* other entries */
            address = runTimePDPtr[currentProcedure].adr;

        if (address > programCounter) {
            if (currentProcedure != 0) {
                /* the PC is in this image */
                found = 1;
                procedure = currentProcedure - 1; /* the PC is in preceeding procedure */
                break; /* stop searching */
            } else {
                /* the PC is outside this image (at a lower address) */
                break; /* stop searching */            }
        }
    }

    if (found) {
        /* the PC is inside this image */
        procedureName = &procedureNameTable[runTimePDPtr[procedure].irpss];
    } else {
        /* the PC is outside this image, probably in a different shared object */
        procedureName = 0;
    }

    return procedureName;
}


void unwindOneLevel(
    struct sigcontext *unwindSignalContext,
    void *             runtimeProcedurePtr)
{
    unwind(unwindSignalContext, (pdsc_crd *)runtimeProcedurePtr);
}


/* Print a stack trace.
*/
void printStackWkr(struct sigcontext *signalContextPtr)
{
    struct sigcontext unwindSignalContext;
    void *runTimeProcedurePtr;

    unsigned long int stackpointer    = 0;
    unsigned long int programCounter  = 0;
    unsigned long int returnAddress   = 0;
    unsigned long int faultingAddress = 0;

    printHeader();

    /* Set the initial context for the unwind.
    */
    unwindSignalContext = *signalContextPtr;

    /* Pick out the return address and program counter.
    */
    returnAddress  = unwindSignalContext.sc_regs[RETURNADDRREG];
    programCounter = unwindSignalContext.sc_pc;

    /* This is the address that caused the fault when we tried to access
     it.
    */
    faultingAddress = signalContextPtr->FAULTING_ADDRESS;

    /* Special cases for bogus program counter values. If the program
     counter is zero or the fault occurred when we were trying to
     fetch an instruction (because the program counter itself was bad)
     then we cannot unwind the stack.
    */
    if (programCounter == 0) {

        printf("PC is zero - stack trace not available.\n");

    } else if (programCounter == faultingAddress) {

        printf("bad PC (%p) - stack trace not available.\n",
            faultingAddress);

    } else {

        unsigned int sameSpCount = 0;

        /* Loop through all the stack frames.
        */
        while  ((returnAddress != 0) && (programCounter != 0)) {

            /* Get the run time procedure descriptor for this frame.
            */
            runTimeProcedurePtr = find_rpd(programCounter);

            /* Print a frame.
            */
            printFrame(runTimeProcedurePtr, programCounter);

            /* Unwind one level.
            */
            unwindOneLevel(&unwindSignalContext, runTimeProcedurePtr);
            returnAddress   = unwindSignalContext.sc_regs[RETURNADDRREG];
            programCounter  = unwindSignalContext.sc_pc;

            if (unwindSignalContext.sc_sp <= stackpointer) {
                sameSpCount++;
                if (sameSpCount == 10) break;
            } else {
                sameSpCount  = 0;
                stackpointer = unwindSignalContext.sc_sp;
            }
        }
    }

    printTrailer();
}

/* Discard one stack frame by silently unwinding.
*/
long int discardOneLevel(
    long int programCounter,
    struct sigcontext *signalContextPtr)
{
    void *runTimeProcedurePtr;

    runTimeProcedurePtr = find_rpd(programCounter);
    unwindOneLevel(signalContextPtr, runTimeProcedurePtr);
    return signalContextPtr->sc_pc;
}


void printStack(int levelsToDiscard)
{
    long int programCounter;
    void *runTimeProcedurePtr;
    jmp_buf context;
    struct sigcontext *signalContextPtr;

    /* Capture the current context.
    */
    setjmp(context);
    signalContextPtr = (struct sigcontext *)context;
    programCounter = signalContextPtr->sc_pc;

    /* Discard the frame for this routine.
    */
    programCounter = discardOneLevel(programCounter, signalContextPtr);

    /* Discard any other frames requested by our caller.
    */
    if (levelsToDiscard > 0) {
        int levels;
        for (levels = 0; levels < levelsToDiscard; levels++) {
            programCounter = discardOneLevel(programCounter, signalContextPtr);
        }
    }

    /* Now that we have the right context, print the stack trace.
    */
    printStackWkr(signalContextPtr);

}

/* Example of usage follows. */

void innermost()
{
    printStack(0);
}


void middle()
{
    innermost();
}


int main()
{
    middle();
    return 0;
}

Chapter 15 - Debugger's Command Processing Structure

This chapter is divided into the following sections:

15.1 Lexical Elements of Commands

After the debugger assembles complete lines of input from the characters it reads from stdin or from the file specified in the source command (as described in Part II), each line is then converted into a sequence of lexical elements known as lexemes. For example, print(a++); is converted into the following lexemes:
	print
	(
	a
	++ 
	)
	;

The sequence of lexemes is then recognized as Ladebug commands containing language-specific expressions by a process known as parsing. The recognition is based on a set of rules known as a grammar. The debugger uses a single grammar for commands regardless of the current program's language, but it has language-specific subgrammars for some of the pieces of commands, such as names, expressions, and so on.

15.1.1 Lexical States

The debugger starts processing each line according to the rules of the LKEYWORD lexical state. It recognizes the longest lexeme it can, according to these rules. After recognizing the lexeme, it may stay in the same state, or it may change to a different lexical state with different rules.

The debugger moves between the following lexical states as it recognizes the lexemes:

Lexical State Description
LBPT Breakpoint commands have lexemes that change the lexical state to LBPT.
LEXPORT The command export changes the lexical state to LEXPORT. This state recognizes an evironment variable identifier.
LFILE Commands that require file names have lexemes that change the lexical state to LFILE so that things like mysrc/foo.txt are recognized as a file name, and not as a variable mysrc being divided by a structure data member foo.txt.
LFINT Commands that require either a file name or a process ID have lexemes to change the lexical state to LFINT.
LKEYWORD All but one of the Ladebug commands begin with one or more keywords. The exception is the examine command. The debugger recognizes the '{' and ';' lexemes that must precede a command, and uses those to reset to the LKEYWORD state for the beginning of the next command.
LLINE Commands that require arbitrary character input have lexemes that change the lexical state to LLINE.
LNORM Language expressions involve language-specific lexemes. The lexemes that precede an expression change the lexical state to LNORM, and then LNORM recognizes the language-specific lexemes.
LSETENV The command setenv changes the lexical state to LSETENV. This state recognizes an evironment variable identifier.
LSIGNAL Commands that require signal names have lexemes that change the lexical state to LSIGNAL.
LWORD Commands that require shell words have lexemes that change the lexical state to LWORD.

The rules that each lexical state uses are described in the following sections, in a format known as a lex regular expression. Rather than repeating some of descriptions, a set of common subrules is described first. After the common subrules, we describe the initial state, the rules, and for each recognized token, the next lexical state.

15.1.2 Identifiers

All languages have a concept of an identifier, composed of letters, digits, and other special characters. The debugger also uses keywords composed of letters; therefore, rules are required to determine which identifiers are actually Ladebug keywords.

All Ladebug commands, except examine, begin with leading keywords. Because the examine command begins with an expression, all identifiers must be recognized as such from both the LKEYWORD state that starts commands and the LNORM state that the debugger uses for processing expressions.

Some Ladebug commands have keywords embedded in them following expressions, and the ends of expressions are hard to recognize. You can use identifiers that have the same spelling as an embedded keyword simply by enclosing the whole expression in parentheses (()). For more information on using keywords within commands, see Section 4.4.4.

Furthermore, the C and C++ grammars need to know whether an identifier is a typedef or struct/class identifier. The debugger currently makes the determination at the time the whole command is parsed, rather than deferring the determination to when the expression itself is being evaluated. This can result in a misidentification of the identifier.

When in the following four lexical states, the debugger can recognize identifiers:

If your operating system supports internationalization (I18N), {LT} equates to {LTI18N}.

15.1.3 Embedded Keywords

The complete set of embedded keywords follows:

Lexeme Representation Initial Lexical State Changed Lexical State Language Specific?
ANY any LNORM LNORM Shared by all
AT at LBPT, LNORM LNORM Shared by all
CHANGED changed LNORM LNORM Shared by all
IF if LBPT LNORM Shared by all
IN in LBPT, LNORM LNORM Shared by all
IN_ALL in{Whitespace}all {Whitespace} LBPT, LNORM LNORM Shared by all
POLICY policy LNORM LNORM Shared by all
PRIORITY priority LNORM LNORM Shared by all
READ read LNORM LNORM Shared by all
THREAD thread LNORM LNORM Shared by all
THREAD_ALL thread{Whitespace}all
thread{Whitespace} *
LNORM LNORM Shared by all
TO to LNORM LNORM Shared by all
WITH_STATE with{Whitespace}state LNORM LNORM Shared by all
WITHIN within LNORM LNORM Shared by all
WRITE write LNORM LNORM Shared by all

NOTE: THREAD is both a leading and an embedded keyword.

15.1.4 Leading Keywords

Leading keywords are recognized only at the beginning of commands. You do not need to use parentheses (()) to use them as a normal identifier, unless they occur at the start of an examine command.

Leading keywords may differ between languages. The complete set follows:

Lexeme Representation
(Some May Be Language Specific)
Initial Lexical State Changed Lexical State Language-Specific?
ALIAS alias LKEYWORD LNORM Shared by all
ASSIGN assign LKEYWORD LNORM Shared by all
ATTACH attach LKEYWORD LNORM Shared by all
CALL call LKEYWORD LNORM Shared by all
CATCH catch LKEYWORD LSIGNAL Shared by all
CATCH_UNALIGN catch{Whitespace}unaligned LKEYWORD LNORM Shared by all
CLASS class LKEYWORD LNORM C++ Special Case
CLONE_SNAPSHOT clone{Whitespace}snapshot LKEYWORD LNORM Shared by all
CONDITION condition LKEYWORD LNORM Shared by all
CONT cont LKEYWORD LNORM Shared by all
CONTI conti LKEYWORD LNORM Shared by all
CONT_THREAD cont{Whitespace}thread LKEYWORD LNORM Shared by all
DELETE delete LKEYWORD LNORM Shared by all, also used for C++ special case
DELETE_ALL delete{Whitespace}*
delete{Whitespace}all
LKEYWORD LNORM Shared by all
DELSHAREDOBJ delsharedobj LKEYWORD LFILE Shared by all
DETACH detach LKEYWORD LNORM Shared by all
DISABLE disable LKEYWORD LNORM Shared by all
DISABLE_ALL disable{Whitespace}*
disable{Whitespace}all
LKEYWORD LNORM Shared by all
DOWN down LKEYWORD LNORM Shared by all
DUMP dump LKEYWORD LNORM Shared by all
EDIT edit LKEYWORD LFILE Shared by all
ELSE else LKEYWORD LKEYWORD Shared by all
ENABLE enable LKEYWORD LNORM Shared by all
ENABLE_ALL enable{Whitespace}*
enable{Whitespace}all
LKEYWORD LNORM Shared by all
EXPORT export LKEYWORD LNORM Shared by all
FILECMD file LKEYWORD LFILE Shared by all
FILEEXPR fileexpr LKEYWORD LFILE Shared by all
FUNC func LKEYWORD LNORM Shared by all
GOTO goto LKEYWORD LNORM Shared by all
GUI gui LKEYWORD LNORM Shared by all
HELP help LKEYWORD LLINE Shared by all
HISTORY history LKEYWORD LNORM Shared by all
HPFGET hpfget LKEYWORD LNORM Fortran
IF if LKEYWORD LNORM Shared by all
IGNORE ignore LKEYWORD LSIGNAL Shared by all
IGNORE_UNALIGN ignore{Whitespace}unaligned LKEYWORD LNORM Shared by all
INPUT input LKEYWORD LFILE Shared by all
IO io LKEYWORD LFILE Shared by all
KILL kill LKEYWORD LNORM Shared by all
KPS kps LKEYWORD LNORM Shared by all
LIST list LKEYWORD LNORM Shared by all
LISTOBJ listobj LKEYWORD LNORM Shared by all
LOAD load LKEYWORD LFILE Shared by all
MAP_SOURCE_DIRECTORY map{Whitespace}source{Whitespace}directory LKEYWORD LNORM Shared by all
MUTEX mutex LKEYWORD LNORM Shared by all
NEXT next LKEYWORD LNORM Shared by all
NEXTI nexti LKEYWORD LNORM Shared by all
OUTPUT output LKEYWORD LFILE Shared by all
PATCH patch LKEYWORD LNORM Shared by all
PLAYBACK playback LKEYWORD LKEYWORD Shared by all
POP pop LKEYWORD LNORM Shared by all
PRINT print LKEYWORD LNORM Shared by all
PRINTB printb LKEYWORD LNORM Shared by all
PRINTD printd LKEYWORD LNORM Shared by all
PRINTENV printenv LKEYWORD LNORM Shared by all
PRINTF printf LKEYWORD LNORM Shared by all
PRINTI printi LKEYWORD LNORM Shared by all
PRINTO printo LKEYWORD LNORM Shared by all
PRINTT printt LKEYWORD LNORM Shared by all
PRINTX printx LKEYWORD LNORM Shared by all
PRINTREGS printregs LKEYWORD LNORM Shared by all
PROCESS process LKEYWORD LNORM Shared by all
PROCESS_ALL process{Whitespace}*
process{Whitespace}all
LKEYWORD LNORM Shared by all
PTHREAD pthread LKEYWORD LNORM Shared by all
QUESTION ? LKEYWORD LNORM Shared by all
QUIT quit LKEYWORD LNORM Shared by all
READSHAREDOBJ readsharedobj LKEYWORD LFILE Shared by all
RECORD record LKEYWORD LKEYWORD Shared by all
RERUN rerun LKEYWORD LWORD Shared by all
RETURN return LKEYWORD LNORM Shared by all
RUN run LKEYWORD LWORD Shared by all
SAVE_SNAPSHOT save{Whitespace}snapshot LKEYWORD LNORM Shared by all
SET set LKEYWORD LNORM Shared by all
SETENV setenv LKEYWORD LNORM Shared by all
SH sh LKEYWORD LNORM Shared by all
SHOW show LKEYWORD LKEYWORD Shared by all
SHOW_SOURCE_DIRECTORY show{Whitespace}source{Whitespace} directory LKEYWORD LNORM Shared by all
SHOW_ALL_SOURCE_DIRECTORY show{Whitespace}all{Whitespace} source{Whitespace}directory LKEYWORD LNORM Shared by all
SLASH / LKEYWORD LNORM Shared by all
SNAPSHOT snapshot LKEYWORD LNORM Shared by all
SNAPSHOT_ALL snapshot all LKEYWORD LNORM Shared by all
SNAPSHOT_* snapshot * LKEYWORD LNORM Shared by all
SOURCE source LKEYWORD LFILE Shared by all
STATUS status LKEYWORD LNORM Shared by all
STEP step LKEYWORD LNORM Shared by all
STEPI stepi LKEYWORD LNORM Shared by all
STOP stop LKEYWORD LBPT Shared by all
STOPI stopi LKEYWORD LNORM Shared by all
THREAD thread LKEYWORD LNORM Shared by all
TRACE trace LKEYWORD LNORM Shared by all
TRACEI tracei LKEYWORD LNORM Shared by all
UNALIAS unalias LKEYWORD LNORM Shared by all
UNLOAD unload LKEYWORD LNORM Shared by all
UNMAP_SOURCE_DIRECTORY unmap{Whitespace}source {Whitespace}directory LKEYWORD LNORM Shared by all
UNRECORD unrecord LKEYWORD LNORM Shared by all
UNSET unset LKEYWORD LNORM Shared by all
UNSETENV unsetenv LKEYWORD LNORM Shared by all
UNSETENV_ALL unsetenv{Whitespace}* LKEYWORD LNORM Shared by all
UNUSE unuse LKEYWORD LFILE Shared by all
UP up LKEYWORD LNORM Shared by all
USE use LKEYWORD LFILE Shared by all
VERSION version LKEYWORD LNORM Shared by all
WATCH watch LKEYWORD LNORM Shared by all
WATCH_MEMORY watch{Whitespace}memory LKEYWORD LNORM Shared by all
WATCH_VARIABLE watch{Whitespace}variable LKEYWORD LNORM Shared by all
WHATIS whatis LKEYWORD LNORM Shared by all
WHEN when LKEYWORD LBPT Shared by all
WHENI wheni LKEYWORD LNORM Shared by all
WHERE where LKEYWORD LNORM Shared by all
WHEREIS whereis LKEYWORD LNORM Shared by all
WHERE_THREAD where{Whitespace}thread LKEYWORD LNORM Shared by all
WHERE_THREAD_ALL where{Whitespace}thread {Whitespace}*
where{Whitespace}thread {Whitespace}all
LKEYWORD LNORM Shared by all
WHICH which LKEYWORD LNORM Shared by all

15.1.5 Reserved Identifiers

Some identifiers are recognized as reserved words, regardless of whether they are inside parentheses (()).

The reserved words may differ between languages. The complete set follows:

Lexeme Representation
(Some May Be Language-Specific)
Initial Lexical State Changed Lexical Language-Specific?
AND AND LNORM LNORM COBOL
MOD MOD LNORM LNORM COBOL
NOT NOT LNORM LNORM COBOL
OR OR LNORM LNORM COBOL
BYCONTENT (BY[ \t]+)?CONTENT LNORM LNORM COBOL
BYDESCRIPTOR (BY[ \t]+)?DESCRIPTOR LNORM LNORM COBOL
BYREFERENCE (BY[ \t]+)?REFERENCE LNORM LNORM COBOL
BYVALUE (BY[ \t]+)?VALUE LNORM LNORM COBOL
CHAR char LNORM LNORM C, C++
CLASS class LNORM LNORM C++
CONST const LNORM LNORM C, C++
DELETE delete LNORM LNORM C++
DOUBLE double LNORM LNORM C, C++
ENUM enum LNORM LNORM C, C++
FLOAT float LNORM LNORM C, C++
GIVING GIVING LNORM LNORM COBOL
INT int LNORM LNORM C, C++
LONG long LNORM LNORM C, C++
NEW new LNORM LNORM C++
OF OF LNORM LNORM COBOL
OPERATOR operator LNORM LNORM C++
REFERENCEOF REFERENCE([ \t]+"OF")? LNORM LNORM COBOL
SHORT short LNORM LNORM C, C++
SIGNED signed LNORM LNORM C, C++
SIGNNEG (IS[ \t]+)?"NEGATIVE"
(IS[ \t]+)?"NOT"[ \t]+"POSITIVE"
LNORM LNORM COBOL
SIGNNOTZERO (IS[ \t]+)?"NOT"[ \t]+"ZERO" LNORM LNORM COBOL
SIGNPOS (IS[ \t]+)?"POSITIVE"
(IS[ \t]+)?"NOT"[ \t]+"NEGATIVE"
LNORM LNORM COBOL
SIGNZERO (IS[ \t]+)?"ZERO" LNORM LNORM COBOL
SIZEOF sizeof LNORM LNORM C, C++, Fortran
STRUCT struct LNORM LNORM C, C++
UNION union LNORM LNORM C, C++
UNSIGNED unsigned LNORM LNORM C, C++
USING USING LNORM LNORM COBOL
VOID void LNORM LNORM C, C++
VOLATILE volatile LNORM LNORM C, C++

15.1.6 Lexemes Shared by All Languages

Because the debugger supports multiple languages, some of the rules must be language specific. To distinguish between the characters used for a particular language to represent a lexeme and the lexeme itself, the debugger names the lexemes, rather than using any one language's representation. For example, the lexeme GE corresponds to Fortran's '.GE.', to C's '>=', and to COBOL's 'IS NOT LESS THAN'.

Some lexemes have the same representation in all languages, especially those that form part of the Ladebug commands apart from the language-specific expressions.

15.1.6.1 Common Elements of Lexemes
The following tables list common elements of lexemes.

ConceptRule RepresentationDescription
Decimal digit DG [0-9] One character from '0'..'9'.
Octal digit OC [0-7] One character from '0'..'7'.
Hexadecimal digit HX [0-9a-fA-F] Any of the characters '0'..'9' and any of the letters 'A'..'F' and 'a'..'f'.
Single letter LT [A-Za-z_$] Any of the characters 'A'..'Z', 'a'..'z', and the underscore (_) and dollar sign ($) characters.
Single letter
from the International Character Set
LT18N [A-Za-z_$\200-\377] Any of the characters 'A'..'Z', 'a'..'z', the underscore (_) and dollar sign ($) characters, and any character in the top half of the 8-bit character set.
Shell 'word' WD [^ \t;\n<>'"] Any character except space, tab, semicolon (;), linefeed, less than (<), greater than (>), and quotes (' or ").
File name FL [^ \t\n\}\;\>\<] Any character except space, tab, semicolon (;), linefeed, right brace (}), less than (<), greater than (>), and tick (`).
Optional exponent Exponent [eE][+-]?{DG}+ Numbers often allow an optional exponent. It is represented as an 'e' or 'E' followed by an optional plus (+) or minus (-), and then one or more decimal digits.
Whitespace Whitespace [ \t]+ Whitespace is often used to separate two lexemes that would otherwise be misconstrued as a single lexeme. For example, stop in is two keywords, but stopin is an identifier. Apart from this separating property, Whitespace is usually ignored. Whitespace is a sequence of one or more tabs or spaces.
String literal stringChar ([^"\\\n]|([\\]({simpleEscape}|
{octalEscape}|{hexEscape})))
Any character except the terminating quote character ("), or a new-line (\n). If the character is a backslash (\), it is followed by an escaped sequence of characters.
Character literal charChar ([^'\\\n]|([\\]({simpleEscape}|
{octalEscape}|{hexEscape})))
Any character except the terminating quote (') character, or a new-line (\n). If the character is a backslash (\), it is followed by an escaped sequence of characters.
Environment variable identifier EID [^ \t\n<>;='"&\|] Any character except space, tab, linefeed, less-than (<), greater-than (>), semicolon (;), equal sign (=), quotes (' or "), ampersand (&), backslash (\), and bar (|).

The escaped sequence of characters can be one of following three forms:

ConceptRule RepresentationDescription
Simple escape simpleEscape ([A-Za-z'"?#*\\]) One of 'A'-'Z' or 'a'-'z'. Some of these have special meanings, the most common being 'n' for new-line and 't' for tab. Can be a quote (' or ") character that does not finish the literal, a question mark (?), a pound sign (#), an asterisk (*), or a backslash (\), which then becomes part of the string literal rather than causing a further escape sequence.
Octal escape octalEscape (OC{1,3}) 1 to 3 octal digits, the combined numeric value of which is the character that becomes part of the string literal.
Hexadecimal escape hexEscape ([xX]HX{1,8}) An 'x' or an 'X' followed by 1 to 8 hexadecimal digits, the combined numeric value of which is the character that becomes part of the string literal.

15.1.6.2 Whitespace and Command-Separating Lexemes Shared by All Languages
In all lexical states, unescaped new-lines produce the NEWLINE token and change the lexical state to be LKEYWORD.

In all lexical states except LLINE, a semicolon also changes the lexical state to be LKEYWORD.

Initial State: LKEYWORD, LNORM, LFILE, LLINE, LWORD, LSIGNAL, LBPT
Regular Expression:[\n]
Lexeme:NEWLINE
Change to State:LKEYWORD

This is because SEMICOLON is the command separator.

Initial State: LKEYWORD, LNORM, LFILE, LSIGNAL, LBPT, LWORD
Regular Expression:";"
Lexeme:SEMICOLON
Change to State:LKEYWORD

Commands can be nested, and the following transitions support this:

Initial State: LNORM
Regular Expression:"{"
Lexeme:LBRACE
Change to State:LKEYWORD

Initial State: LKEYWORD, LNORM, LFILE, LSIGNAL, LBPT
Regular Expression:"}"
Lexeme:RBRACE
Change to State:LKEYWORD

In most lexical states, the spaces, tabs, and escaped new-lines are ignored. In the LLINE state, the spaces and tabs are part of the line, but escaped new-lines are still ignored. In the LWORD state, the spaces and tabs are ignored, but escaped new-lines are not.

Initial State: LKEYWORD, LNORM, LFILE, LSIGNAL, LBPT
Regular Expression:[ \t]
\\\n
Lexeme:Ignored
Change to State:Unchanged

Initial State: LLINE
Regular Expression:\\\n
Lexeme:Ignored
Change to State:Unchanged

Initial State: LWORD
Regular Expression:[ \t]
Lexeme:Ignored
Change to State:Unchanged

15.1.6.3 LNORM Lexemes Shared by All Languages
The state stays in LNORM.

LexemeRegular Expression
ANY any
AT at
ATSIGN "@"
CHANGED changed
CHARACTERconstant [lL][']{charChar}+[']
COLON ":"
COMMA ","
DOLLAR "$"
DOT "."
GREATER ">"
HASH unknown
IF if
IN in
IN_ALL in{Whitespace}all{Whitespace}
INTEGERconstant "0"[kK]{HX}+
LESS "<"
LPAREN "("
POLICY policy
PRIORITY priority
RPAREN ")"
READ read
SLASH "/"
STAR "*"
STATE state
STRINGliteral ["]{stringChar}*["]
THREAD thread
THREAD_ALL thread{Whitespace}all
thread{Whitespace}"*"
TICK "`"
TO to
WIDECHARACTERconstant [lL][']{charChar}+[']
WIDESTRINGliteral [lL]["]{stringChar}*["]
WITH with
WITHIN within
WRITE write

15.1.6.4 LBPT Lexemes Shared by All Languages

LexemeRegular Expression Initial Lexical StateChanged Lexical State
IN in LBPTLNORM
IN_ALL in{Whitespace}all LBPTLNORM
INTEGERconstant "0"[kK]{HX}+ LBPTLBPT
AT at LBPTLNORM
PC_IS pc LBPTLNORM
SIGNAL signal LBPTLNORM
UNALIGNED unaligned LBPTLNORM
VARIABLE variable LBPTLNORM
MEMORY memory LBPTLNORM
EVERY_INSTRUCTION every{Whitespace}instructionLBPTLNORM
EVERY_PROC_ENTRY every{Whitespace}proc[edure]{Whitespace}entry LBPTLNORM
QUIET quiet LBPTLBPT

15.1.6.5 LFILE Lexemes Shared by All Languages
Files are one or more characters that can appear in a file name.

The state is left as LFILE, so that commands such as use and unuse can have lists of files.

LexemeRegular Expression
FILENAME {FL}+

15.1.6.6 LKEYWORD Lexemes Shared by All Languages
The state remains in LKEYWORD.

LexemeRegular Expression
INTEGERconstant "0"{OC}+
"0"[xX]{HX}+
{DG}+
"0"[kK]{HX}+

15.1.6.7 LLINE Lexemes Shared by All Languages
All characters up to the next new-line are assembled into a STRINGliteral.
15.1.6.8 LWORD Lexemes Shared by All Languages
Once the lexical state has been set to LWORD, it will stay there until a NEWLINE or a SEMICOLON is found. Both of these cause the lexical state to become LKEYWORD again. The individual words recognized can be any of the following, but in each case, the state stays LWORD.

LexemeRegular Expression
GREATER ">"
LESS "<"
GREATERAMPERSAND ">&"
ONEGREATER "1>"
TWOGREATER "2>"
STRINGliteral [']{charChar}*[']
["]{stringChar}*["]
STRINGliteral {WD}* that does not end in a backslash
WIDECHARACTERconstant [lL][']{charChar}+[']
WIDESTRINGliteral [lL]["]{stringChar}*["]

15.1.6.9 LSIGNAL Lexemes Shared by All Languages
The state stays in LSIGNAL.

LexemeRegular Expression
INTEGERconstant {DG}+
IDENTIFIER {LT}({LT}|{DG})*

15.1.6.10 LSETENV and LEXPORT Lexemes Shared by All Languages
LexemeRegular Expression
ENVARID {EID}+

15.1.7 Lexemes That Are Represented Differently in Each Language

Lexeme Representation
(Some May Be Language-Specific)
Initial Lexical State Changed Lexical State Language Specific?
ALPHASDIGITSI18N [0-9A-Za-z_$\200-\377] LNORM, LBPT LNORM COBOL
ALPHASDIGITS
HYPHENI18N
[0-9A-Za-z_$\200-\377\-] LNORM, LBPT LNORM COBOL
AMPERSAND "&" LNORMUnchanged C, C++, Fortran, Ada
AND AND LNORMUnchanged COBOL
ANDAND "&&" LNORMUnchanged C, C++, Ada
ANDassign "&=" LNORMUnchanged C, C++
ARROW "->" LNORM Unchanged C, C++, Ada
ARROWstar "->*" LNORM Unchanged C++
ASSIGNOP "=" LNORM Unchanged C, C++, Fortran, Ada, COBOL
BRACKETS "[]" LNORM Unchanged C, C++, Ada
CLCL "::" LNORM Unchanged C++
DECR "--" LNORM Unchanged C, C++, Ada
DIVassign "/=" LNORM Unchanged C, C++, Ada
DOTstar ".*" LNORM Unchanged C++
ELLIPSIS "..." LNORM Unchanged C++
EQ "=="
".EQ."
(IS[ \t]+)?
  ("="|("EQUAL"([ \t]+"TO")?))
LNORM Unchanged C, C++, Fortran, Ada, COBOL
ERassign "^=" LNORM Unchanged C, C++
EXPON "**" LNORM Unchanged COBOL
GE ">="
".GE."
(IS[ \t]+)?
  "NOT"[ \t]+
  ("<"|("LESS"([ \t]+"THAN")?))
(IS[ \t]+)?
  (">="|("GREATER"([ \t]+"THAN")?[ \t]
  +"OR"[ \t]+"EQUAL"([ \t]+"TO")?))
LNORM Unchanged C, C++, Fortran, Ada, COBOL
GREATER ">"
".GT."
(IS[ \t]+)?
  (">"|("GREATER"([ \t]+"THAN")?))
LNORM Unchanged Shared by all, also used for Fortran and COBOL special cases
HAT "^" LNORM Unchanged C, C++, Ada
INCR "++" LNORM Unchanged C, C++, Ada
LBRACKET "[" LNORM Unchanged C, C++, Fortran, Ada
LE "<="
".LE."
(IS[ \t]+)?"NOT"[ \t]+
  (">"|("GREATER"([ \t]+"THAN")?))
(IS[ \t]+)?
  ("<="|("LESS"([ \t]+"THAN")?[ \t]+
  "OR"[ \t]+"EQUAL"([ \t]+"TO")?))
LNORM Unchanged C, C++, Fortran, Ada, COBOL
LESS "<"
".LT."
(IS[ \t]+)?
  ("<"|("LESS"([ \t]+"THAN")?))
LNORM Unchanged Shared by all, also used for Fortran and COBOL special cases
LOGAND ".AND." LNORM Unchanged Fortran
LOGEQV ".EQV." LNORM Unchanged Fortran
LOGNEQV ".NEQV." LNORM Unchanged Fortran
LOGNOT ".NOT." LNORM Unchanged Fortran
LOGOR ".OR." LNORM Unchanged Fortran
LOGXOR ".XOR." LNORM Unchanged Fortran
LS "<<" LNORM Unchanged C, C++, Ada
LSassign "<<=" LNORM Unchanged C, C++
MINUS "-" LNORM Unchanged C, C++, Fortran, Ada, COBOL
MINUSassign "-=" LNORM Unchanged C, C++
MOD "%"
MOD
LNORM Unchanged C, C++, Ada, COBOL
MODassign "%=" LNORM Unchanged C, C++, Ada
MULTassign "*=" LNORM Unchanged C, C++, Ada
NE "!="
".NE."
(IS[ \t]+)?
  "NOT"[ \t]+("="|("EQUAL"([ \t]+"TO")?))
LNORM Unchanged C, C++, Fortran, Ada
NOT "!"
NOT
LNORM Unchanged C, C++, Ada, COBOL
OPENSLASH "(/" LNORM Unchanged Fortran
OR "|"
OR
LNORM Unchanged C, C++, Ada, COBOL
OROR "||" LNORM Unchanged C, C++, Ada
ORassign "|=" LNORM Unchanged C, C++
PARENS "()" LNORM Unchanged C++
PERCENT "%" LNORM Unchanged Fortran
PLUS "+" LNORM Unchanged C, C++, Fortran, COBOL
PLUSassign "+=" LNORM Unchanged C, C++, Ada
QUESTION "?" LNORM Unchanged C, C++, Ada
RBRACKET "]" LNORM Unchanged C, C++, Fortran, Ada
RS ">>" LNORM Unchanged C, C++, Ada
RSassign ">>=" LNORM Unchanged C, C++
SLASHCLOSE "/)" LNORM Unchanged Fortran
SLASHSLASH "//" LNORM Unchanged Fortran
STARSTAR "**" LNORM Unchanged Fortran
TWIDDLE "~" LNORM Unchanged C, C++, Ada
15.1.7.1 LKEYWORD Lexemes Specific to C++
If a C++ identifier is followed by a "::", it is assumed to be a class or namespace identifier.

If a C++ identifier is followed by a "<", complex and dubious checks are made to try to match a complete template instance specifier.

15.1.7.2 LNORM Lexemes Specific to C and C++
The lexemes in the following table are specific to C and C++. The state stays in LNORM.

Lexeme Representation Language
ARROW "->" C, C++
INCR "++" C, C++
DECR "--" C, C++
LS "<<" C, C++
RS ">>" C, C++
LE "<=" C, C++
GE ">=" C, C++
EQ "==" C, C++
NE "!=" C, C++
ANDAND "&&" C, C++
OROR "||" C, C++
MULTassign "*=" C, C++
DIVassign "/=" C, C++
MODassign "%=" C, C++
PLUSassign "+=" C, C++
MINUSassign "-=" C, C++
LSassign "<<=" C, C++
RSassign ">>=" C, C++
ANDassign "&=" C, C++
ERassign "^=" C, C++
ORassign "|=" C, C++
PLUS "+" C, C++
MINUS "-" C, C++
MOD "%" C, C++
HAT "^" C, C++
AMPERSAND "&" C, C++
OR "|" C, C++
TWIDDLE "~" C, C++
NOT "!" C, C++
BRACKETS "[]" C, C++
ASSIGNOP "=" C, C++
LBRACKET "[" C, C++
RBRACKET "]" C, C++
QUESTION "?" C, C++
CHAR char C, C++
DOUBLE double C, C++
FLOAT float C, C++
INT int C, C++
LONG long C, C++
SHORT short C, C++
SIGNED signed C, C++
UNSIGNED unsigned C, C++
VOID void C, C++
CONST const C, C++
VOLATILE volatile C, C++
SIZEOF sizeof C, C++
ENUM enum C, C++
STRUCT struct C, C++
UNION union C, C++
INTEGERconstant "0"{OC}+
"0"[xX]{HX}+
{DG}+
C, C++
FLOATINGconstant {DG}*"."{DG}*
{DG}*"."{DG}* {Whitespace}?{Exponent}
{DG}+{Whitespace}?{Exponent }
C, C++
IDENTIFIER, TYPEDEFname {LT}({LT}|{DG})* C, C++

The lexemes in the following table are specific to C++. The state stays in LNORM.

Lexeme Representation Language
OPERATOR operator C++
NEW new C++
DELETE delete C++
CLASS class C++
ELLIPSIS "..." C++
CLCL "::" C++
THIS this C++
DOTstar ".*" C++
ARROWstar "->*" C++

15.1.7.3 LNORM Lexemes Specific to Fortran
The lexemes in the following table are specific to Fortran. The state stays in LNORM.

Lexeme Representation
PLUS "+"
MINUS "-"
STARSTAR "**"
LESS ".LT."
LE ".LE."
EQ ".EQ."
NE ".NE."
GE ".GE."
GREATER ".GT."
LE "<="
EQ "=="
NE "/="
GE ">="
LOGNOT ".NOT."
LOGAND ".AND."
LOGOR ".OR."
LOGEQV ".EQV."
LOGNEQV ".NEQV."
LOGXOR ".XOR."
PERCENT "%"
ASSIGNOP "="
SLASHSLASH "//"
OPENSLASH "(/"
SLASHCLOSE "/)"
LBRACKET "["
RBRACKET "]"
AMPERSAND "&"
SIZEOF sizeof
INTEGERconstant ".TRUE."
".FALSE."
"0"{OC}+
"0X"{HX}+
{DG}+
IDENTIFIER, TYPEDEFname {LT}({LT}|{DG})*
FortranName [A-Za-z$]({LT}|{DG})*
FortranNamedKind "_"{FortranName}
FortranNumericKind "_"{DG}+
FortranKind {FortranNamedKind}
{FortranNumericKind}
FortranCharacterNamedKind {FortranName}"_"
FortranCharacterNumericKind {DG}+"_"
FortranCharacterKind {FortranCharacterNamedKind}
{FortranCharacterNumericKind}
RealWithDecimal ({DG}+"."{DG}*)
({DG}*"."{DG}+)
ExponentVal [+-]?{DG}+
RealEExponent [Ee]{ExponentVal}
RealDExponent [Dd]{ExponentVal}
RealQExponent [Qq]{ExponentVal}
RealSingleConstant (({DG}+{RealEExponent})
({RealWithDecimal}{RealEExponent}?)){FortranKind}?
RealDoubleConstant ({DG}+|{RealWithDecimal}){RealDExponent}
RealQuadConstant ({DG}+|{RealWithDecimal}){RealQExponent}
RealConstant {RealSingleConstant}
{RealDoubleConstant}
{RealQuadConstant}
REALconstantWithKind RealConstant
FortranBinaryValue [Bb]((['][01]+['])|(["][01]+["]))
FortranOctalValue [Oo](([']{OC}+['])|(["]{OC}+["]))
FortranHexValue [Zz](([']{HX}+['])|(["]{HX}+["]))
FortranOctalValueAlternative (([']{OC}+['])|(["]{OC}+["]))[Oo]
FortranHexValueAlternative (([']{HX}+['])|(["]{HX}+["]))[Xx]
INTEGERconstantWithKind {DG}+{FortranKind}
{DG}*"#"[0-9A-Za-z]+
{FortranBinaryValue}
{FortranOctalValue}
{FortranHexValue}
{FortranOctalValueAlternative}
{FortranHexValueAlternative}
LOGICALconstantWithKind ".TRUE."{FortranKind}?
".FALSE."{FortranKind}?
CharSingleDelim [^'\\\n]|('')
CharDoubleDelim [^"\\\n]|("")
FortranOctalEscape {OC}{1,3}
FortranHexEscape [Xx]{HX}{1,2}
FortranEscapeChar [\\]([AaBbFfNnRrTtVv]|{FortranOctalEscape}
                         |{FortranHexEscape}|0|[\\])
StringSingleDelim [']({CharSingleDelim}|[\\])*[']
StringDoubleDelim ["]({CharDoubleDelim}|[\\])*["]
FortranString {StringSingleDelim}
{StringDoubleDelim}
CStringSingleDelim [']({CharSingleDelim}|{FortranEscapeChar})*[']
CStringDoubleDelim ["]({CharDoubleDelim}|{FortranEscapeChar})*["]
FortranCString ({CStringSingleDelim}|{CStringDoubleDelim})[Cc]
CHARACTERconstantWithKind {FortranString}
{FortranCharacterKind}{FortranString}
{FortranCharacterKind}?{FortranCString}

15.1.7.4 LNORM Lexemes Specific to Ada
The lexemes in the following table are specific to Ada. The state stays in LNORM.

Lexeme Representation
ARROW "->"
INCR "++"
DECR "--"
LS "<<"
RS ">>"
LE "<="
GE ">="
EQ "=="
NE "!="
ANDAND "&&"
OROR "||"
MULTassign "*="
DIVassign "/="
MODassign "%="
PLUSassign "+="
MINUSassign "-="
LSassign "<<="
RSassign ">>="
ANDassign "&="
ERassign "^="
ORassign "|="
PLUS "+"
MINUS "-"
MOD "%"
HAT "^"
AMPERSAND "&"
OR "|"
TWIDDLE "~"
NOT "!"
BRACKETS "[]"
ASSIGNOP "="
LBRACKET "["
RBRACKET "]"
QUESTION "?"
INTEGERconstant "0"{OC}+
"0X"{HX}+
{DG}+
FLOATINGconstant {DG}*"."{DG}+
{DG}*"."{DG}*{Whitespace}? {Exponent}
{DG}+{Whitespace}?{Exponent}
IDENTIFIER, TYPEDEFname {LT}({LT}|{DG})*

15.1.7.5 LNORM Lexemes Specific to COBOL
The lexemes in the following table are specific to COBOL. The state stays in LNORM.

Lexeme Representation
ALPHASDIGITSI18N [0-9A-Za-z_$\200-\377]
ALPHASDIGITSHYPHENI18N [0-9\-A-Za-z_$\200-\377]
CobolWord {ALPHASDIGITSI18N}|({ALPHASDIGITSI18N}{ALPHASDIGITSI18N})
|({ALPHASDIGITSI18N}+{ALPHASDIGITSHYPHENI18N}
+{ALPHASDIGITSI18N}+)
ASSIGNOP "="
PLUS "+"
MINUS "-"
EXPON "**"
AMPERSAND "&"
AND AND
MOD MOD
NOT NOT
OR OR
REFERENCEOF REFERENCE([ \t]+"OF")?
OF OF
GIVING GIVING
USING USING
BYREFERENCE (BY[ \t]+)?"REFERENCE"
BYVALUE (BY[ \t]+)?"VALUE"
BYCONTENT (BY[ \t]+)?"CONTENT"
BYDESCRIPTOR (BY[ \t]+)?"DESCRIPTOR"
SIGNPOS (IS[ \t]+)?"POSITIVE"
(IS[ \t]+)?"NOT"[ \t]+"NEGATIVE"
SIGNNEG (IS[ \t]+)?"NEGATIVE"
(IS[ \t]+)?"NOT"[ \t]+"POSITIVE"
SIGNZERO (IS[ \t]+)?"ZERO"
SIGNNOTZERO (IS[ \t]+)?"NOT"[ \t]+"ZERO"
EQ (IS[ \t]+)?("="|("EQUAL"([ \t]+"TO")?))
LESS (IS[ \t]+)?("<"|("LESS"([ \t]+"THAN")?))
GREATER (IS[ \t]+)?(">"|("GREATER"([ \t]+"THAN")?))
NE (IS[ \t]+)?"NOT"[ \t]+("="|("EQUAL"([ \t]+"TO")?))
GE (IS[ \t]+)?"NOT"[ \t]+("<"|("LESS"([ \t]+"THAN")?))
(IS[ \t]+)?(">="|("GREATER"([ \t]+"THAN")?[ \t]+"OR"[ \t]+"EQUAL"([ \t]+"TO")?))
LE (IS[ \t]+)?"NOT"[ \t]+(">"|("GREATER"([ \t]+"THAN")?))
(IS[ \t]+)?("<="|("LESS" ([ \t]+"THAN")?[ \t]+"OR"[ \t]+"EQUAL"([ \t]+"TO")?))
DECIMALconstant {DecLit}
INTEGERconstant {IntLit}
{CHexLit} HEXADECIMAL
{CobolHexLit} HEXADECIMAL
FLOATINGconstant {FloatLit}
IDENTIFIER {CobolWord}

15.2 Grammar of Commands

Most of the grammar for commands has already been given in previous sections. This section concentrates on the grammar for expressions.

15.2.1 The Names and Expressions Within Commands

The exact syntax of expressions are specific to the current language.
expression
	: expression for C
        | expression for C++
        | expression for Fortran
        | expression for Ada
        | expression for COBOL
Often you can omit an expression from a command or use a convenient default instead, to change the meaning of a command.
expression-opt
        : [ expression ]
Identifiers, Keyword, and Typedef Names
The debugger uses the normal language lookup rules for identifiers, (obeying scopes, and so on,) but also extends those rules as follows: These rules can be subverted by rescoping the name.

NOTE: The debugger does not know where in the scope a declaration occurred, so all lookups consider all identifiers in the scope, whether or not they occurred before the current line.

The lexical tokens for identifiers are specific to the current language, and also to the current lexical state.

IDENTIFIER
        : identifier for LSIGNAL lexical state
        | identifier for C
        | identifier for C++
        | identifier for Fortran
        | identifier for Ada
        | identifier for COBOL
TYPEDEFnames are lexically just identifiers, but when looking them up in the current scope, the debugger determines that they refer to types, such as typedefs, classes, or structs. This information is needed to correctly parse C and C++ expressions.
TYPEDEFname
        : IDENTIFIER
A few lexical tokens act as embedded keywords in some positions within expressions, but the debugger generally tries to accept them as though they were normal identifiers.
identifier-or-key-word
        : IDENTIFIER
        | embedded-key-word

embedded-key-word
        : ANY
        | CHANGED
        | READ
        | WRITE
In other contexts, the debugger is also prepared to accept TYPEDEFnames (for example, int or the name of a class).
identifier-or-typedef-name
        : identifier-or-typedef-name for C
        | identifier-or-typedef-name for C++
        | identifier-or-typedef-name for Fortran
        | identifier-or-typedef-name for Ada
        | identifier-or-typedef-name for COBOL
Integer Constants
The lexical tokens for integer constants are specific to the current language.
integer_constant
    	: INTEGERconstant for C and C++
    	| INTEGERconstant for Fortran
    	| INTEGERconstant for Ada
    	| INTEGERconstant for COBOL
Macros
The debugger does not currently understand usages of macros, for example, uses of C and C++ preprocessor #define macros, and so on.
Calls
You can call any function whose address can be taken, provided that the parameters can also be passed, and the result returned.
call-expression
	: call-expression for C
	| call-expression for C++
	| call-expression for Fortran
	| call-expression for Ada 
	| call-expression for COBOL
Parameters
Each language may impose its own restrictions on exactly what can be passed as a parameter.

Any expression can be passed 'by value', but C++ constructors and destructors will not be invoked. Evaluating parameters can involve evaluating nested calls.

Anything whose address can be taken can be passed 'by reference'.

The debugger has very limited understanding of array descriptors.

Comma is both the argument separator and a valid operator in C and C++. Hence, argument lists are comma-separated assignment-expressions rather than full expressions.

argument-expression-list
        : assignment-expression
        | assignment-expression COMMA argument-expression-list

arg-expression-list-opt
        : [ argument-expression-list ]

assignment-expression
        : assignment-expression for C
        | assignment-expression for C++
        | assignment-expression for Fortran
        | assignment-expression for Ada
        | assignment-expression for COBOL
Return Results
Any scalar or structure type can be the return result of a called function. Some simple array types are also supported, but the general cases are not.

The C++ constructors and destructors are not invoked, which may cause problems.

Addresses
You can take the addresses of variables and other data that are in memory, and functions that have had code generated for them. You can also take the address of a line of source code.

Some variables may be in registers; you cannot take their addresses.

The optimizing compilers may move variables from one memory location to another, in which case you will obtain the address of the current memory location of the variable.

The optimizing compilers may eliminate unused functions, as well as functions that have had all calls inlined. Static functions in header files may result in multiple copies of the code, and the address will be of only one of those copies.

The optimizing compilers and linkers may skip some instructions on the way in during a call, so a breakpoint on the first few instructions may not be hit. When you set a breakpoint on a function, the debugger sets it deeper in the function, at the end of the entry sequence, to try to avoid this.

The address of a line of source code is the address of the first instruction in memory that came from this line, but this instruction may be branched around, so it might not be executed before any other instruction from the same line.

Address of a Source Line
The debugger has extended the syntax of most languages to allow you to get the address of the first instruction that a source line generates. If you do not specify a file via the string, then the current file is used. If you specify a DOLLAR as the line-number, then the last line in the file that generated any instructions is used.
line-address
        : ATSIGN string COLON line-number
        | ATSIGN line-number

line-number
        : INTEGERconstant
        | DOLLAR
Other Modified Forms of Expressions
The whatis_command supports supersets of the normal expression syntax of the language.
whatis-expressions
        : whatis-expressions for C
        | whatis-expressions for C++
        | whatis-expressions for Fortran
        | whatis-expressions for Ada
        | whatis-expressions for COBOL
Some commands (notably the examine command and the cont command) have a syntax that inhibits the use of a full expression. In this case, a more limited form of expression is still allowed.
address-exp
        : address-exp for C
        | address-exp for C++
        | address-exp for Fortran
        | address-exp for Ada
        | address-exp for COBOL
The cont command and the change_stack_frame_commands have a form that specifies where to continue to, or where to cut the stack back to.
loc
        : loc for C
        | loc for C++
        | loc for Fortran
        | loc for Ada
        | loc for COBOL
The target of a modifying_command can only be a subset of the possible expressions, known as a unary-expression.
unary-expression
        : unary-expression for C
        | unary-expression for C++
        | unary-expression for Fortran
        | unary-expression for Ada
        | unary-expression for COBOL
Strings
The syntax of strings is sensitive to the current lexical state and language.
string
        : LNORM string
        | LLINE string
        | LWORD string
Most of the languages have places where they allow a series of string literals to be equivalent to a single string formed of their concatenated characters.
string-literal-list
        : string-literal-list for C
        | string-literal-list for C++
        | string-literal-list for Ada
Rescoped Expressions
Sometimes the normal language visibility rules are not sufficient for specifying the variable, function, and so on, to which you may want to refer. The debugger extends the language's idea of an expression with an additional possibility called a rescoped expression.

Rescoped expressions cause the debugger to look up the identifiers and so on in the qual-symbol-opt, as though it were in the source file specified by the preceding filename-tick or qual-symbol-opt.

rescoped-expression
        : filename-tick qual-symbol-opt
        | TICK qual-symbol-opt

rescoped-typedef
        : filename-tick qual-typedef-opt
        | TICK qual-typedef-opt

filename-tick
        : string-tick

string-tick
        : string TICK

qual-symbol-opt
        : expression                        /* Base (global) name */
        | qual-symbol-opt TICK expression   /* Qualified name */

qual-typedef-opt
        : qual-typedef-opt for C
        | qual-typedef-opt for C++
        | qual-typedef-opt for Fortran
        | qual-typedef-opt for Ada
        | qual-typedef-opt for COBOL
In the following example, rescoped expressions are used to distinguish which x the user is querying, because there are two variables named x (one local to main and one global): By default, a local variable is found before a global one, so that the plain x refers to the local variable. You may use the C++ :: operator to specify the global x in C++ code, or the debugger's rescoping specification in any language.

In the following example, the x variable is used in the following places to demonstrate how rescoping expressions can find the correct variable:

Printable Types
The lexical tokens for printable types are specific to the current language.
printable-type
    	: printable-type for C
    	| printable-type for C++
    	| printable-type for Fortran
    	| printable-type for Ada
    	| printable-type for COBOL

15.2.2 Expressions Specific to C

The debugger has an almost complete understanding of C expressions, given the general restrictions.
expression
        : assignment-expression

constant-expression
        : conditional-expression
C Identifiers
The lookup rules are almost always correct for C.
identifier-or-typedef-name
        : identifier-or-key-word
        | TYPEDEFname
C Constants
The numeric constants are treated exactly the same as in C. The enumeration constant identifiers go though the same grammar paths as variable identifiers, which has basically the same effect as the C semantics.
primary-expression
        : identifier-or-key-word
        | constant
        | string-literal-list
        | LPAREN expression RPAREN

string-literal-list
        : string
        | string-literal-list string

constant
        : FLOATINGconstant
        | INTEGERconstant
        | CHARACTERconstant
        | WIDECHARACTERconstant
        | WIDESTRINGliteral
C Rescoped Expressions
The C implementation of rescoped expressions is the following:
qual-typedef-opt
        : TYPEDEFname
        | qual-typedef-opt TICK TYPEDEFname

whatis-expressions
        : expression
        | rescoped-expression
        | printable-type
C Calls
Following is the C implementation of calls.
call-expression
        : expression

function-call
        : postfix-expression LPAREN [arg-expression-list] RPAREN
Restrictions and limits are documented here.
C Addresses
Following is the C implementation of addresses.
address
        : AMPERSAND postfix-expression
        | line-address
        | postfix-expression

address-exp
        : address
        | address-exp PLUS  address
        | address-exp MINUS address
        | address-exp STAR  address
Restrictions and limits are documented here.
C Loc Specifications
The C implementation of loc is the following:
loc
        : expression
        | rescoped-expression
C Types
The debugger understands the full C type specification grammar.
type-specifier
        : basic-type-specifier
        | struct-union-enum-type-specifier
        | typedef-type-specifier

basic-type-specifier
        : basic-type-name
        | type-qualifier-list basic-type-name
        | basic-type-specifier type-qualifier
        | basic-type-specifier basic-type-name

type-qualifier-list
        : type-qualifier
        | type-qualifier-list type-qualifier

type-qualifier
        : CONST
        | VOLATILE

basic-type-name
        : VOID
        | CHAR
        | SHORT
        | INT
        | LONG
        | FLOAT
        | DOUBLE
        | SIGNED
        | UNSIGNED
	
printable-type
        : rescoped_typedef
	| type_name	

struct-union-enum-type-specifier
        : elaborated-type-name
        | type-qualifier-list elaborated-type-name
        | struct-union-enum-type-specifier type-qualifier

typedef-type-specifier
        : TYPEDEFname
        | type-qualifier-list TYPEDEFname
        | typedef-type-specifier type-qualifier

elaborated-type-name
        : struct-or-union-specifier
        | enum-specifier

struct-or-union-specifier
        : struct-or-union opt-parenthesized-identifier-or-typedef-name

opt-parenthesized-identifier-or-typedef-name
        : identifier-or-typedef-name
        | LPAREN opt-parenthesized-identifier-or-typedef-name RPAREN
	
struct-or-union
        : STRUCT
        | UNION

enum-specifier
        : ENUM identifier-or-typedef-name

type-name
        : type-specifier
        | type-specifier abstract-declarator
        | type-qualifier-list                       // Implicit "int"
        | type-qualifier-list abstract-declarator   // Implicit "int"

type-name-list
        : type-name
        | type-name COMMA type-name-list
 
abstract-declarator
        : unary-abstract-declarator
        | postfix-abstract-declarator
        | postfixing-abstract-declarator

postfixing-abstract-declarator
        : array-abstract-declarator
        | LPAREN RPAREN

array-abstract-declarator
        : BRACKETS
        | LBRACKET constant-expression RBRACKET
        | array-abstract-declarator LBRACKET constant-expression RBRACKET

unary-abstract-declarator
        : STAR
        | STAR type-qualifier-list
        | STAR abstract-declarator
        | STAR type-qualifier-list abstract-declarator

postfix-abstract-declarator
        : LPAREN unary-abstract-declarator RPAREN
        | LPAREN postfix-abstract-declarator RPAREN
        | LPAREN postfixing-abstract-declarator RPAREN
        | LPAREN unary-abstract-declarator RPAREN postfixing-abstract-declarator
C Other Forms of Expressions
The following expressions all have their usual C semantics:
assignment-expression
        : conditional-expression
        | unary-expression ASSIGNOP    assignment-expression
        | unary-expression MULTassign  assignment-expression
        | unary-expression DIVassign   assignment-expression
        | unary-expression MODassign   assignment-expression
        | unary-expression PLUSassign  assignment-expression
        | unary-expression MINUSassign assignment-expression
        | unary-expression LSassign    assignment-expression
        | unary-expression RSassign    assignment-expression
        | unary-expression ANDassign   assignment-expression
        | unary-expression ERassign    assignment-expression
        | unary-expression ORassign    assignment-expression

conditional-expression
        : logical-OR-expression
        | logical-OR-expression QUESTION expression COLON conditional-expression

logical-OR-expression
        : logical-AND-expression
        | logical-OR-expression OROR logical-AND-expression

logical-AND-expression
        : inclusive-OR-expression
        | logical-AND-expression ANDAND inclusive-OR-expression

inclusive-OR-expression
        : exclusive-OR-expression
        | inclusive-OR-expression OR exclusive-OR-expression

exclusive-OR-expression
        : AND-expression
        | exclusive-OR-expression HAT AND-expression

AND-expression
        : equality-expression
        | AND-expression AMPERSAND equality-expression

equality-expression
        : relational-expression
        | equality-expression EQ relational-expression
        | equality-expression NE relational-expression

relational-expression
        : shift-expression
        | relational-expression LESS    shift-expression
        | relational-expression GREATER shift-expression
        | relational-expression LE      shift-expression
        | relational-expression GE      shift-expression

shift-expression
        : additive-expression
        | shift-expression LS additive-expression
        | shift-expression RS additive-expression

additive-expression
        : multiplicative-expression
        | additive-expression PLUS  multiplicative-expression
        | additive-expression MINUS multiplicative-expression

multiplicative-expression
        : cast-expression
        | multiplicative-expression STAR  cast-expression
        | multiplicative-expression SLASH cast-expression
        | multiplicative-expression MOD   cast-expression

cast-expression
        : unary-expression
        | LPAREN type-name RPAREN cast-expression

unary-expression
        : postfix-expression
        | INCR unary-expression
        | DECR unary-expression
        | AMPERSAND cast-expression
        | STAR cast-expression
        | PLUS cast-expression
        | MINUS cast-expression
        | TWIDDLE cast-expression
        | NOT cast-expression
        | SIZEOF unary-expression
        | SIZEOF LPAREN type-name RPAREN
        | line-address

postfix-expression
        : primary-expression
        | postfix-expression LBRACKET expression RBRACKET
        | function-call
	| postfix-expression LPAREN type-name-list RPAREN
        | postfix-expression DOT   identifier-or-typedef-name
        | postfix-expression ARROW identifier-or-typedef-name
        | postfix-expression INCR
        | postfix-expression DECR

15.2.3 Expressions Specific to C++

C++ is a complex language, with a rich expression system. The debugger understands much of the system, but it does not understand how to evaluate some complex aspects of a C++ expression. It can correctly debug these when they occur in the source code.

The aspects of the expression system not processed properly during debugger expression evaluation include the following:

There are also some minor restrictions in the following grammar, compared with the full C++ expression grammar, to make it unambiguous:

expression
        : assignment-expression

constant-expression
        : conditional-expression
C++ Identifiers
The debugger correctly augments the general lookup rules when inside class member functions, to look up the members correctly.

The debugger has only a limited understanding of namespaces. It correctly processes names such as UserNameSpace::NestedNamespace::userIdentifier, as well as C++ use-declarations, which introduce a new identifier into a scope.

The debugger does not currently understand C++ using-directives.

The debugger understands the relationship between struct and class identifiers and typedef identifiers.

id-or-keyword-or-typedef-name
        : identifier-or-key-word
        | TYPEDEFname
C++ Constants
The debugger treats numeric constants the same as C++ does. The enumeration constant identifiers go though the same grammar paths as variable identifiers, which has basically the same effect as the C++ semantics.
constant
        : FLOATINGconstant
        | INTEGERconstant
        | CHARACTERconstant
        | WIDECHARACTERconstant
        | WIDESTRINGliteral
C++ Calls
The debugger does not understand the following aspects of C++ calls:
call-expression
        : expression
Restrictions and limits are documented here.
C++ Addresses
Following is the C++ implementation of addresses:
address
        : AMPERSAND postfix-expression /* Address of */
        | line-address
        | postfix-expression

address-exp
        : address
        | address-exp PLUS  address
        | address-exp MINUS address
        | address-exp STAR  address
Restrictions and limits are documented here.
C++ Loc
Following is the C++ implementation of loc:
loc
        : expression
        | rescoped-expression
C++ Other Modified Forms of Expressions
whatis-expressions
        : expression
        | printable-type
C++ Rescoped Expressions
The C++ implementation of rescoped expressions is as follows:
qual-typedef-opt
        : type-name
        | qual-typedef-opt TICK type-name
C++ Strings
The C++ implementation of strings is as follows:
string-literal-list
        : string
        | string-literal-list string
C++ Identifier Expressions
The debugger understands nested names. Namespaces go through the same paths as classes, hence the unusual use of TYPEDEFname.
id-expression
	: id-expression-internals

id-expression-internals
	: qualified-id
	| id-or-keyword-or-typedef-name
	| operator-function-name
	| TWIDDLE id-or-keyword-or-typedef-name

qualified-id
	: nested-name-specifier qualified-id-follower 

qualified-type
	: nested-name-specifier TYPEDEFname

nested-name-specifier
	: CLCL
	| TYPEDEFname CLCL
	| nested-name-specifier TYPEDEFname CLCL

qualified-id-follower
	: identifier-or-key-word 
	| operator-function-name
	| TWIDDLE id-or-keyword-or-typedef-name
C++ Types
The debugger understands the full C++ type specification grammar.
type-specifier
        : basic-type-specifier
        | struct-union-enum-type-specifier
        | typedef-type-specifier

type-qualifier-list
        : type-qualifier
        | type-qualifier-list type-qualifier

type-qualifier
        : CONST
        | VOLATILE

basic-type-specifier
        : basic-type-name basic-type-name
        | basic-type-name type-qualifier
        | type-qualifier-list basic-type-name
        | basic-type-specifier type-qualifier
        | basic-type-specifier basic-type-name

struct-union-enum-type-specifier
        : elaborated-type-name
        | type-qualifier-list elaborated-type-name
        | struct-union-enum-type-specifier type-qualifier

typedef-type-specifier
        : TYPEDEFname type-qualifier
        | type-qualifier-list TYPEDEFname
        | typedef-type-specifier type-qualifier

basic-type-name
        : VOID
        | CHAR
        | SHORT
        | INT
        | LONG
        | FLOAT
        | DOUBLE
        | SIGNED
        | UNSIGNED

elaborated-type-name
        : aggregate-name
        | enum-name
	
printable-type
        : rescoped-typedef
	| type-name	

aggregate-name
        : aggregate-key opt-parenthesized-identifier-or-typedef-name
        | aggregate-key qualified-type

opt-parenthesized-identifier-or-typedef-name
        : id-or-keyword-or-typedef-name
        | LPAREN opt-parenthesized-identifier-or-typedef-name RPAREN

aggregate-key
        : STRUCT
        | UNION
        | CLASS

enum-name
        : ENUM id-or-keyword-or-typedef-name

parameter-type-list
        : PARENS type-qualifier-list-opt

type-name
        : type-specifier
        | qualified-type
	| basic-type-name
        | TYPEDEFname
        | type-qualifier-list
        | type-specifier abstract-declarator
        | basic-type-name abstract-declarator
	| qualified-type abstract-declarator
	| TYPEDEFname abstract-declarator
        | type-qualifier-list abstract-declarator

abstract-declarator
        : unary-abstract-declarator
        | postfix-abstract-declarator
        | postfixing-abstract-declarator

postfixing-abstract-declarator
        : array-abstract-declarator
        | parameter-type-list

array-abstract-declarator
        : BRACKETS
        | LBRACKET constant-expression RBRACKET
        | array-abstract-declarator LBRACKET constant-expression RBRACKET

unary-abstract-declarator
        : STAR
        | AMPERSAND
        | pointer-operator-type
        | STAR                  abstract-declarator
        | AMPERSAND             abstract-declarator
        | pointer-operator-type abstract-declarator

postfix-abstract-declarator
        : LPAREN unary-abstract-declarator RPAREN
        | LPAREN postfix-abstract-declarator RPAREN
        | LPAREN postfixing-abstract-declarator RPAREN
        | LPAREN unary-abstract-declarator RPAREN postfixing-abstract-declarator

pointer-operator-type
        : TYPEDEFname CLCL STAR type-qualifier-list-opt
        | STAR                  type-qualifier-list
        | AMPERSAND             type-qualifier-list
C++ Other Forms of Expressions
The following expressions all have the usual C++ semantics:
primary-expression
        : constant
        | string-literal-list
   	| THIS
        | LPAREN expression RPAREN
        | operator-function-name
        | identifier-or-key-word
        | qualified-id

operator-function-name
        : OPERATOR operator-predefined
        | OPERATOR basic-type-name
        | OPERATOR TYPEDEFname
        | OPERATOR LPAREN type-name RPAREN
        | OPERATOR type-qualifier
	| OPERATOR qualified-type

operator-predefined
	: PLUS
	| MINUS
	| STAR
	| ...
	| DELETE
	| COMMA

type-qualifier-list-opt
        : [ type-qualifier-list ]

postfix-expression
        : primary-expression
        | postfix-expression LBRACKET expression RBRACKET
        | postfix-expression PARENS 
        | postfix-expression LPAREN  argument-expression-list RPAREN
        | postfix-expression LPAREN  type-name-list RPAREN
        | postfix-expression DOT  id-expression
        | postfix-expression ARROW id-expression
        | postfix-expression INCR
        | postfix-expression DECR
        | TYPEDEFname LPAREN argument-expression-list RPAREN
        | TYPEDEFname LPAREN type-name-list RPAREN
        | basic-type-name LPAREN assignment-expression RPAREN

type-name-list
        : type-name
        | type-name COMMA type-name-list
        | type-name comma-opt-ellipsis
        | ELLIPSIS

comma-opt-ellipsis
        : ELLIPSIS
        | COMMA ELLIPSIS

unary-expression
        : postfix-expression
        | INCR unary-expression
        | DECR unary-expression
        | line-address
        | AMPERSAND cast-expression
        | STAR cast-expression
        | MINUS cast-expression
        | PLUS cast-expression
        | TWIDDLE LPAREN cast-expression RPAREN
        | NOT cast-expression
        | SIZEOF unary-expression
        | SIZEOF LPAREN type-name RPAREN
        | allocation-expression

allocation-expression
        : operator-new LPAREN type-name RPAREN operator-new-initializer
        | operator-new LPAREN argument-expression-list RPAREN LPAREN type-name RPAREN operator-new-initializer

operator-new
        : NEW
        | CLCL NEW

operator-new-initializer
        : [ PARENS ]
        | [ LPAREN argument-expression-list RPAREN ]

cast-expression
        : unary-expression
        | LPAREN type-name RPAREN cast-expression

deallocation-expression
        : cast-expression
        | DELETE               deallocation-expression
        | CLCL DELETE          deallocation-expression
        | DELETE BRACKETS      deallocation-expression
        | CLCL DELETE BRACKETS deallocation-expression

point-member-expression
        : deallocation-expression
        | point-member-expression DOTstar    deallocation-expression
        | point-member-expression ARROWstar  deallocation-expression

multiplicative-expression
        : point-member-expression
        | multiplicative-expression STAR  point-member-expression
        | multiplicative-expression SLASH point-member-expression
        | multiplicative-expression MOD   point-member-expression

additive-expression
        : multiplicative-expression
        | additive-expression PLUS  multiplicative-expression
        | additive-expression MINUS multiplicative-expression

shift-expression
        : additive-expression
        | shift-expression LS additive-expression
        | shift-expression RS additive-expression

relational-expression
        : shift-expression
        | relational-expression LESS    shift-expression
        | relational-expression GREATER shift-expression
        | relational-expression LE      shift-expression
        | relational-expression GE     shift-expression

equality-expression
        : relational-expression
        | equality-expression EQ relational-expression
        | equality-expression NE relational-expression

AND-expression
        : equality-expression
        | AND-expression AMPERSAND equality-expression

exclusive-OR-expression
        : AND-expression
        | exclusive-OR-expression HAT AND-expression

inclusive-OR-expression
        : exclusive-OR-expression
        | inclusive-OR-expression OR exclusive-OR-expression

logical-AND-expression
        : inclusive-OR-expression
        | logical-AND-expression ANDAND inclusive-OR-expression

logical-OR-expression
        : logical-AND-expression
        | logical-OR-expression OROR logical-AND-expression

conditional-expression
        : logical-OR-expression
        | logical-OR-expression QUESTION expression COLON conditional-expression

assignment-expression
        : conditional-expression
        | unary-expression ASSIGNOP    assignment-expression
        | unary-expression MULTassign  assignment-expression
        | unary-expression DIVassign   assignment-expression
        | unary-expression MODassign   assignment-expression
        | unary-expression PLUSassign  assignment-expression
        | unary-expression MINUSassign assignment-expression
        | unary-expression LSassign    assignment-expression
        | unary-expression RSassign    assignment-expression
        | unary-expression ANDassign   assignment-expression
        | unary-expression ERassign    assignment-expression
        | unary-expression ORassign    assignment-expression

15.2.4 Expressions Specific to Fortran

The following sections contain expressions specific to Fortran.
Fortran Identifiers
The Fortran implementation of identifiers is as follows:
identifier-or-typedef-name
        : identifier-or-key-word
        | TYPEDEFname
        | PROCEDUREname
Fortran Constants
real-or-imag-part
        : real_constant
        | PLUS real_constant
        | MINUS real_constant
        | integer_constant
        | PLUS integer_constant
        | MINUS integer_constant
constant
        : real_constant
        | integer_constant
        | complex-constant
        | character_constant
	| LOGICALconstantWithKind
character_constant
	: CHARACTERconstantWithKind
	| string
complex-constant
        : LPAREN real-or-imag-part COMMA real-or-imag-part RPAREN
Fortran Rescoped Expressions
The Fortran implementation of rescoped expressions is as follows:
qual-typedef-opt
        : TYPEDEFname /* Base (global) name */
        | qual-typedef-opt TICK TYPEDEFname /* Qualified name */

whatis-expressions
        : expression
        | rescoped-expression
        | printable_type
Fortran Calls
The Fortran implementation of calls is as follows:
call-expression
        : call-stmt

call-stmt
        : named-subroutine
        | named-subroutine LPAREN RPAREN
        | named-subroutine LPAREN actual-arg-spec-list RPAREN
Fortran Addresses
The Fortran implementation of addresses is as follows:
address
        : line-address
        | primary   

address-exp
        : address
        | address-exp PLUS  address
        | address-exp MINUS address
        | address-exp STAR  address
Restrictions and limits are documented here.
Fortran Loc
The Fortran implementation of loc is as follows:
loc
        : expression
        | rescoped-expression
Fortran Types
The Fortran implementation of types is as follows:
type-name
        : TYPEDEFname

printable-type
        : rescoped-typedef
	| type-name	
Other Forms of Fortran Expressions
expression
        : expr
        | named-procedure

assignment-expression
        : expr

constant-expression
        : constant

unary-expression
        : variable

expr
        : level-5-expr
        | expr defined-binary-op level-5-expr

level-5-expr
        : equiv-operand
        | level-5-expr LOGEQV equiv-operand
        | level-5-expr LOGNEQV equiv-operand
        | level-5-expr LOGXOR equiv-operand

equiv-operand
        : or-operand
        | equiv-operand LOGOR or-operand

or-operand
        : and-operand
        | or-operand LOGAND and-operand

and-operand
        : level-4-expr
        | LOGNOT and-operand

level-4-expr
        : level-3-expr
        | level-3-expr LESS level-3-expr
        | level-3-expr GREATER level-3-expr
        | level-3-expr LE level-3-expr
        | level-3-expr GE level-3-expr
        | level-3-expr EQ level-3-expr
        | level-3-expr NE level-3-expr

level-3-expr
        : level-2-expr
        | level-3-expr SLASHSLASH level-2-expr

level-2-expr
        : add-operand
        | level-2-expr PLUS  add-operand
        | level-2-expr MINUS add-operand

add-operand
        : add-operand-f90
        | add-operand-dec
        | unary-expr-dec

add-operand-f90
        : mult-operand-f90
        | add-operand-f90 STAR  mult-operand-f90
        | add-operand-f90 SLASH mult-operand-f90

mult-operand-f90
        : level-1-expr
        | level-1-expr STARSTAR mult-operand-f90

add-operand-dec
        : mult-operand-dec
        | add-operand-f90 STAR  mult-operand-dec
        | add-operand-f90 SLASH mult-operand-dec
        | add-operand-f90 STAR  unary-expr-dec
        | add-operand-f90 SLASH unary-expr-dec

mult-operand-dec
        : level-1-expr STARSTAR mult-operand-dec
        | level-1-expr STARSTAR unary-expr-dec

unary-expr-dec
        : PLUS  add-operand
        | MINUS add-operand

level-1-expr
        : primary
        | defined-unary-op primary

defined-unary-op

        : DOT_LETTERS_DOT 

primary
        : constant
        | variable
        | function-reference
        | LPAREN expr RPAREN
        | AMPERSAND variable 

defined-binary-op

        : DOT_LETTERS_DOT 

int-expr
        : expr

scalar-int-expr
        : int-expr

variable
        : named-variable
        | subobject

named-variable
        : variable-name

subobject
        : array-elt-or-sect
        | structure-component
        | known-substring

known-substring
        : disabled-array-elt-or-sect LPAREN substring-range RPAREN
        | hf-array-abomination

substring-range
        : scalar-int-expr COLON scalar-int-expr
        | scalar-int-expr COLON
        |                 COLON scalar-int-expr
        |                 COLON

hf-array-abomination
        : named-variable 
	    LPAREN section-subscript-list RPAREN
            LPAREN section-subscript RPAREN
        | structure PERCENT any-identifier
            LPAREN section-subscript-list RPAREN
            LPAREN section-subscript RPAREN
        | structure DOT any-identifier
            LPAREN section-subscript-list RPAREN
            LPAREN section-subscript RPAREN

disabled-array-elt-or-sect
        : DISABLER array-elt-or-sect 

array-elt-or-sect
        : named-variable LPAREN section-subscript-list RPAREN
        | structure PERCENT any-identifier LPAREN section-subscript-list RPAREN
        | structure DOT any-identifier LPAREN section-subscript-list RPAREN

section-subscript-list
        : section-subscript
        | section-subscript COMMA section-subscript-list

subscript
        : scalar-int-expr

section-subscript
        : subscript
        | subscript-triplet

subscript-triplet
        : subscript COLON subscript COLON stride
        | subscript COLON           COLON stride
        |           COLON subscript COLON stride
        |           COLON           COLON stride
        | subscript COLON subscript
        | subscript COLON
        |           COLON subscript
        |           COLON

stride
        : scalar-int-expr

structure-component
        : structure PERCENT any-identifier
        | structure DOT any-identifier

structure
        : named-variable
        | structure-component
        | array-elt-or-sect

function-reference
        : SIZEOF LPAREN expr RPAREN
        | named-function LPAREN RPAREN
        | named-function LPAREN actual-arg-spec-list RPAREN

named-procedure
        :  PROCEDUREname

named-function
        :  PROCEDUREname

named-subroutine
        :  PROCEDUREname

actual-arg-spec-list
        : actual-arg-spec
        | actual-arg-spec COMMA actual-arg-spec-list

actual-arg-spec
        : actual-arg

actual-arg
        : expr

any-identifier
        : variable-name
	| PROCEDUREname
	
variable-name
        : identifier-or-key-word

PROCEDUREname
        : IDENTIFIER

15.2.5 Expressions Specific to Ada

The following sections contain expressions specific to Ada.
Ada Constants
NOTE: We are not including ENUMERATIONconstant in this section because we are treating it like a variable with a type of "enumeration constant".

constant
        : FLOATINGconstant
        | INTEGERconstant
	| CHARACTERconstant

Ada Rescoped Expressions
qual-typedef-opt : TYPEDEFname | qual-symbol-opt TICK TYPEDEFname whatis-expressions : expression | rescoped-expression | printable_type
Ada Calls
call-expression : expression
Ada Addresses
address : line-address | AMPERSAND postfix_expression | LPAREN postfix_expression RPAREN address-exp : address | address-exp PLUS address | address-exp MINUS address | address-exp STAR address
Ada Loc Specification
loc : expression | rescoped-expression
Ada Types
type-specifier : typedef-type-specifier typedef-type-specifier : TYPEDEFname identifier-or-typedef-name : identifier-or-key-word | TYPEDEFname type-name : type-specifier | type-specifier abstract-declarator printable-type : rescoped-typedef | type-name
Other Forms of Ada Expressions
primary-expression : identifier-or-key-word | constant | string-literal-list | LPAREN expression RPAREN postfix-expression : primary-expression | postfix-expression LBRACKET expression RBRACKET | postfix-expression LPAREN arg-expression-list-opt RPAREN | postfix-expression DOT identifier-or-typedef-name | postfix-expression ARROW identifier-or-typedef-name | postfix-expression INCR | postfix-expression DECR string-literal-list : string | string-literal-list string unary-expression : postfix-expression | INCR unary-expression | DECR unary-expression | AMPERSAND cast-expression | line-address | STAR cast-expression | PLUS cast-expression | MINUS cast-expression | TWIDDLE cast-expression | NOT cast-expression cast-expression : unary-expression | LPAREN type-name RPAREN cast-expression multiplicative-expression : cast-expression | multiplicative-expression STAR cast-expression | multiplicative-expression SLASH cast-expression | multiplicative-expression MOD cast-expression additive-expression : multiplicative-expression | additive-expression PLUS multiplicative-expression | additive-expression MINUS multiplicative-expression shift-expression : additive-expression | shift-expression LS additive-expression | shift-expression RS additive-expression relational-expression : shift-expression | relational-expression LESS shift-expression | relational-expression GREATER shift-expression | relational-expression LE shift-expression | relational-expression GE shift-expression equality-expression : relational-expression | equality-expression EQ relational-expression | equality-expression NE relational-expression AND-expression : equality-expression | AND-expression AMPERSAND equality-expression exclusive-OR-expression : AND-expression | exclusive-OR-expression HAT AND-expression inclusive-OR-expression : exclusive-OR-expression | inclusive-OR-expression OR exclusive-OR-expression logical-AND-expression : inclusive-OR-expression | logical-AND-expression ANDAND inclusive-OR-expression logical-OR-expression : logical-AND-expression | logical-OR-expression OROR logical-AND-expression conditional-expression : logical-OR-expression | logical-OR-expression QUESTION expression COLON conditional-expression assignment-expression : conditional-expression | unary-expression ASSIGNOP assignment-expression | unary-expression MULTassign assignment-expression | unary-expression DIVassign assignment-expression | unary-expression MODassign assignment-expression | unary-expression PLUSassign assignment-expression | unary-expression MINUSassign assignment-expression | unary-expression LSassign assignment-expression | unary-expression RSassign assignment-expression | unary-expression ANDassign assignment-expression | unary-expression ERassign assignment-expression | unary-expression ORassign assignment-expression expression : assignment-expression constant-expression : conditional-expression abstract-declarator : unary-abstract-declarator | postfix-abstract-declarator | postfixing-abstract-declarator postfixing-abstract-declarator : array-abstract-declarator | LPAREN RPAREN array-abstract-declarator : BRACKETS | LBRACKET constant-expression RBRACKET | array-abstract-declarator LBRACKET constant-expression RBRACKET unary-abstract-declarator : STAR | STAR abstract-declarator postfix-abstract-declarator : LPAREN unary-abstract-declarator RPAREN | LPAREN postfix-abstract-declarator RPAREN | LPAREN postfixing-abstract-declarator RPAREN | LPAREN unary-abstract-declarator RPAREN postfixing-abstract-declarator

15.2.6 Expressions Specific to COBOL

COBOL Constants
constant
        : FLOATINGconstant
        | INTEGERconstant
        | DECIMALconstant
        | CHARACTERconstant
	
constant-expression
        : cobol-expression

COBOL Rescoped Expressions
qual-typedef-opt : TYPEDEFname | qual-typedef-opt TICK TYPEDEFname
COBOL Calls
call-expression : identifier-or-key-word | identifier-or-key-word USING cobol-expression-list
COBOL Addresses
address : INTEGERconstant | line-address | address-language | LPAREN cobol-expression RPAREN address-exp : address | address-exp PLUS address | address-exp MINUS address | address-exp STAR address address-language : AMPERSAND cobol-identifier | REFERENCEOF cobol-identifier
COBOL Loc
loc : cobol-identifier | rescoped-expression
COBOL Types
printable-type : rescoped-typedef
Other Forms of COBOL Expressions
assignment-expression : expression cobol-expression : cobol-identifier | constant | string | cobol-expression PLUS cobol-expression | cobol-expression MINUS cobol-expression | cobol-expression STAR cobol-expression | cobol-expression SLASH cobol-expression | cobol-expression EXPON cobol-expression | MINUS cobol-expression | PLUS cobol-expression | LPAREN cobol-expression RPAREN cobol-expression-list : cobol-expression | cobol-expression COMMA cobol-expression-list cobol-identifier : qualification | subscript | refmod condition-expression : combined-condition | negated-simple-condition combined-condition : negated-simple-condition AND negated-simple-condition | negated-simple-condition OR negated-simple-condition | LPAREN combined-condition RPAREN negated-simple-condition : simple-condition | NOT simple-condition | LPAREN NOT simple-condition RPAREN simple-condition : cobol-expression EQ cobol-expression | cobol-expression ASSIGNOP cobol-expression | cobol-expression NE cobol-expression | cobol-expression LESS cobol-expression | cobol-expression GREATER cobol-expression | cobol-expression LE cobol-expression | cobol-expression GE cobol-expression | cobol-expression SIGNPOS | cobol-expression SIGNNEG | cobol-expression SIGNZERO | cobol-expression SIGNNOTZERO | LPAREN simple-condition RPAREN expression : constant-expression | condition-expression | address-language identifier-or-typedef-name : identifier-or-key-word lvalue-expression : cobol-identifier qualification : identifier-or-key-word OF qualification | identifier-or-key-word refmod : qualification LPAREN cobol-expression COLON RPAREN | qualification LPAREN cobol-expression COLON cobol-expression RPAREN | subscript LPAREN cobol-expression COLON RPAREN | subscript LPAREN cobol-expression COLON cobol-expression RPAREN subscript : qualification LPAREN cobol-expression-list RPAREN unary-expression : lvalue-expression whatis-expressions : expression | rescoped-expression | rescoped-typedef

Chapter 16 - Debugging Core Files

When the operating system encounters an unrecoverable error while running a process, for example a segmentation violation (SEGV), the system creates a file named core and places it in the current directory. The core file is not an executable file; it is a snaphot of the state of your process at the time the error occurred. It allows you to analyze the process at the point it crashed.

This chapter discusses the following topics:

It also contains a core file debugging example and a quick reference for transporting a core file

16.1 Invoking the Debugger on a Core File

You can use the debugger to examine the process information in a core file. Use the following Ladebug command syntax to invoke the debugger on a core file:
       % ladebug executable_file core_file
or
       (ladebug) load executable_file core_file
The executable file is that which was being executed at the time the core file was generated.

16.2 Debugging a Core File

When debugging a core file, you can use the debugger to obtain a stack trace and the values of some variables just as you would for a stopped process.

The stack trace lists the functions in your program that were active when the dump occurred. By examining the values of a few variables along with the stack trace, you may be able to pinpoint the process state and the cause of the core dump. Core files cannot be executed; therefore the rerun, step, cont and so on commands will not work until you create a process using the run command.

In addition, if the program is multithreaded, you can examine the thread information with the show thread and thread commands. You can examine the stack trace for a particular thread or for all threads with the where thread command.

The following example uses a null pointer reference in the factorial function. This reference causes the process to abort and dump the core when it is executed. The dump command prints the value of the x variable as a null, and the print *x command reveals that you cannot dereference a null pointer.

       % cat testProgram.c

        #include <stdio.h>
        int factorial(int i)

        main() {
                int i,f;
                for (i=1 ; i<3 ; i++) {
                        f = factorial(i);
                        printf("%d! = %d\en",i,f);
                }
        }

        int factorial(int i)
        int i;
        {
        int *x;
                 x = 0;
                 printf("%d",*x);
                 if (i<=1)
                         return (1);
                 else
                         return (i * factorial(i-1) );
        }

        % cc -o testProgram -g testProgram.c
        % testProgram
        Memory fault - core dumped.
        % ladebug testProgram core
        Welcome to the Ladebug Debugger Version n
        ------------------
        object file name: testProgram
        core file name: core
        Reading symbolic information ...done
        Core file produced from executable testProgram
        Thread terminated at PC 0x120000dc4 by signal SEGV
        (ladebug) where
        >0  0x120000dc4 in factorial(i=1) testProgram.c:13
        #1  0x120000d44 in main() testProgram.c:4
        (ladebug) dump
        >0  0x120000dc4 in factorial(i=1) testProgram.c:13
        printf("%d",*x);
        (ladebug) print *x
        Cannot dereference 0x0
        Error: no value for *x
        (ladebug)

16.3 Transporting a Core File

Transporting core files is usually necessary to debug a core file on a system other than that which produced it. It is sometimes possible to debug a core file on a system other than that which produced it if the current system is sufficiently similar to the original system, but it will not work correctly in general.

Procedure for Transporting Core Files

The following procedure (see also quick reference) shows how to transport the core files, including the Compaq POSIX Threads Library. In this example, a.out is the name of the executable and core is the name of the core file.

You need to collect a variety of files from the original system. These include the executable, the core file, shared libraries used by the executable, and /usr/shlib/libpthreaddebug.so if the POSIX Threads Library is involved.

Do the following steps (1 through 4) on the original system:

  1. Determine the shared objects in use:
           % ladebug a.out core
            (ladebug) listobj
            (ladebug) quit
    
  2. Cut, paste and edit the result into a list of file names. Most will probably begin with /usr/shlib/.
  3. If /usr/shlib/libpthread.so is one of the files, add /usr/shlib/libpthreaddebug.so to the list. (If you have a privately delivered libpthread.so, there should be a privately delivered corresponding libpthreaddebug.so; use the privately delivered one.)
  4. Package the a.out, core and shared objects, for example, into a tar file. Be sure to use the tar h option to force tar to follow symbolic links as if they were normal files or directories.
           % tar cfvh mybug.tar
    
Then do the following steps (5 through 14) on the current system:

On the current system, the executable and core file are generally put in the current working directory, the shared objects are put in an "application" subdirectory, and libpthreaddebug.so is put in a "debugger" subdirectory.

  1. Create a directory for debugging the transported core files:
           % mkdir mybug
    
  2. Move to that directory:
           % cd mybug
    
  3. Get the package:
           % mv <wherever>/mybug.tar .
    
  4. Create the subdirectories applibs and dbglibs:
           % mkdir applibs dbglibs
    
  5. Unpackage the tar files. Be sure to use the tar s option to strip off any leading slashes from pathnames during extraction.
           % tar xfvs mybug.tar
    
  6. Move the shared objects (that were originally in /usr/shlib and are now in usr/shlib) into applibs:
       	% mv usr/shlib/* applibs
    

    If the tar xfvs output in step 9 moved shared objects into other directories, move them into applibs as well.

  7. Make libpthreaddebug.so exist in the dbglibs directory, for example, by linking it to the file in the applibs directory.
           % ln -s ../applibs/libpthreaddebug.so dbglibs/libpthreaddebug.so
    
  8. Set the LADEBUG_COREFILE_LIBRARY_PATH environment variable to the application subdirectory. This directs the debugger to look for shared objects (by their base names) in the application subdirectory before trying the system directories. If the POSIX Threads Library is involved, set the LD_LIBRARY_PATH environment variable to the debugger subdirectory so that the debugger will use the correct libpthreaddebug.so.
           % env LADEBUG_COREFILE_LIBRARY_PATH=applibs \
            LD_LIBRARY_PATH=dbglibs \
            ladebug a.out core
    
  9. Determine that the shared objects are in the applibs subdirectory rather than in /usr/shlib/:
           (ladebug) listobj
    
    For an alternative method when the debugger cannot be run on the original system, see the corefile_listobj.c example.
  10. Debug as usual:
           (ladebug)
    

16.4 Core File Debugging Example

The following is a complete example, from core creation, through transporting and core file debugging:
  1. Create the core file:
           % a.out -segv
            Segmentation fault (core dumped)
    
  2. Determine the shared objects using the debugger on the original system:
           % ladebug a.out core
            Welcome to the Ladebug Debugger Version 4.0-n
            ------------------
            object file name: a.out
            core file name: core
            Reading symbolic information ...done
            Core file produced from executable a.out
            Thread 0x5 terminated at PC 0x3ff8058b448 by signal SEGV
            (ladebug) listobj
                section         Start Addr           End Addr
            ------------------------------------------------------------------------------
            a.out
                 .text        0x120000000        0x120003fff
                 .data        0x140000000        0x140001fff
    
            /usr/shlib/libpthread.so
                 .text      0x3ff80550000      0x3ff8058bfff
                 .data      0x3ffc0180000      0x3ffc018ffff
                 .bss       0x3ffc0190000      0x3ffc01901af
    
            /usr/shlib/libmach.so
                 .text      0x3ff80530000      0x3ff8053ffff
                 .data      0x3ffc0170000      0x3ffc0173fff
    
            /usr/shlib/libexc.so
                 .text      0x3ff807b0000      0x3ff807b5fff
                 .data      0x3ffc0210000      0x3ffc0211fff
    
            /usr/shlib/libc.so
                 .text      0x3ff80080000      0x3ff8019ffff
                 .data      0x3ffc0080000      0x3ffc0093fff
                 .bss       0x3ffc0094000      0x3ffc00a040f
    
            (ladebug) quit
    
  3. Cut, paste, and edit the result into a list of file names. Note that libpthread.so is included, so add /usr/shlib/libpthreaddebug.so to the list.
  4. Create a tar file:
           % tar cfv mybug.tar a.out core \
                    /usr/shlib/libpthread.so /usr/shlib/libmach.so \
                    /usr/shlib/libexc.so /usr/shlib/libc.so \
                    /usr/shlib/libpthreaddebug.so
            a a.out 128 Blocks
            a core 2128 Blocks
            a /usr/shlib/libpthread.so 928 Blocks
            a /usr/shlib/libmach.so 208 Blocks
            a /usr/shlib/libexc.so 96 Blocks
            a /usr/shlib/libc.so symbolic link to ../../shlib/libc.so
            a /usr/shlib/libpthreaddebug.so 592 Blocks
    
    Note that libc.so is a symbolic link. Therefore, use the tar h option to force tar to follow symbolic links as if they were normal files or directories:
           % tar hcfv mybug.tar a.out core \
                    /usr/shlib/libpthread.so /usr/shlib/libmach.so \
                    /usr/shlib/libexc.so /usr/shlib/libc.so \
                    /usr/shlib/libpthreaddebug.so
            a a.out 128 Blocks
            a core 2128 Blocks
            a /usr/shlib/libpthread.so 928 Blocks
            a /usr/shlib/libmach.so 208 Blocks
            a /usr/shlib/libexc.so 96 Blocks
            a /usr/shlib/libc.so 3193 Blocks
            a /usr/shlib/libpthreaddebug.so 592 Blocks
    

    Now you have a package that you can transport.

  5. On the current system, create a directory for debugging, move to that directory, and get the package.
            % mkdir mybug
    	% cd mybug
    	% mv <wherever>/mybug.tar .
    
  6. Create the necessary subdirectories and unpackage the tar file using the s option:
            % mkdir applibs dbglibs
            % tar xfvs mybug.tar
            blocksize = 256
            x a.out, 65536 bytes, 128 tape blocks
            x core, 1089536 bytes, 2128 tape blocks
            x usr/shlib/libpthread.so, 475136 bytes, 928 tape blocks
            x usr/shlib/libmach.so, 106496 bytes, 208 tape blocks
            x usr/shlib/libexc.so, 49152 bytes, 96 tape blocks
            x usr/shlib/libc.so, 1634400 bytes, 3193 tape blocks
            x usr/shlib/libpthreaddebug.so, 303104 bytes, 592 tape blocks
    
  7. Move the original shared objects into applibs, and make libpthreaddebug.so exist in the dbglibs directory, for example, by linking it to the file in the applibs directory:
            % mv usr/shlib/* applibs
            % ln -s ../applibs/libpthreaddebug.so dbglibs/libpthreaddebug.so
    
    In this example, all shared objects were in usr/shlib/, so no other moving is needed.
  8. Observe the file system:
    
            % ls -lR
            total 4904
            -rwxr-xr-x   1 user1 ladebug    65536 Sep 17 11:20 a.out*
            drwxrwxr-x   2 user1 ladebug     8192 Sep 17 11:36 applibs/
            -rw-------   1 user1 ladebug  1089536 Sep 17 11:21 core
            drwxrwxr-x   2 user1 ladebug     8192 Sep 17 11:24 dbglibs/
            -rw-rw-r--   1 user1 ladebug  3737600 Sep 17 11:23 mybug.tar
            drwxrwxr-x   3 user1 ladebug     8192 Sep 17 11:36 usr/
    
            ./applibs:
            total 2632
            -rw-r--r--   1 user1 ladebug  1634400 Dec  7  1998 libc.so
            -rw-r--r--   1 user1 ladebug    49152 Jun 26  1998 libexc.so
            -rw-r--r--   1 user1 ladebug   106496 Dec 29  1997 libmach.so
            -rw-r--r--   1 user1 ladebug   475136 Dec  7  1998 libpthread.so
            -rw-r--r--   1 user1 ladebug   303104 Dec  7  1998 libpthreaddebug.so
    
            ./dbglibs:
            total 0
            lrwxrwxrwx   1 user1 ladebug       29 Sep 17 11:24 libpthreaddebug.so@ -> ../applibs/libpthreaddebug.so
    
            ./usr:
            total 8
            drwxrwxr-x   2 user1 ladebug     8192 Sep 17 11:36 shlib/
    
            ./usr/shlib:
            total 0
            %
    
    If other files need to be moved into applibs, do that as well and then re-observe the file system. In this example, there are none.

  9. Now set the environment variables as indicated:
           % env LADEBUG_COREFILE_LIBRARY_PATH=applibs \
            LD_LIBRARY_PATH=dbglibs \
            ladebug a.out core
            Welcome to the Ladebug Debugger Version 4.0-n
            ------------------
            object file name: a.out
            core file name: core
            Reading symbolic information ...done
            Core file produced from executable a.out
            Thread 0x5 terminated at PC 0x3ff8058b448 by signal SEGV
    
  10. Issue the listobj command to ensure the application libraries are coming from applibs/. Find any that are not, either from the original system, or unpacked from the tar file but not yet moved into applibs.
           (ladebug) listobj
                section         Start Addr           End Addr
            ------------------------------------------------------------------------------
            a.out
                 .text        0x120000000        0x120003fff
                 .data        0x140000000        0x140001fff
    
            applibs/libpthread.so
                 .text      0x3ff80550000      0x3ff8058bfff
                 .data      0x3ffc0180000      0x3ffc018ffff
                 .bss       0x3ffc0190000      0x3ffc01901af
    
            applibs/libmach.so
                 .text      0x3ff80530000      0x3ff8053ffff
                 .data      0x3ffc0170000      0x3ffc0173fff
    
            applibs/libexc.so
                 .text      0x3ff807b0000      0x3ff807b5fff
                 .data      0x3ffc0210000      0x3ffc0211fff
    
            applibs/libc.so
                 .text      0x3ff80080000      0x3ff8019ffff
                 .data      0x3ffc0080000      0x3ffc0093fff
                 .bss       0x3ffc0094000      0x3ffc00a040f
    
  11. Now debug as usual:
           (ladebug) where
            >0  0x3ff8058b448 in nxm_thread_kill(0x140091c68, 0xb, 0x1, 0x0, 0x0, 0xfffffffffffffcc0) in applibs/libpthread.so
            #1  0x3ff80578c58 in pthread_kill(0x140091c68, 0xb, 0x1, 0x0, 0x0, 0xfffffffffffffcc0) in applibs/libpthread.so
            #2  0x3ff8056cd34 in UnknownProcedure3FromFile69(0x140091c68, 0xb, 0x1, 0x0, 0x0, 0xfffffffffffffcc0) in applibs/libpthread.so
            #3  0x3ff807b22d8 in UnknownProcedure4FromFile1(0x140091c68, 0xb, 0x1, 0x0, 0x0, 0xfffffffffffffcc0) in applibs/libexc.so
            #4  0x3ff807b3824 in UnknownProcedure17FromFile1(0x140091c68, 0xb, 0x1, 0x0, 0x0, 0xfffffffffffffcc0) in applibs/libexc.so
            #5  0x3ff807b3864 in exc_unwind(0x140091c68, 0xb, 0x1, 0x0, 0x0, 0xfffffffffffffcc0) in applibs/libexc.so
            #6  0x3ff807b3af0 in exc_raise_signal_exception(0x140091c68, 0xb, 0x1, 0x0, 0x0, 0xfffffffffffffcc0) in applibs/libexc.so
            #7  0x3ff8057a328 in UnknownProcedure6FromFile80(0x140091c68, 0xb, 0x1, 0x0, 0x0, 0xfffffffffffffcc0) in applibs/libpthread.so
            #8  0x3ff800d6a30 in __sigtramp(0x140091c68, 0xb, 0x1, 0x0, 0x0, 0xfffffffffffffcc0) in applibs/libc.so
            #9  0x120001d94 in mandel_val(cr=0.01, ci=0.16, nmin=0, nmax=255) "mb_pi.c":62
            #10 0x12000274c in smp_fill_in_data(raw_mthread=0x11fffe998) "mb_pi.c":338
            #11 0x3ff80582068 in thdBase(0x0, 0x2, 0x0, 0x0, 0xff, 0x1) in applibs/libpthread.so
            (ladebug) quit
            %
    

16.5 Quick Reference for Transporting a Core File

The following sections contain a quick reference for transporting a core file.

First, do the following steps on the original system:

  1. % ladebug a.out core

  2. (ladebug) listobj; quit
  3. Cut, paste, and edit into list of filenames.
  4. Add /usr/shlib/libpthreaddebug.so, if libpthread.so
  5. % tar cfvh mybug.tar a.out core <shlibs>
Next, do the following steps on the current system:
  1. % mkdir mybug
  2. % cd mybug
  3. % mv mybug.tar
  4. % mkdir applibs dbglibs
  5. % tar sfvc mybug.tar
  6. % mv usr/shlib/* applibs
  7. % ln -s ../applibs/libptheaddebug.so dbglib/libptheaddebug.so

  8. The ../applibs is not a typo. Think of it as:
    % cd dbglibs
    % ln -s ../applibs/libpthreaddebug.so libpthreaddebug.so
    % cd ..
  9. % env LADEBUG_COREFILE_LIBRARY_PATH=applibs \
    LD_LIBRARY_PATH=dbglibs \
    ladebug a.out core
  10. (ladebug) listobj
  11. (ladebug)

Chapter 17 - Kernel Debugging

This chapter discusses kernel debugging, and contains sections for each type of kernel debugging that a user may wish to engage in.

17.1 Overview of Kernel Debugging

Local kernel debugging is useful for examining the state of the running kernel, or any of its modules or components. If a process is hung, a system administrator might want to debug the kernel to find out what the process is caught on. The debugger has no ability to stop a local kernel, because that will stop the debugger too!

When a local kernel crashes, systems personnel typically want to know why. The kernel has been designed to save a copy of its memory state to a core file just before crashing. A copy of the kernel that crashed is also saved. These two files can be loaded into the debugger, and the state of the kernel just prior to the crash can be examined, to determine what went wrong.

Remote kernel debugging is useful for systems engineers who are building and testing kernels, and need to have more control over the kernel. Breakpoints can be set in a kernel that is debugged remotely, which allow it to be stopped and examined more closely. Remote kernel debugging requires at least two machines, with the debugger running on one, and the kernel to be tested on the other.

17.1.1 Security

The kernel is typically owned by root, so you may need to be the superuser (root login) to examine either the running system or crash dumps. Whether or not you need to be the superuser to debug a kernel directly depends on the directory and file protections of the files you attempt to examine.

17.1.2 Compiling a kernel for debugging

Ideally the kernel should be compiled without full optimization and without stripping the kernel of its symbol table information, so that the debugger can provide the user with the most information in a most friendly manner. However most of the time this is not the case, and the user is working with a stripped-down, highly optimized kernel. In these cases the debugger will be limited in what it can display. Information relating to source files will not be available, and often function parameters and other variables will be "optimized away".

17.1.3 Special Features for Kernel Debugging

The kernel is a complex piece of software. It contains kernel processes denoted by a process id, or pid. Some of these relate to user processes, and some are kernel-specific. Since the debugger supports multi-processing as well as kernel debugging, we need to be able to distinguish between processes that the debugger is managing, and processes being managed within the kernel. Since the debugger command for managing multiple processes is process, we will refer to a process being debugged as a process, and we will refer to a process within the kernel being debugged as a pid.

The debugger maintains two extra debugger variables when kernel debugging: $pid and $tid. These variables assist the user in establishing and changing the user context while debugging the kernel.

The $pid variable contains the current pid that the user is examining. The user can switch the context to another pid within the kernel by setting $pid to the desired value. Note that the debugger variable $curprocess, which is read-only, does not change. The value in the $curprocess variable is how the debugger refers to the kernel process as a whole. Typically this value will be the actual kernel process listed as [kernel idle] with the ps system command (or ladebug kps command - see below), but not always. In any case, the $pid variable controls the pid that the user is examining within the kernel.

The $tid variable contains the current thread id that the user is examining. It too can be used to switch the user context to a different thread (it simply calls the thread command with its new value).

The debugger also supplies a kps command, which displays all the pids in the kernel:


kernel_debugging_command
        : kps
For example, if you wanted to examine the stack of the kloadsrv daemon, you would first find out its pid:

    (ladebug) kps
    00000   kernel idle
    00001   init
    00003   kloadsrv
    00020   update
    02082   dtexec
    02092   dtterm
    02093   csh
    ...

And then set the $pid accordingly and enter the where command:

    (ladebug) set $pid = 3
    (ladebug) where
    >0  0xfffffc00002b3a10 in thread_block()

17.1.4 Patching a Disk File

From within the debugger, you can use the patch command to correct bad data or instructions in an executable disk file. The text, initialized data, or read-only data areas can be patched. The bss segment cannot be patched because it does not exist in disk files. For example:

    (ladebug) patch @foo = 20

17.1.5 KSEG Addresses

You can specify addresses in the KSEG segment by prefixing the hexadecimal offset of the address with "0k"; Ladebug will add the KSEG base address to any such hexadecimal constant. For example,

        0k2400
will be converted by Ladebug into the actual address for the location at offset 0x2400 into the KSEG segment.

17.2 Local Kernel Debugging

When you have a problem with a process, you can debug the running kernel or examine the values assigned to system parameters. (It is generally recommended that you avoid modifying the value of the parameters, which can cause problems with the kernel.)

Invoke the debugger with the following command:

    # ladebug -k /vmunix /dev/mem

The -k flag maps virtual to physical addresses to enable local kernel debugging. The /vmunix and /dev/mem parameters cause the debugger to operate on the running kernel (the /dev/mem parameter is optional when debugging the live kernel, the debugger knows to look there for the kernel address space).

Now you can use debugger commands to display information such as the current list of pids (kps), and trace the execution of processes. Note that the debugger motion commands such as cont, next, rerun, run, step, return, and stop are not available, nor can you change values in registers when you do local kernel debugging (stopping the kernel would also stop the debugger!).

17.3 Crash Dump Analysis

If your system panics or crashes, you can often find the cause by using the debugger to analyze a crash dump. Keep in mind that the debugger is only one useful tool for determining the cause of a crash. There are other tools and techniques that are available to system personnel that aid in analyzing crash dumps. Some of them are mentioned briefly here.

NOTE: Crash dump analysis with this debugger cannot be done remotely.

17.3.1 Crash Dumps

The operating system can crash in the following ways:

If the system crashes because of a hardware fault or an unrecoverable software state, a dump function is invoked. The dump function copies the core memory into the primary default swap disk area as specified by the /etc/fstab file structure table and the /sbin/swapdefault file. At system reboot time, the information is copied into a file, called a crash dump file. Crash dump files are either partial (the default) or full. See Compaq TRU64 UNIX Kernel Debugging for more information.

You can analyze the crash dump file to determine what caused the crash. For example, if a hardware trap occurred, you can examine variables, such as savedefp, the program counter ($pc), and the stack pointer ($sp), to help you determine why the crash occurred. If a software panic caused the crash, you can use the debugger to examine the crash dump and use the uerf utility to examine the error log. Using these tools, you can determine which function called the panic() routine.

17.3.2 Guidelines for Examining Crash Dumps

In examining crash dump files, there is no one way to determine the cause of a system crash. However, the following guidelines should help you identify the events that led to the crash:

For more information and for examples, see Compaq TRU64 UNIX Kernel Debugging. This manual contains detailed information on the following topics related to crash dump analysis:

17.3.3 Analyzing a Crash Dump

Compaq Tru64 UNIX crash dump files can be found in /var/adm/crash. There you will find a copy of the kernel that crashed (vmunix.number) and the core memory file (vmcore.number or vmzcore.number, depending on the operating system version). The number appended to these files distinguishes crashes, with the highest number denoting the most recent crash. These files are owned by root, so you must have root permissions to access them.

To invoke the debugger on a kernel crash dump numbered "0":

	# ladebug -k vmunix.0 vmzcore.0

On startup, the debugger analyzes the core file to determine the final PC address of the crash, and outputs that information. You can use the debugger to inspect kernel data structures, global variables (such as the panic string), and kernel thread stacks, in order to determine why the kernel crashed.

If you are going to view the stack using the where, up, down, and dump commands, you may want to set the control variable $stackargs to zero (0) to suppress the output of argument values, The kernel is typically compiled for high performance, which means those argument values have been optimized away. If the debugger is set to look for those arguments and cannot find them, it notifies the user with error messages, causing the stack output to be full of repetitious warnings.

When debugging a kernel memory fault, verify that the core memory has not been corrupted; otherwise, the debugger may present erroneous information. One quick way to check this is by comparing the global panic string with the panic string in the machine_slot structure of the machine that caused the crash. Select the appropriate machine_slot structure name depending on your operating system version, as follows:

If the strings are different, the core memory file may be corrupted. The following example shows how to compare these strings:

	# ladebug -k vmunix.0 vmzcore.0
	Welcome to the Ladebug Debugger Version 4.0-64 (built Nov 12 2000 for 
	Compaq Tru64 UNIX)
	------------------ 
	object file name: vmunix.0 
	core file name: vmzcore.0
	Reading symbolic information ...done
	Thread terminated at PC 0xfffffc0000457a48
	done
	(ladebug) print panicstr
	0xfffffc0000640970="kernel memory fault"		    <== memory fault
	(ladebug) print utsname
	struct utsname {
  	  sysname = "OSF1";
  	  nodename = "";
  	  release = "X5.1";					    <== 5.n system
  	  version = "730";
  	  machine = "alpha";
	}
	(ladebug) print processor_ptr[paniccpu].m
	struct machine_slot {
  	  is_cpu = 1;
  	  cpu_type = 15;
  	  cpu_subtype = 22;
  	  running = 1;
  	  cpu_ticks = [0] = 0,[1] = 0,[2] = 0,[3] = 0,[4] = 0;
  	  clock_freq = 1200;
  	  error_restart = 0;
  	  cpu_panicstr = 0xfffffc0000640970="kernel memory fault";  <== strings match
  	  cpu_panic_thread = 0xfffffc0001c51180;
	}

When you debug your code by working with a crash dump file, you can examine the exception frame using the debugger. The variable savedefp contains the location of the exception frame. (No exception frames are created when you force a system to dump.) Refer to the header file /usr/include/machine/reg.h to determine where registers are stored in the exception frame. The following example shows an exception frame:

    (ladebug) print savedefp/33X

    ffffffff9618d940:   0000000000000000 fffffc000046f888
    ffffffff9618d950:   ffffffff86329ed0 0000000079cd612f
    .
    .
    .
    ffffffff9618da30:   0000000000901402 0000000000001001
    ffffffff9618da40:   0000000000002000

You can use the debugger to extract the preserved message buffer from a running system or use dump files to display system messages logged by the kernel. For example:

    (ladebug) print *pmsgbuf
    struct msgbuf {
    msg_magic = 405601;
    msg_bufx = 1851;
    msg_bufr = 1343;
    msg_bufc = "Alpha boot: available memory from 0x6ca000 to 0x3f16000\nTru64 UNIX V.n  (Rev. 564); Fri Jul 11 11:25:29 EDT 1997 \nphysical m";
    }

The print command is regulated in length by the $maxstrlen debugger variable. Also, the print command does not expand the newline character. To see the full message string as formatted text, use the following command:

    (ladebug) printf "%s",pmsgbuf->msg_bufc

17.3.4 Other Crash Dump Tools

The crashdc utility collects critical data from operating system crash dump files or from a running kernel. You can use the data it collects to analyze the cause of a system crash. The crashdc utility uses existing system tools and utilities to extract information from crash dumps. The information garnered from crash dump files or from the running kernel includes the hardware and software configuration, current processes, the panic string (if any), and swap information.

See Compaq TRU64 UNIX Kernel Debugging and the crashdc(8) reference page for more information.

17.4 Remote Kernel Debugging

For remote kernel debugging, the Ladebug debugger is used in conjunction with the kdebug debugger, which is a tool for executing, testing, and debugging test kernels.

Used alone, kdebug has its own syntax and commands, and allows local non-symbolic debugging of a running kernel across a serial line. See the kdebug(8) manpage for information about kdebug local kernel debugging.

You use Ladebug commands to start and stop kernel execution, examine variable and register values, and perform other debugging tasks, just as you would when debugging user-space programs. The kdebug debugger, not the Ladebug debugger, performs the actual reads and writes to registers, memory, and the image itself (for example, when breakpoints are set).

17.4.1 Connections Needed

The kernel code to be debugged runs on a test system. The Ladebug debugger runs on a remote build system and communicates with the kernel code over a serial communication line or through a gateway system.

You use a gateway system when you cannot physically connect the test and build systems. The build system is connected to the gateway system over a network. The gateway system is connected to the test system by a serial communication line.

The following diagram shows the physical connection of the test and build systems (with no gateway):

   Build system              Serial line           Test system
   (with the Ladebug <------------------------->  (kernel code here)
    debugger)
The following diagram shows the connections when you use a gateway system:
   Build system          Network     Gateway   Serial line    Test system
   (with the Ladebug  <----------->  system  <------------->  (kernel code
    debugger)                        with                      here)
                                     kdebug
                                     daemon

The serial line provides a physical connection between communication ports on two systems; it consists of a BC16E cable and two H8571-J DECconnect Passive Adapters:

The test system always uses the same communication port for kdebug debugger input and output:

The build or gateway system, whichever is at the other end of the serial line from the test system, uses /etc/remote to specify the device to use for kdebug debugger input and output. Serial communication ports 1 and 2 correspond to device names /dev/tty00 and /dev/tty01, respectively:

The following line in /etc/remote defines /dev/tty00 as the device to use for kdebug debugger input and output:

        kdebug:dv=/dev/tty00:br#9600:pa=none:

For AlphaStation and AlphaServer systems, it is possible to change this definition to /dev/tty01 so the same serial port can be used for remote kernel debugging whether the system is used as a build, gateway, or test system:

        kdebug:dv=/dev/tty01:br#9600:pa=none:

The first field, kdebug, is a label and has no significance except as an identifier, and "kdebug" is the default name for the serial line used by the debugger for kdebug debugger input and output.

Using a Second Serial Line for Test System Console Emulation

For AlphaStation and AlphaServer systems, it is also possible to redirect the test system console input and output to the build or gateway system at the other end of the serial line. This requires a second serial line connected between the communications ports of the build or gateway system and the test system that are not used for kdebug debugger input and output.

When the line has been connected, enter console mode on the test system by shutting it down; console mode is recognizable by the ">>>" prompt. At the prompt, enter the following command:

   >>> set console serial

This redirects all console input and output to serial communication port 1 (/dev/tty00).

On the build or gateway system, modify the /etc/remote file to define this second line. For example, in order to change the serial line used for kdebug debugger input and output to /dev/tty01 and create a useful label for /dev/tty00 to use for the test system console input and output, replace the original definition for kdebug in /etc/remote on the build or gateway system with the following:

     kdebug:dv=/dev/tty01:br#9600:pa=none:
     tstsys:dv=/dev/tty00:br#9600:pa=none:

Then on the build or gateway system (in a window separate from the debugger window if on the build system), enter the following command:

   % tip tstsys

You can use this separate window as the console for the test system. When finished, return to the console mode on the test system and enter the following command in the separate console window on the build or gateway system:

   >>> set console graphics
Then exit out of tip on the build or gateway system by entering a tilde and a period (˜.).

17.4.2 System Requirements

The test, build, and (if used) gateway systems must meet the following requirements for kdebug:

You can verify the status of each of the system requirements by the using the following commands:

17.4.3 Getting Ready to Use the kdebug Debugger

To use the kdebug debugger, do the following:

  1. Attach the test system and the build system or the test system and the gateway system. See your hardware documentation for information about connecting systems to serial lines and networks.

  2. Configure the kernel to be debugged with the configuration file option OPTIONS KDEBUG. If you are debugging the installed kernel, you can do this by selecting KERNEL BREAKPOINT DEBUGGING from the Kernel Options menu.

  3. Recompile kernel files, if necessary. By default, the kernel is compiled with only partial debugging information, occasionally causing the debugger to display erroneous arguments or mismatched source lines. To correct this, recompile selected source files specifying the CDEBUGOPTS=-g argument.

  4. Copy the kernel to be tested to /vmunix on the test system. Retain an exact copy of this image on the build system.

  5. Install the Product Authorization Key (PAK) for the Developers' kit (OSF-DEV) if it is not already installed. For information about installing PAKs, see the Compaq Tru64 UNIX Installation Guide.

  6. Determine the debugger variable settings or command-line options you will use. On the build system, add the following lines to your .dbxinit file if you need to override the default values (and you choose not to use the corresponding options, described below). Alternatively, you can use these lines within the debugger session, at the (ladebug) prompt:

          (ladebug) set $kdebug_host="<name_of_your_gateway_system>"
          (ladebug) set $kdebug_line="<name_of_your_serial_line>"
          (ladebug) set $kdebug_dbgtty="<name_of_your_tty>"
    

    Instead of using debugger variables, you can specify any of the following options on the ladebug command line:

    The above three options require the -remote option or its alternative, the -rp kdebug option, for example:

    # ladebug -remote -rn "decosf" -line "kdebug" -tty "/dev/ttyp2" /usr/test/vmunix
    
    The -rn, -line, and -tty options on this command line produce the same result as the set command examples above. This example assumes a copy of the test system's vmunix is in /usr/test on the build system.

    The variables you set in your .dbxinit file override any options you use on the ladebug command line. In your debugging session, you can still override the .dbxinit variable settings by using the set command at the (ladebug) prompt, prior to issuing the run command.

  7. If you are debugging on an symmetric multiprocessing (SMP) system , set the lockmode system attribute to 4, as shown:

       # sysconfig -r lockmode = 4
    

Setting this system attribute makes debugging on an SMP system easier.

17.4.4 Invoking the Debugger

When the setup is complete, start the debugger as follows:

  1. Invoke the Ladebug debugger on the build system, supplying the pathname of the copy of the test kernel that resides on the build system. Set a breakpoint and start running the Ladebug debugger as follows (assuming that vmunix resides in the /usr/test directory):

       # ladebug -remote /usr/test/vmunix
        ...
        (ladebug) stop in panic
        [2] stop in panic
        (ladebug) stop in ttyretype
        [3] stop in ttyretype
     

    Because Ctrl/C cannot be used as an interrupt, set at least one breakpoint if you want the debugger to gain control of kernel execution. You can set a breakpoint anytime after the execution of the kdebug_bootstrap() routine. Setting a breakpoint prior to the execution of this routine can result in unpredictable behavior. Setting a breakpoint in panic enables regaining control after a panic and setting a breakpoint in ttyretype enables returning control to the debugger whenever Ctrl/R is entered at the console.

    NOTE: Pressing Ctrl/C causes the remote debugger to exit, not interrupt as it does during local debugging.

  2. Halt the test system:

       # shutdown -h now
    

  3. At the console prompt, set the boot_osflags console variable to contain the value k (the default is usually the value a). For example:

       >>> set boot_osflags k
    

    Alternatively, you can enter the following command:

       >>> boot -flags k
    

    The boot command can be abbreviated to b and the -flags option can be abbreviated to -fl. The boot command without the -flags option boots the machine using the current value for boot_osflags; with the -flags option, it uses the value specified in the option and does not change the value of the boot_osflags console variable. Other useful commands from the console prompt include show, which lists the values of console variables, and help, which provides information about other commands. For more information about the boot_osflags values, refer to the Compaq Tru64 UNIX System Administration Manual and the Compaq Tru64 UNIX Installation Guide.

  4. With the Ladebug debugger started and waiting at a (ladebug) prompt with breakpoints already set and the test system waiting at the console prompt (>>>), start both the Ladebug debugger and test system kernel executing. You can start them in either order as follows:

17.4.5 Breakpoint Behavior on SMP Systems

If you set breakpoints in code that is executed on an SMP system, the breakpoints are handled serially. When a breakpoint is encountered on a particular CPU, the state of all the other processors in the system is saved and those processors spin, similar to how execution stops when a simple lock is obtained on a particular CPU.

When the breakpoint is dismissed (for example, because you entered a step or cont command to the debugger), processing resumes on all processors.

17.4.6 Troubleshooting Tips

If you have completed the kdebug setup and it fails to work, refer to the following list for help:

Chapter 18 - Machine-Level Debugging

The debugger lets you debug your programs at the machine-code level as well as at the source-code level. Using debugger commands, you can examine and edit values in memory, print the values of all machine registers, and step through program execution one machine instruction at a time.

Only those users familiar with machine-language programming and executable-file-code structure will find low-level debugging useful.

This chapter contains the following sections:

18.1 Examining Memory Addresses

You can examine the value contained at an address in memory as follows:

18.1.1 Using the examine_commands

The examine_commands (/ and ?) can be used to print the value contained at the address in one of a number of formats (decimal, octal, hexadecimal, and so on). See Memory Display Commands for more information.

The debugger also maintains the $readtextfile debugger variable that allows you to view the data from the text section of the executable directly from the binary file, rather than reading it from memory. You can use this variable to speed up the reading of instructions during kernel debugging, however, remember that you are reading from the file and NOT from the live image, which could be different!

18.1.2 Using Pointer Arithmetic

You can use C and C++ pointer-type conversions to display the contents of a single address in decimal. Using the print command, the syntax is as follows:
	print *(int *)(address)
Using the same pointer arithmetic, you can use the assign command to alter the contents of a single address. Use the following syntax:
	assign *(int *)(address) = value
The following example shows how to use pointer arithmetic to examine and change the contents of a single address:
       (ladebug) print *(int*)(0x10000000)
        4198916
       (ladebug) assign *(int*)(0x10000000) = 4194744
       (ladebug) print *(int*)(0x10000000)
        4194744
       (ladebug)

18.1.3 Examining Machine-Level Registers

The printregs command prints the values of all machine-level registers. The registers displayed by the debugger are machine dependent. The values are in decimal or hexadecimal, depending on the value of the $hexints variable (the default is 0, decimal). The register aliases are shown; for example, $r1 [$t0]. See the printregs command for more information.

18.2 Stepping at the Machine Level

The stepi and nexti commands let you step through program execution incrementally, like the step and next commands. The stepi and nexti commands execute one machine instruction at a time, as opposed to one line of source code. The following example shows stepping at the machine-instruction level:
       (ladebug) stop in main
        [#1: stop in main ]
        (ladebug) run
        [1] stopped at [main:4 0x120001180]
        4     for (i=1 ; i<3 ; i++) {
        (ladebug) stepi
        stopped at [main:4 0x120001184] stl     t0, 24(sp)
        (ladebug) [Return]
        stopped at [main:5 0x120001188] ldl     a0, 24(sp)
        (ladebug) [Return]
        stopped at [main:5 0x12000118c] ldq     t12, -32664(gp)
        (ladebug) [Return]
        stopped at [main:5 0x120001190] bsr     ra,
        (ladebug) [Return]
        stopped at [factorial:12 0x120001210]   ldah    gp, 8192(t12)
        (ladebug)
At the machine-instruction level, you can step into, rather than over, a function's prologue. While within a function prologue, you may find that the stack trace, variable scope, and parameter list are not correct. Stepping out of the prologue and into the actual function updates the stack trace and variable information kept by the debugger.

Single-stepping through function prologues that initialize large local variables is slow. As a workaround, use the next command.

18.2.1 Load Locked Store Conditional Sequences

Load locked store conditional sequences have special semantics. See the Alpha Architecture Reference Manual and the Alpha Architecture Handbook for more information.

The debugger interaction with the target modifies the target state enough that the store conditional is guaranteed to fail. So that users can make progress, the debugger normally steps over the entire sequence, as though it were one big instruction. This allows the application to continue normally.

To debug inside the sequence, while understanding that this implies failure of the store, set a breakpoint inside the sequence and continue to the breakpoint. From there, you will be able to step.


Appendixes

Appendix 1—Debugger Variables

The debugger has the following predefined variables. Conventionally, a Ladebug debugger variable name is an identifier with a leading dollar sign ($).

Variable
Default Setting
Description
$ascii
1
Prints ASCII or all ISO Latin-1.
$beep
1
Beeps on illegal command line editing.
$catchexecs
0
Stops execution on program exec.
$catchforkinfork
0
Notifies you as soon as the forked process is created (otherwise you are notified when the call finishes).
$catchforks
0
Notifies you on program fork and stops child.
$childprocess
0
When the debugger detects a fork, it assigns the child process ID to $childprocess.
$curevent
0
Displays the current breakpoint number.
$curfile
(null)
Displays the current source file.
$curfilepath
(null)
Displays the current source file access path.
$curline
0
Displays the current source line.
$curpc
0
Displays the current point of program execution.
$curprocess
0
Displays the current process ID.
$cursrcline
0
Displays the last source line at end of most recent source listing.
$cursrcpc
0
Displays the PC address at end of most recent machine code listing.
$curthread
0
Displays the current thread ID.
$dbxoutputformat
0
Displays various data structures in dbx format.
$dbxuse
0
Replaces current use paths.
$decints
0
Displays integers in decimal radix.
$doverbosehelp
1
Displays the help menu front page.
$editline
1
Enables command line editing.
$eventecho
1
Echoes events with event numbers.
$exitonterminationofprocesswithpid
None
If set to process ID (pid), when that process terminates, the debugger exits.
$funcsig
1
Displays function signature at breakpoint.
$giveladebughints
1
Displays hints on Ladebug features.
$hasmeta
0
Interprets multibyte characters.
$hexints
0
Displays integers in hex radix.
$historylines
20
Defines the number of commands to show for history.
$indent
1
Prints structures with indentation.
$kdebug_host
localhost
Specifies the node name of the gateway system. The default "localhost" indicates no gateway system.
$kdebug_line
kdebug
Specifies the label of the /etc/remote entry on the gateway system for the kdebug communication port.
$kdebug_dbgtty
(null)
Specifies the pathname of the terminal window (output from tty (1)) on the gateway system used to display kdebug communication traffic. The default (null) indicates no gateway system.
$lang
"None"
Defines the programming language of current routine.
$lasteventmade
0
Displays the number of last (successful) breakpoint definition.
$lc_ctype
"C"
Displays the current locale information.
$listwindow
20
Displays the number of lines to show for list.
$main
"main"
Displays the name of the first routine in the program.
$maxstrlen
128
Defines the largest string to print fully.
$octints
0
Displays integers in octal radix.
$overloadmenu
1
Prompts for choice of overloaded C++ name.
$page
1
Paginates debugger terminal output.
$pagewindow
0
Defines the number of lines per output page.
$parentprocess
0
When the debugger detects a fork, it assigns the parent process ID to $parentprocess.
$pimode
0
Echoes input to log file on playback input.
$prompt
"(ladebug) "
Specifies debugger prompt.
$readtextfile
0
If set to non-zero, instructions are read from the text area of the binary file rather than from the memory image.
$regstyle
1
Controls the format of register names during disassembly. Valid settings are:
  • 0 = compiler names, for example, t0, ra, or zero.
  • 1 = hardware names, for example, r1, r26, or r31.
  • 2 = assembly names, for example, $1, $26, or $31.
$repeatmode
1
Repeats previous command when you press the Return key.
$showlineonstartup
0
Displays the first executable line in main.
$showwelcomemsg
1
Displays welcome message at startup time.
$stackargs
1
Shows arguments in the call stack if 1.
$statusargs
1
Prints breakpoints with parameters if 1.
$stepg0
0
Steps over routines with minimal symbols.
$stoponattach
0
Stops the running process on attach.
$stopparentonfork
0
Stops parent process execution on fork. When set to a nonzero value, this variable instructs the debugger to stop the parent process after it forks a child process. The child process continues to run if $catchforks is not set, otherwise stops. The default is 0.
$threadlevel
decthreads
Specifies POSIX threads (DECthreads) or native threads.
$usedynamictypes
1
Evaluates using C++ static or dynamic type.
$verbose
0
Produces even more output.



Appendix 2—Debugger Aliases

The debugger has the following predefined aliases:

Appendix 3—corefile_listobj.c Example

You can use the following example as an alternative to the listobj command for cases in which the debugger cannot be run on the original system. See the Transporting Core Files section for more information.
/*
  cc corefile_listobj.c -lxproc -o corefile_listobj
 */


#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>

#include <errno.h>

typedef unsigned long vma_t;

/* core file format */

#include <sys/user.h>
#include <sys/core.h>

/* dynamic loader hookup */

#include <loader.h>
typedef int (*ldr_reader_func)(vma_t from,
                               void * to,
                               long nbytes,
                               int is_string);
extern pid_t ldr_core_process();
extern int ldr_set_core_reader(ldr_reader_func reader);

/********************************************************************/

static FILE *                   corefile;
static struct core_filehdr      corehdr;
static int                      nsections;
static struct core_scnhdr *     section_headers;

int
open_corefile(const char * corename)
{
  size_t nread;

  corefile = fopen(corename, "rb");
  if (!corefile) {
    perror("Opening corefile");
    return -1;
  }
  nread = fread(&corehdr, sizeof(corehdr), 1, corefile);
  if (nread != 1) {
    perror("fread() of corefile header");
    return -1;
  }
  if (strncmp(corehdr.magic, "Core", 4) != 0) {
    fprintf(stderr, "Corefile header magic is not \"Core\"\n");
    return -1;
  }
  nsections = corehdr.nscns;
  section_headers = calloc(nsections, sizeof(section_headers[0]));
  if (!section_headers) {
    perror("Allocating corefile section headers");
    return -1;
  }
  nread = fread(section_headers, sizeof(section_headers[0]),
                nsections, corefile);
  if (nread != nsections) {
    perror("fread() of corefile section headers");
    return -1;
  }

  return 0;
}

static int
section_type_has_memory(int type)
{
  switch (type) {
  case SCNTEXT: case SCNDATA: case SCNRGN: case SCNSTACK:
    return 1;
  case SCNREGS: case SCNOVFL:
  default:
    return 0;
  }
}

static int
read_from_corefile(vma_t from,
                   void * to,
                   long nbytes,
                   int is_string)
{
  vma_t getter = from;
  char * putter = (char *) to;
  long to_go = nbytes;
  int secnum;
  size_t nxfer;

try_for_more:
  while (to_go > 0) {
    for (secnum = 0; secnum < nsections; secnum += 1) {
      if (section_type_has_memory(section_headers[secnum].scntype)) {
        vma_t vaddr = (vma_t) section_headers[secnum].vaddr;
        vma_t size  = (vma_t) section_headers[secnum].size;
        if (vaddr <= getter && getter < vaddr+size) {
          vma_t this_time = (size < to_go ? size : to_go);
          long file_offset = section_headers[secnum].scnptr+(getter-vaddr);
          if (fseek(corefile, file_offset, SEEK_SET) != 0) {
            perror("fseek() for corefile read");
            return -1;
          }
          nxfer = fread(putter, 1, this_time, corefile);
          if (nxfer != this_time) {
            perror("fread() of corefile data ");
            return -1;
          }
          to_go -= this_time;
          getter += this_time;
          putter += this_time;
          goto try_for_more;
        }
      }
    }
    fprintf("Couldn't find core address for %#lx\n", getter);
    return -1;
  }
  return 0;
}

int
main(int argc, char* argv[])
{
  pid_t process;

  if (argc != 2) {
    fprintf(stderr, "Usage is %s \n", argv[0]);
    return 1;
  }
  if (open_corefile(argv[1]) < 0)
    return -1;

  process = ldr_core_process();
  ldr_set_core_reader(read_from_corefile);

  if (ldr_xattach(process) < 0) {
    perror("Attaching to corefile");
    return 1;
  } else {
    ldr_module_t mod_id = LDR_NULL_MODULE;
    ldr_module_info_t info;
    size_t ret_size;
    while (1) {
      if (ldr_next_module(process, &mod_id) < 0) {
        perror("ldr_next_module");
        return 1;
      }
      if (mod_id == LDR_NULL_MODULE)
        break;
      if (ldr_inq_module(process, mod_id, &info,
                         sizeof(info), &ret_size) < 0) {
        perror("ldr_inq_module");
        return 1;
      }
      printf("%s\n", info.lmi_name);
    }
    ldr_xdetach(process);
    return 0;
  }
}

Appendix 4—Array Navigation Example

The debugger provides parameterized aliases and debugger variables of arbitrary types. Clever use of these can do almost any list traversal.

For example, here is how to navigate an array:

alias elt(e_) "{ p e_ }"
alias pa0(a)  "{ set $a = &a[0]; set $i = 0; elt($a[$i]); set $i = $i+1 }"
alias pan     "{ elt($a[$i]); set $i = $i+1 }"
pa0
pan
pan
pan

%ladebug a.out
...
(ladebug) alias elt(e_) "{ p e_ }"
(ladebug) alias a0(a)   "{ set $a = &a[0]; set $i = 0; elt($a[$i]); set $i = $i+1 }"
(ladebug) alias pan     "{ elt($a[$i]); set $i = $i+1 }"
...
(ladebug) pa0(a)
struct S {
  next = 0x140000178;
}
(ladebug) pan
struct S {
  next = 0x140000180;
}
(ladebug)
struct S {
  next = 0x140000188;
}
(ladebug)
struct S {
  next = 0x140000190;
}

Compaq, the Compaq logo, AlphaServer, and AlphaStation are Registered in the U.S. Patent and Trademark Office. Alpha and Tru64 are trademarks of Compaq Information Technologies Group, L.P.

Motif, UNIX, and The Open Group are trademarks of The Open Group. All other product names mentioned herein may be trademarks of their respective companies.

Confidential computer software. Valid license from Compaq required for possession, use, or copying. Consistent with FAR 12.211 and 12.212, Commercial Computer Software, Computer Software Documentation, and Technical Data for Commercial Items are licensed to the U.S. Government under vendor's standard commercial license.

Compaq shall not be liable for technical or editorial errors or omissions contained herein. The information in this publication is subject to change without notice and is provided "as is" without warranty of any kind. The warranties for Compaq products are set forth in the express limited warranty statements accompanying such products. Nothing herein should be construed as constituting an additional warranty.