Contained Within
Find More Documentation
Featured Support Resources
| Download this book in PDF
Debugging an OpenStep Application
A
- The SPARCworks Debugger is an interactive, window-based, source code and machine-instruction level debugging tool. It provides dynamic analysis for observing run-tme program behavior. The Debugger gives you complete control of the dynamic execution of a program, including the collection of performance data.
- The Debugger provides the same functionality as dbx, the command-line debugging tool, and you can enter dbx commands in the Command Pane of the Debugger base window.
- To debug an OpenStep application, click on the Debug button in the project window for the application in Project Builder. If the project has not been built yet, it is built first. If the project builds successfully, then the application is run in debug mode and the SPARCworks Debugger starts up. See the SPARCworks manual Debugging a Program for details on using the Debugger windows.
-
Note - If the project has already been built, you can Alt-click on the Debug button to run the application under the Debugger.
Debugger Objective C Support
- Release 3.1 of the SPARCworks Debugger includes support for Objective C applications, such as those developed using OpenStep.
Dynamic Types
- In Objective C an object pointer has two types:
-
- its static type, which is defined in the source code
- its dynamic type, which is known at run-time
- The Debugger can provide information about the dynamic type of an object when you use the print, display, inspect, and whatis commands with the -d option, or when you have set the dbx customization variable output_dynamic_type to on. If you use the +d option, the commands will use the static type.
- It is recommended that you put dbxenv output_dynamic_type on in your ~/.dbxrc file when debugging Objective C programs.
Finding Methods and Using Method Names in Non-expression Commands
- The following are non-expression commands:
-
-
stop in
funcs
whatis
list
edit
- Use the funcs command (with a regular expression) to find methods and functions that the Debugger knows about and to print them in a format the Debugger accepts. Use the dbx command help funcs for more information on the funcs command.
- If the process is active, the Debugger uses the run-time system to look up a method, otherwise it uses static information (stabs).
Setting Breakpoints
- The Debugger accepts the following variations of the stop command for setting breakpoints in Objective C methods:
-
-
stop in -[Test ival:second:]
-
-
stop in +[Test alloc]
stop in [Test ival:second:]
stop in [obj ival:second:]// through an object (only if active
process)
stop in ``ival:second: // searches all classes for ival:second:
stop in ival:second: // only if dbxenv scope_look_aside is `on'
stop inmethod ival:second:// stops in all methods with that name
- If the process is not active, use the following syntax for category methods:
-
-
stop in -[Test(Cat1) catmethod:second:]
Calling Objective C Methods
- All Objective C instance methods must be called through an object. The following are some valid variations of calling Objective C methods:
-
-
call [obj ival: 30] // calling instance method with parameter
call [self ival: 30] // use self if stopped inside a class
call [Test alloc] // calling class method
Recovering from a Run-time System Crash
- The Debugger calls the Objective C run-time system to look up methods and, if output_dynamic_type is on, to find the dynamic type of an object. In some cases this can cause a crash of the run-time system. The Debugger can usually recover if you use the pop command. Use the where command and then the pop command to unwind frames from the stack. You can also use the kill command to return to a previous Debugger level.
Sample .dbxrc File
- Your ~/.dbxrc file is read automatically if it exists when the Debugger starts up. The following is a sample .dbxrc file for debugging Objective C applications. This file is located in /usr/openstep/etc. To have the Debugger read this file when it starts up, add source /usr/openstep/etc/.dbxrc to your .dbxrc file.
-
-
##################################################################
# Objective C settings #
##################################################################
language objc
dbxenv scope_look_aside on // sets the dbx customization variable
scope_look_aside to on (find static
symbols even when not in scope)
dbxenv output_dynamic_type on // sets the dbx customization variable
output_dynamic_type to on (display,
inspect, print, whatis commands use
dynamic type of object)
# do
# call objc_enableMessageSendDebug(1)
# to enable the tracing of messages in objc_msgSend. This tracing is
# very fast and flexible. The above command will echo back all the
# info you need to use this feature.
function objchelp
{
echo "Add 'source /usr/openstep/etc/.dbxrc' to your ~/.dbxrc
file"
echo " to define helpful Objective C functions, aliases and
buttons."
echo " Look at this file in an editor to see what it contains."
echo "For more help, enter:"
echo " help general dbx help"
echo " help ObjC more Objective C help"
echo " help FAQ dbx - gdb correspondences, and other
information"
}
function memon # stop if an object is freed too many times. VERY
SLOW!!
{
-
-
call [NSAutoreleasePool enableDoubleReleaseCheck: 1]
stop in _NSAutoreleaseInconsistency
status
}
function memoff
{
call [NSAutoreleasePool enableDoubleReleaseCheck: 0]
}
function defbrks # breakpoints that catch errors
{
language objc
stop in abort
stop in -[NSObject doesNotRecognizeSelector:]
stop in +[NSAssertionHandler currentHandler]
stop in -[NSAssertionHandler
handleFailureInMethod:object:file:lineNumber:description:]
stop in -[NSAssertionHandler
handleFailureInFunction:file:lineNumber:description:]
stop in -[NSException raise]
stop in DPSDefaultErrorProc
stop in DPSCantHappen
stop in _exit
}
function morebrks # other helpful places to breakpoint
{
stop in NSLog
stop in _XErrorHandler
stop in -[Zombie forward::]
}
function allbrks # set breakpoints to catch errors
-
-
{
defbrks
morebrks
}
function pselfvar
{
print self->${1}
}
function pdesc
{
print [[$* description] cString]
}
function pnsstring
{
print [$* cString]
}
function prstar
{
print -r *($*)
}
function pcounts # print retain count and number of autoreleases of
$1
{
print [$* retainCount]
print [NSAutoreleasePool _numberOfObjectsIdenticalTo: $*]
}
# print string of an NSText object
-
-
dalias ptext print [[!1 text] cString] // sets dbx alias ptext to
print string of an NSText object
# print string of an NSCStringText object
dalias pcs print [[!1 cStringTextInternalState]->_string cString]
// sets dbx alias pcs to print string of
an NSCStringText object
dalias pns pnsstring // sets dbx alias pns for psstring command
dalias pd pdesc // sets dbx alias pd for pdesc command
dalias prs prstar // sets dbx alias prs for prstar command
alias typeof="print -l ((NSObject *)!:*)->isa->name" // sets dbx
alias typeof for printing type of
current object
dalias currwin "print -l [(NSView *)[NSView focusView] window]"
// sets dbx alias currwin for printing
current window
dalias flushcurrwin "print -l [((NSWindow *)[(NSView *)[NSView
focusView] window]) _forceFlushWindowToScreen]" // sets dbx alias
fluchcurrwin for synchronous flushing
of current window's off-screen buffer
to screen
button expand whatis // adds whatis button command;if selected
characters begin with alphanumeric
character, $, or _, then expands
selection and uses as target
button expand prstar // adds prstar button command;if selected
characters begin with alphanumeric
character, $, or _, then expands
selection and uses as target
button expand pselfvar // adds pselfvar button command;if
selected characters begin with
alphanumeric character, $, or _, then
expands selection and uses as target
button expand pnsstring // adds pnsstring button command;if
selected characters begin with
alphanumeric character, $, or _, then
-
-
expands selection and uses as target
button expand pcounts // add pcounts button command;if selected
characters begin with alphanumeric
character, $, or _, then expands
selection and uses as target
button expand pdesc // add pcounts button command;if selected
characters begin with alphanumeric
character, $, or _, then expands
selection and uses as target
button ignore defbrks // adds defbrks button command; ignores
current mouse selection for command
##################################################################
# General purpose settings #
##################################################################
toolenv cmdlines 20 // sets the number of lines in the
command subwindow to 20
dbxenv step_events on // sets the dbx customization variable
step_events to on to allow breakpoints
while stepping or "nexting"
dbxenv suppress_startup_message 4.0 // sets the dbx customization
variable suppress_startup_message to
set -o ignoresuspend # uncomment to cause dbx to ignore ^Z
set -o emacs # uncomment to enable emacs-style command
editing
#set -o vi # or uncomment this line for vi-style editing
function attach # attach to a running process
{
typeset PIG="$(/bin/ps -ef | /bin/egrep ${1} | /bin/egrep -v
egrep | /bin/head -1 | /bin/awk '{ print $8 " " $2 }')"
debug $PIG
}
function collOn # enable collector modes
{
collector work_set mode on
-
-
collector profile mode stack
}
function ff
{
where -f $(frame) 1
list
}
function penviron # dump the environment variables of the target
process
{
[ -z "$1" ] || { echo "$0: unexpected argument" >&2 && return; }
typeset -i i=0
typeset env="((char **)$[(char**)environ])"
while :
do
x=$[($env)[$i]]
echo "$i: " "${x#0x*\ }"
case "$x" in
*\(nil\)*) break;;
esac
((i += 1))
done
}
PS1="$SMSO(dbx !)$RMSO " # reverse-video prompt with history number
function _cb_prompt
{
if $mtfeatures
then # set prompt for MT debugging
PS1='${SMSO}${thread} ${lwp}:!${RMSO} '
-
-
else # set prompt for non-thread debugging
PS1='${SMSO}dbx:!${RMSO} '
fi
}
function hex # print arg in hex
{
: ${1?"usage: $0 <expr> # print <expr> in hex"}
typeset -i16 x
((x = $[(int)$*]))
echo - $* = $x
}
typeset -q hex
function hexdump # dump $2 (default: sizeof $1) bytes in hex
{
: ${1?"usage: $0 <exp> [<size>] # dump <size> bytes in hex"}
typeset -i16 p="$[(void *)&$1]" # address of $1
typeset -i s="${2:-$[sizeof ($1)]}" >/dev/null 2>&1 # number of
bytes
builtin examine $p/$[(${s:-4}+3)/4]X
}
typeset -q hexdump
function pg # print process status by name
{
/bin/ps -ef | /bin/egrep ${1} | /bin/egrep -v egrep
}
regs() # print register contents
{
case $# in
0) x &$g0/32X; x &$y/X; x &$psr/X; x &$pc/X; x &$npc/X ;;
*) for i
-
-
do reg=\$$i; x &$reg/X
done ;;
esac
}
dalias p print // sets dbx alias p for print command
dalias w where // sets dbx alias w for where command
dalias br where // sets dbx alias br for where command
dalias ww where -q // sets dbx alias ww for where -q (quick
traceback) command
dalias fr frame // sets dbx alias fr for frame command
dalias b stop in // sets dbx alias b for stop in command
dalias ba stop at // sets dbx alias ba for stop at command
dalias si stop in // sets dbx alias si for stop in command
dalias sa stop at // sets dbx alias sa for stop at command
dalias sic stop inclass // sets dbx alias sic for stop inclass
command
dalias sif stop infunction// sets dbx alias sif for stop
infunction command
dalias sim stop inmember // sets dbx alias sim for stop inmember
command
dalias sm stop modify // sets dbx alias sm for stop modify
command
dalias sr stop returns // sets dbx alias sr for stop returns
command
kalias cc="clear;cont" // sets Korn alias cc for clear command
followed by cont command
dalias cl clear // sets dbx alias cl for clear command
dalias ib status // sets dbx alias ib for status command
dalias st status // sets dbx alias st for status command
dalias d delete // sets dbx alias d for delete command
dalias r run // sets dbx alias r for run command
dalias c cont // sets dbx alias c for cont command
dalias s step // sets dbx alias s for step command
dalias su step up // sets dbx alias su for step up command
-
-
dalias n next // sets dbx alias n for next command
dalias di handler -disable// sets dbx alias di for handler
-disable command
dalias en handler -enable // sets dbx alias en for handler -enable
command
dalias N nexti // sets dbx alias N for nexti command
dalias S stepi // sets dbx alias S for stepi command
dalias q quit // sets dbx alias q for quit command
dalias tiny toolenv srclines 16; toolenv cmdlines 8// sets dbx alias
tiny to 16 lines in the source
subwindow and 8 lines in the command
subwindow
dalias mid toolenv srclines 25; toolenv cmdlines 25// sets dbx alias
mid to 25 lines in the source
subwindow and 25 lines in the command
subwindow
dalias big toolenv srclines 33; toolenv cmdlines 14// sets dbx alias
big to 33 lines in the source
subwindow and 14 lines in the command
subwindow
dalias und_all undisplay
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20 // sets dbx
alias und_all to undo display commands
1 through 20
dalias insense dbxenv case insensitive // sets dbx alias insense to
make case insignificant in variable
and function names
dalias sense dbxenv case sensitive // sets dbx alias sense to make
case significant in variable and
function names
button lineno cont at // adds button command cont at; uses
line number associated with current
selection as target of command
button ignore step up // adds button command step up; ignores
current mouse selection for command
button ignore tiny // adds button command tiny; ignores
current mouse selection for command
button ignore mid // adds button command mid; ignores
-
-
button ignore big // adds button command big; ignores
current mouse selection for command
button ignore quit // adds button command quit; ignores
Helpful User Default Variables to Set with dwrite
- The following user default variables, which you can set with the dwrite command, may be useful in debugging your application:
-
-
NSEnableAutoreleasePool
NSEnableDoubleReleaseCheck
NSHideOnDeactivateEnabled
NSPauseAtStartup
NSSetPoolThreshold
NSShowAllViews
NSShowAllWindows
NSShowDrawTimes
NSShowEvents
NSShowPS
NSShowWindowInfo
NSShowXEvents
NSTrapIllegalFloatingPointOps
Tracing Objective C Objects
- You can monitor Objective C messages being sent by objects in your application by calling the function objc_messageSendDebug. This function allows you to filter on different message attributes, and also stop at breakpoints when a certain filter matches the current message.
- This facility is particularly useful for finding memory allocation errors and performance problems.
- You can enable messageSendDebug in three ways:
-
Invoking messageSendDebug Using dwrite Commands
- You can automatically invoke messageSendDebug at execution time by using one of the following dwrite commands.
- This command turns on messageSendDebug, but no messages are sent until a filter is set:
-
-
dwrite AppName NSEnableMessageSendDebug YES
- This command suspends the display of messages, even if filters are set:
-
-
dwrite AppName NSEnableMessageSendDebug NO
- This command turns on messageSendDebug, and adds a generic filter to show all messages:
-
-
dwrite AppName NSEnableMessageSendDebug ALL
- This command displays an explanation of how to use this facility:
-
-
dwrite AppName NSEnableMessageSendDebug HELP
Adding Individual Message Filters
- You can add individual message filters with the following commands:
-
-
dwrite AppName NSMessageSendDebugFilter "ClassName | *,
[+ | -]selectorName | [+ | -]*, receiverID[hex or dec] | *, YES | NO
dwrite AppName NSMessageSendDebugFilter "GENERIC_FILTER"
dwrite AppName NSMessageSendDebugFilter1 "(AnotherSelectorName,
...)"
dwrite AppName NSMessageSendDebugFilterN "(SelectorNameN, ...)"
-
YES or NO applies to whether or not to call objc_messageMatchedFilter() when a filter matches. Enter YES if you want your program to hit a breakpoint when any filter matches (see "Setting a Breakpoint on a Filter Match" on page A-17).
-
GENERIC_FILTER shows all messages.
- If ClassName in the filter is *, any class counts as a match.
- If selectorName in the filter is *, any selector counts as a match.
- If selectorName in the filter is preceeded by a "+" or "-", only class methods, or instance methods (respectively) are considered matches.
- If receiverID in the filter is *, any receiver counts as a match.
- If ClassName, selectorName, and receiverID are all *, all messages are considered matches.
Controlling Call Level Indentation
- By default, the call level (nested level) of each method is shown in the matched filter output by indenting the line. At times this may be undesirable. To disable or enable this feature, use one of the following commands to control (typically turn off) call level indentation in all applications.
-
-
dwrite AppName NSEnableFilterCallLevelIndentation YES | NO
dwrite NSGlobalDomain NSEnableFilterCallLevelIndentation YES | NO
- This setting effects console output only, and has no effect on external debug-monitoring applications like ObjectDebug.
Invoking messageSendDebug from a Program or the Debugger
- For detailed information, see the file /usr/openstep/include/objc/objc-debug.h. Enabling messageSendDebug adds to, and does not preclude, filtering options you have set using dwrite.
Enabling messageSendDebug
- To enable messageSendDebug, call one of the following methods:
-
-
objc_enableMessageSendDebug(EnableDebug)
-
-
objc_enableMessageSendDebug(EnableDebugShowAllMessages)
objc_enableMessageSendDebug(EnableDebugSilently)
objc_enableMessageSendDebug(DisableDebug)
objc_enableMessageSendDebug(DisableDebugSilently)
- The following methods are equivalent to the above methods:
-
-
objc_enableMessageSendDebug(1)
objc_enableMessageSendDebug(2)
objc_enableMessageSendDebug(3)
objc_enableMessageSendDebug(0)
objc_enableMessageSendDebug(-1)
-
Note - You may want to disable this mechanism before making method calls in your debugger, as those method calls will get traced as well!
Adding Filters
- To add filters, you can call one of the following methods:
-
-
objc_addFilterFromString(const char *filterString)
objc_addFilterForClass(const char *className)
objc_addFilterForSelector(const char *selectorName)
objc_addFilterForReceiver(id receiver)
-
objc_addFilterFromString has the same syntax as the NSMessageSendDebugFilter dwrite command, with the addition of a FilterID field as the first value. This field lets you uniquely identify the filter.
- The filter string format looks like this:
-
-
objc_addFilterFromString("FilterID, ClassName | *,
[+ | -]selectorName | [+ | -]*, receiverID[hex or dec] | *, YES | NO
- or this:
-
-
objc_addFilterFromString("GENERIC_FILTER")
-
YES or NO applies to whether or not to call objc_messageMatchedFilter() when a filter matches. Enter YES if you want your program to hit a breakpoint when any filter matches (see "Setting a Breakpoint on a Filter Match" on page A-17).
-
GENERIC_FILTER shows all messages.
- If ClassName in the filter is *, any class counts as a match.
- If selectorName in the filter is *, any selector counts as a match.
- If selectorName in the filter is preceeded by a "+" or "-", only class methods, or instance methods (respectively) are considered matches.
- If receiverID in the filter is *, any receiver counts as a match.
- If ClassName, selectorName, and receiverID are all *, all messages are considered matches.
Controlling Call Level Indentation
- By default, the call level (nested level) of each method is shown in the matched filter output by indenting the line. At times this may be undesirable. To disable or enable this feature, call the following method:
-
-
objc_enableFilterCallLevelIndentation(0 | 1)
- This setting effects console output only, and has no effect on external debug-monitoring applications. It is unnecessary if the feature has already been enabled or disabled with dwrite.
Removing Filters
- To remove all filters, call the following method:
-
-
objc_removeAllFilters()
Disabling Filters
- To temporarily disable all filters, call the following method:
-
-
objc_enableMessageSendDebug(DisableDebug[0])
Setting a Breakpoint on a Filter Match
- If you want your program to hit a breakpoint when any filter matches, call the following method:
-
-
objc_callMessageMatchedFilter(0 | 1)
-
Note - objc_callMessageMatchedFilter sets this flag for all existing filters. To set the flag for individual filters, use the objc_addFilterFromString method to create your filter, or call objc_callMessageMatchedFilter after setting some filters, and then set the rest of your filters.
- Once you continue program execution, a string is printed indicating that the current message or receiver matched one of the set filters.
- After this string is printed, the function objc_messageMatchedFilter() is called by the Objective C runtime system.
- You can put a breakpoint at objc_messageMatchedFilter() to get a backtrace.
Examples
Example 1:
- To see all the messages sent to the NSAutoreleasePool class, either enter the following dbx commands in the Debugger Command Pane:
-
-
call objc_enableMessageSendDebug(1)
call objc_addFilterForClass("NSAutoreleasePool")
- or use the following dwrite commands at execution time:
-
-
dwrite AppName NSEnableMessageSendDebug YES
dwrite AppName NSMessageSendDebugFilter "NSAutoreleasePool,*,*,NO"
Example 2:
- To see all the addObject: messages sent to the NSAutoreleasePool class, and have Objective C call objc_messageMatchedFilter() when that message is sent (so you can hit a breakpoint there), either enter the following dbx commands in the Debugger Command Pane:
-
-
call objc_enableMessageSendDebug(1)
call
objc_addFilterFromString("1,NSAutoreleasePool,addObject:,*,YES")
- or use the following dwrite commands at execution time
-
-
dwrite AppName NSEnableMessageSendDebug YES
dwrite AppName NSMessageSendDebugFilter1
"NSAutoreleasePool,addObject:,*,YES"
- Notice the lack of the first parameter, the filterID, which is automatically generated in this case by the number that is appended to the dwrite key + 1000 (for example. "1001").
Example 3:
- To see all the class method calls (as opposed to instance method calls) sent to any object, and not show call level indentation, either enter the following dbx commands in the Debugger Command Pane:
-
-
call objc_enableMessageSendDebug(1)
call objc_addFilterForSelector("+*")
call objc_enableFilterCallLevelIndentation(0)
- or use the following dwrite commands at execution time:
-
-
dwrite AppName NSEnableMessageSendDebug YES
dwrite AppName NSMessageSendDebugFilter "*,+*,*,NO"
dwrite AppName NSEnableFilterCallLevelIndentation NO
Implementing Your Own Filtering Mechanism
- If you want to implement your own filtering mechanism, you can call the following function:
-
-
objc_setMessageSendFilterFunction(void
(*customFilterFunction)(Class receiverClass,id receiver, SEL
selector,void *callLevel, void *threadID))
- This function takes a pointer to a filterFunction. After calling this function, every message (objc_msgSend) that is sent will go through your own filter function. You can then implement your own filtering system.
- You can also call the following function from your filter function, in case you want to do the normal filtering stuff but tweak a few things first.:
-
-
objc_defaultMessageSendFilterFunction()
- You can get a linked list of all the currently set filters by calling the following:
-
-
objc_filterList(void)
Debugging Applications Using Optimized Libraries
- If you compile an application with -g but use libraries not compiled with -g, the Debugger does not know the prototypes of methods defined in the libraries. This means it does not know the types of the returned values, nor of the parameters. It assumes the types are int.
- For example, assume that set1 is an NSSet, You could use casts to tell the Debugger the return types of methods:
-
-
dbx:3 whatis set1
@interface NSSet *set1;
dbx:4 p [set1 description]
[set1 description] = -283014864
dbx:5 p (NSString *)[set1 description]
(@interface NSString *) [set1 description] = 0xef218bd0
dbx:6 p [(NSString *)[set1 description] cString]
[(@interface NSString *) [set1 description] cString] = -281204711
dbx:7 p (char *)[(NSString *)[set1 description] cString]
(char *) [(@interface NSString *) [set1 description] cString] =
0xef3d2841 "NSConcreteSet(a, b)"
- In order to obviate the need for these casts, Project Builder includes a special module named dbxInfo.o. This module contains debugging information for all the methods defined in the Application Kit and Foundation Kit libraries. When you do a debug build using the Project Builder Makefiles, this module is automatically linked into your application. In order to make this information available to the Debugger after it has started, Project Builder issues the following command to the Debugger as it is starting up:
-
-
module dbxInfo.o
- This command causes the Debugger to read in the debugging information contained in dbxInfo.o so it is available when the Debugger has to determine the return types and parameter types of methods invocations.
|
|