|A P P E N D I X B|
Event management refers to the capability of dbx to perform actions when events take place in the program being debugged. When an event occurs, dbx allows you to stop a process, execute arbitrary commands, or print information. The simplest example of an event is a breakpoint (see Chapter 6). Examples of other events are faults, signals, system calls, calls to dlopen(), and data changes (see Setting Data Change Breakpoints).
This appendix is organized into the following sections:
Event management is based on the concept of a handler. The name comes from an analogy with hardware interrupt handlers. Each event management command typically creates a handler, which consists of an event specification and a series of side-effect actions. (See Setting Event Specifications.) The event specification specifies the event that will trigger the handler.
When the event occurs and the handler is triggered, the handler evaluates the event according to any modifiers included in the event specification. (See Event Specification Modifiers.) If the event meets the conditions imposed by the modifiers, the handler's side-effect actions are performed (that is, the handler "fires").
An example of the association of a program event with a dbx action is setting a breakpoint on a particular line.
The most generic form of creating a handler is by using the when command.
Examples in this chapter show how you can write a command (like stop, step, or ignore) in terms of when. These examples are meant to illustrate the flexibility of when and the underlying handler mechanism, but they are not always exact replacements.
Use the commands when, stop, and trace to create event handlers. (For detailed information, see when Command, stop Command, and trace Command.)
stop is shorthand for a common when idiom.
An event-specification is used by the event management commands stop, when, and trace to specify an event of interest. (see Setting Event Specifications).
Most of the trace commands can be handcrafted using the when command, ksh functionality, and event variables. This is especially useful if you want stylized tracing output.
Every command returns a number known as a handler id (hid). You can access this number using the predefined variable $newhandlerid.
You can use the following commands to manipulate event handlers. For more information on each command, see the cited section.
An event handler has a trip counter, which has a count limit. Whenever the specified event occurs, the counter is incremented. The action associated with the handler is performed only if the count reaches the limit, at which point the counter is automatically reset to 0. The default limit is 1. Whenever a process is rerun, all event counters are reset.
You can set the count limit using the -count modifier with a stop, when, or trace command (see -count n -count infinity). Otherwise, use the handler command to individually manipulate event handlers:.
Event specifications are used by the stop,stopi, when, wheni, trace,and tracei commands to denote event types and parameters. The format consists of a keyword representing the event type and optional parameters. The meaning of an event specification is generally identical for all three commands; exceptions are documented in the command descriptions (see stop Command, trace Command, and when Command).
A breakpoint is a location where an action occurs, at which point the program stops executing. The following are event specifications for breakpoint events.
The function has been entered, and the first line is about to be executed. The first executable code after the prolog is used as the actual breakpoint location. This may be a line where a local variable is being initialized. In the case of C++ constructors, execution stops after all base class constructors have executed. If the -instr modifier is used (see -instr), it is the first instruction of the function about to be executed. The function specification can take a formal parameter signature to help with overloaded function names or template instance specification. For example:
The designated line is about to be executed. If you specify filename, then the designated line in the specified file is about to be executed. The file name can be the name of a source file or an object file. Although quotation marks are not required, they may be necessary if the file name contains special characters. If the designated line is in template code, a breakpoint is placed on all instances of that template.
The instruction at the given address is about to be executed. This event is available only with the stopi command (see stopi Command) or with the -instr event modifier (see -instr).
Equivalent to in function for all overloaded functions named function or all template instantiations thereof.
Equivalent to in function or the member function named function for every class.
Equivalent to in function for all member functions that are members of classname, but not any of the bases of classname. -norecurse is the default. If -recurse is specified, the base classes are included.
A member function called on the specific object at the address denoted by object-expression has been called. stop inobject ox is roughly equivalent to the following, but unlike inclass, bases of the dynamic type of ox are included. -recurse is the default. If -norecurse is specified, the base classes are not included.
The following are event specifications for events that involve access or change to the contents of a memory address.
The memory specified by address-expression has been accessed.
mode specifies how the memory was accessed. It can be composed of one or all of the letters:
mode can also contain either of the following:
In both cases the program counter will point at the offending instruction. The "before" and "after" refer to the side effect.
address-expression is any expression that can be evaluated to produce an address. If you give a symbolic expression, the size of the region to be watched is automatically deduced; you can override it by specifying byte-size-expression. You can also use nonsymbolic, typeless address expressions; in which case, the size is mandatory. For example:
The access command has the limitation that no two matched regions may overlap.
The value of variable has changed. The change event is roughly equivalent to:
This event is implemented using single-stepping. For faster performance, use the access event (see access mode address-expression [, byte-size-expression]).
The first time variable is checked causes one event, even though no change is detected. This first event provides access to the initial value of variable. Subsequent detected changes in the value of variable trigger additional events.
The condition denoted by condition-expression evaluates to true. You can specify any expression for condition-expression, but it must evaluate to an integral type. The cond event is roughly equivalent to:
stop step -if conditional_expression
The following are event specifications for system events.
These events occur after a dlopen() or a dlclose() call succeeds. A dlopen() or dlclose() call can cause more than one library to be loaded. The list of these libraries is always available in the predefined variable $dllist. The first shell word in $dllist is a "+" or a "-", indicating whether the list of libraries is being added or deleted.
lib-path is the name of a shared library. If it is specified, the event occurs only if the given library was loaded or unloaded. In that case, $dlobj contains the name of the library. $dllist is still available.
If lib-path begins with a /, a full string match is performed. Otherwise, only the tails of the paths are compared.
If lib-path is not specified, then the events always occur whenever there is any dl-activity. In this case, $dlobj is empty but $dllist is valid.
The fault event occurs when the specified fault is encountered. The faults are architecture-dependent. The following set of faults known to dbx is defined in the proc(4) man page.
These faults are taken from /sys/fault.h. fault can be any of those listed above, in uppercase or lowercase, with or without the FLT- prefix, or the actual numerical code.
The lwp_exit event occurs when lwp has been exited. $lwp contains the id of the exited LWP (lightweight process) for the duration of the event handler.
The sig signal event occurs when the signal is first delivered to the program being debugged. signal can be either a decimal number or the signal name in uppercase or lowercase; the prefix is optional. This is completely independent of the catch and ignore commands, although the catch command can be implemented as follows:
When the specified signal with the specified sub-code is first delivered to the child, the sig signal sub-code event occurs. As with signals, you can type the sub-code as a decimal number, in uppercase or lowercase; the prefix is optional.
The specified system call has just been initiated, and the process has entered kernel mode.
The concept of system call supported by dbx is that provided by traps into the kernel as enumerated in /usr/include/sys/syscall.h.
This is not the same as the ABI notion of system calls. Some ABI system calls are partially implemented in user mode and use non-ABI kernel traps. However, most of the generic system calls (the main exception being signal handling) are the same between syscall.h and the ABI.
Note - The list of kernel system call traps in /usr/include/sys/syscall.h is part of a private interface in the Solaris operating environment that changes from release to release. The list of trap names (codes) and trap numbers that dbx accepts includes all of those supported by any of the versions of the Solaris operating environment that dbx supports. It is unlikely that the names supported by dbx exactly match those of any particular release of the Solaris operating environment, and some of the names in syscall.h might not be available. Any trap number (code) is accepted by dbx and works as expected, but a warning is issued if it does not correspond to a known system call trap.
The specified system call is finished, and the process is about to return to user mode.
Without arguments, all system calls are traced. Certain dbx features, for example, the modify event and runtime checking, cause the child to execute system calls for its own purposes and show up if traced.
The following are event specifications for events pertaining to execution progress.
The exit event occurs when the process has exited.
The next event is similar to the step event except that functions are not stepped into.
The returns event is a breakpoint at the return point of the current visited function. The visited function is used so that you can use the returns event specification after giving a number of step up commands. The returns event is always -temp and can only be created in the presence of a live process.
The returns function event executes each time the given function returns to its call site. This is not a temporary event. The return value is not provided, but you can find integral return values by accessing the following registers:
The event is roughly equivalent to:
The step event occurs when the first instruction of a source line is executed. For example, you can get simple tracing with:
When enabling a step event, you instruct dbx to single step automatically next time the cont command is used.
Note - The step (and next) events do not occur upon the termination of the step command. The step command is implemented in terms of the step event roughly as follows:
The following are event specifications for other types of events.
dbx has successfully attached to a process.
dbx has successfully detached from the program being debugged.
The process being debugged is about to expire, which can happen for the following reasons:
The final state of the process is usually, but not always, available when this event is triggered, giving you your last opportunity to examine the state of the process. Resuming execution after this event terminates the process.
The proc_gone event occurs when dbx is no longer associated with a debugged process. The predefined variable $reason may be signal, exit, kill, or detach.
The prog_new event occurs when a new program has been loaded as a result of follow exec.
The process has stopped. The stop event occurs whenever the process stops such that the user receives a prompt, particularly in response to a stop handler. For example, the following commands are equivalent:
The process being debugged has just been executed with exec(). All memory specified in a.out is valid and present, but preloaded shared libraries have not been loaded. For example, printf, although available to dbx, has not been mapped into memory.
A stop on this event is ineffective; however, you can use the sync event with the when command.
The syncrtld event occurs after a sync (or attach if the process being debugged has not yet processed shared libraries). It executes after the dynamic linker startup code has executed and the symbol tables of all preloaded shared libraries have been loaded, but before any code in the .init section has run.
A stop on this event is ineffective; however, you can use the syncrtld event with the when command.
The thr_create event occurs when a thread, or a thread with the specified thread_id, has been created. For example, in the following stop command, the thread ID t@1 refers to creating thread, while the thread ID t@5 refers to the created thread.
The thr_exit event occurs when a thread has exited. To capture the exit of a specific thread, use the -thread option of the stop command as follows:
The throw event occurs whenever any exception that is not unhandled or unexpected is thrown by the application.
If an exception type is specified with the throw event, only exceptions of that type cause the throw event to occur.
-unhandled is a special exception type signifying an exception that is thrown but for which there is no handler.
-unexpected is a special exception type signifying an exception that does not satisfy the exception specification of the function that threw it.
The timer event occurs when the program being debugged has been running for seconds. The timer used with this event is shared with collector command. The resolution is in milliseconds, so a floating point value for seconds, for example 0.001, is acceptable.
An event specification modifier sets additional attributes of a handler, the most common kind being event filters. Modifiers must appear after the keyword portion of an event specification. A modifier begins with a dash (-). The following are the valid event specification modifiers.
The condition is evaluated when the event specified by the event specification occurs. The side effect of the handler is allowed only if the condition evaluates to nonzero.
If the -if modifier is used with an event that has an associated singular source location, such as in or at, condition is evaluated in the scope corresponding to that location. Otherwise, qualify it with the desired scope.
The -resumeone modifier can be used with the -if modifier in an event specification for a multithreaded program, and causes only one thread to be resumed if the condition contains function calls. For more information, see Using a Filter With a Conditional Event.
The event triggers only if it occurs between the time the first instruction of the given function is reached and the time the function returns. Recursion on the function are ignored.
The -disable modifier creates the handler in the disabled state.
The -count n and -count infinity modifiers have the handler count from 0 (see Using Event Counters). Each time the event occurs, the count is incremented until it reaches n. Once that happens, the handler fires and the counter is reset to zero.
Counts of all enabled handlers are reset when a program is run or rerun. More specifically, they are reset when the sync event occurs.
The count is reset when you begin debugging a new program with the debug -r command (see debug Command) or the attach -r command (see attach Command).
Creates a temporary handler. Once the event has occurred it is automatically deleted. By default, handlers are not temporary. If the handler is a counting handler, it is automatically deleted only when the count reaches 0 (zero).
Use the delete -temp command to delete all temporary handlers.
Makes the handler act at an instruction level. This event replaces the traditional 'i' suffix of most commands. It usually modifies two aspects of the event handler:
The action is executed only if the thread that caused the event matches thread_id. The specific thread you have in mind might be assigned a different thread_id from one execution of the program to the next.
The action is executed only if the thread that caused the event matches lwp_id. The action is executed only if the thread that caused the event matches lwp_id. The specific thread you have in mind might be assigned a different lwp_id from one execution of the program to the next.
Hides the handler in a regular status command. Use status -h to see hidden handlers.
Normally all handlers are thrown away when a new program is loaded. Using the -perm modifier retains the handler across debugging sessions. A plain delete command does not delete a permanent handler. Use delete -p to delete a permanent handler.
The syntax for event specifications and modifiers is:
Expressions can have spaces embedded in them, causing ambiguous situations. For example, consider the following two commands:
In the first example, even though the application might have a variable named temp, the dbx parser resolves the event specification in favor of -temp being a modifier. In the second example, a-temp is collectively passed to a language-specific expression parser. There must be variables named a and temp or an error occurs. Use parentheses to force parsing.
Certain read-only ksh predefined variables are provided. The following variables are always valid:
ID of the most recently created handler. This variable has an undefined value after any command that deletes handlers. Use the variable immediately after creating a handler. dbx cannot capture all of the handler IDs for a command that creates multiple handlers.
Previous value of $prog, which is used to get back to what you were debugging following an exec(), when $prog reverts to "-". While $prog is expanded to a full path name, $oprog contains the program path as specified on the command line or to the debug command. If exec() is called more than once, there is no way to return to the original program.
As an example, consider that whereami can be implemented as:
The following variables are valid only within the body of a when command.
During the execution of the body, $handlerid is the id of the when command to which the body belongs. These commands are equivalent:
$booting is set to true if the event occurs during the boot process. Whenever a new program is debugged, it is first run without the user's knowledge so that the list and location of shared libraries can be ascertained. The process is then killed. This sequence is termed booting.
While booting is occurring, all events are still available. Use this variable to distinguish the sync and the syncrtld events occurring during a debug and the ones occurring during a normal run.
$newlwp is set to the lwp-id of a newly created LWP.
Certain variables are valid only for specific events as shown in the following tables.
The following are some examples of setting event handlers.
To set a breakpoint on array, type:
To implement a simple trace, type:
To enable a handler while within a function, type:
This is equivalent to:
To see how many lines have been executed in a small program, type:
The program never stops--the program terminates. The number of lines executed is 133. This process is very slow. It is most useful with breakpoints on functions that are called many times.
To count how many instructions a line of code executes, type:
If the line you are stepping over makes a function call, the lines in the function are counted as well. You can use the next event instead of step to count instructions, excluding called functions.
Enable a breakpoint only after another event has occurred. For example, if your program begins to execute incorrectly in function hash, but only after the 1300'th symbol lookup, you would type:
If your application processes files that need to be reset during a replay, you can write a handler to do that each time you run the program:
To see quickly where the program is while it is running, type:
Then type ^C to see a stack trace of the program without stopping it.
This is basically what the collector hand sample mode does (and more). Use SIGQUIT (^\) to interrupt the program because ^C is now used up.
To catch only specific floating point exceptions, for example, IEEE underflow, type: