| 
    
        | 
 | This technote shows how to
                  translate your experience with MacsBug into a
                  working knowledge of the GNU source-level debugger
                  (GDB) as used on Mac OS X. The focus is on using
                  GDB as an assembly-level tool for debugging
                  programs for which you don't have source (for
                  example, debugging crashes inside Carbon itself) or
                  for which the source is inaccessible (for example,
                  debugging a crash that only occurs at a customer's
                  site). However, many of the tricks you learn here
                  will also be useful for more mundane source-level
                  debugging. This technote is primarily directed
                  at application developers (and other developers of
                  user space code) who are familiar with MacsBug and
                  want a quick introduction to GDB. If you're not
                  already familiar with MacsBug, you probably should
                  skip this technote and go straight to the GDB
                  manual. If you're developing kernel code (for
                  example, an IOKit driver) you will find most of
                  this technote useful but you must also learn the
                  two-machine debugging techniques described in
                  other
                  Apple documentation. [Oct 18 2001] |  
 
 
 
 
 
 
 IntroductionThe first step in a successful transition from MacsBug to
         GDB is...   If you're used to MacsBug, learning GDB won't be that
         hard. And while you'll miss some of MacsBug's nicer features
         (the showpath'dcmd' for example) you'll be
         impressed by some of the cool things that GDB can do for you
         (future break is one of my favorites). So, let's get
         started. About GDBThe GNU source-level debugger (GDB) is the primary
         debugging tool on Mac OS X. Whenever you debug on Mac OS X,
         you are using GDB in one form or another. 
            GDB is available as a standard command-line tool
            (gdb) that you can run from Terminal.Project Builder provides a graphical front end to
            GDB's commonly-used debugging facilities. However, there
            are cases where you might want to enter GDB commands
            directly, either by starting GDB from the command line or
            by entering commands at Project Builder's GDB
            prompt.The CodeWarrior source-level debugger is currently
            layered on top of GDB (although there is no direct access
            to GDB from within CodeWarrior). This technote concentrates on using GDB from the command
         line. This is in line with the overall goal of exploring GDB
         as an assembly-level debugging tool. While it is possible to define macros to make GDB behave
         like MacsBug (for an amazing example, check out the
         MacsBug GDB plug-in), that's
         not a good long-term choice in my opinion. Mac OS X is the
         long-term direction of Apple, and GDB will be the core
         debugger on Mac OS X for the foreseeable future. It behooves
         you to learn GDB now rather than later. GDB, as its full name suggests, is primarily a
         source-level debugger. This has its pros and cons. There's
         no denying that source-level debugging is easier and its
         absence in MacsBug has always been sorely missed. However,
         GDB's focus on source-level debugging means that it isn't a
         great assembly-level debugger. It is, however, good enough
         to get the job done. Conventions Used In This TechnoteThroughout this technote the following styles have
         special meaning. 
            Monospace textindicates a GDB or
            MacsBug command, or text output by the computer.Bold monospace textindicates
            commands entered by the user.Italic monospace textindicates
            comments.A backslash (\) at the end of a line indicates that
            the line is continued on the next line.^Xindicates that you should enter a
            control character (for example, the notation ^C means you
            should hold down the control key and press the C
            key).In debugger command descriptions, meta variables are
            shown in angle brackets. For example, DM
            <addr>indicates that you should substitute
            an address for<addr>. GDB BasicsWhen you break into MacsBug on traditional Mac OS you
         stop the entire computer. This is not the case with GDB on
         Macs OS X. When you break into GDB you stop only the process
         that you're debugging. This allows you to look up source
         code and documentation while you're debugging. There are two basic ways of targeting the process you
         want to debug. 
            Attach to the process after it's started runningRun your program from within GDB. The first approach has the advantage that you can start
         debugging a program after it's been executing for some time.
         You don't need to know in advance that this is a debugging
         session. You can start debugging when you notice the program
         misbehaving. The disadvantage of this approach is that you
         have to jump through some hoops to see the program's output
         to stdoutandstderr. The
         technique for doing this is explained later
         in this document. The second approach lets you see your program's output to
         stdoutandstderreasily. You
         should use it when you're actively debugging a problem. Attaching to a Running
         ProcessListing 1 shows how to attach GDB to a running process.
         You start by launching Terminal and running GDB from the
         command line (gdb). Once you've started
         GDB you can either attach by name or by BSD process ID.
         First you should try attaching by name (attach
         BBEdit). This can fail for a variety of
         reasons--in this example it failed because the application
         is a CFM binary--in which case you should attach by BSD
         process ID. You can find the BSD process ID using GDB'sshellcommand to execute thepscommand line tool. This example usesgrepto
         search the output (which would be rather large otherwise).
         The first column of numbers is the BSD process ID. You can
         use that BSD process ID to attach to the process
         (attach 795). 
            
               |  Listing 1. Attaching to a running
                  process 
                     
                        | [localhost:~] quinn% gdb
[... output omitted ...]
(gdb) attach BBEdit
Unable to locate process named "BBEdit".
(gdb) shell ps auxww | grep BBEdit
quinn   823   [...] -csh -c ps auxww | grep BBEdit (tcsh)
quinn   795   [...] /Volumes/YipYip/MyApplications/BBEdit [...]
(gdb) attach 795
[... output omitted ...]
(gdb) |  
 |  
 
            
               | IMPORTANT:A BSD process ID (PID) is the BSD way of
                  identifying a process. It is not the same as a
                  Carbon Process Serial Number (PSN), although there
                  are routines to map between the two in
                  "Processes.h".
 |  
 
            
               | Note:To attach to a running application either it must
                  by owned by you or you must have root
                  privileges.
 |  
 
            
               | Note:The
 attachcommand supports Tab
                  completion. If you type a partial process name and
                  press Tab, GDB will complete the full name. |  
 Running Your Program From
         Within GDBRunning a program from within GDB requires different
         actions depending on the program's binary format. For Mach-O
         programs you can simply enter the path to the executable on
         the command line as you launch GDB. This is shown in Listing
         2. GDB will stop after reading the program's symbols. You
         can then set breakpoints, or just run the program
         (r). 
            
               |  Listing 2. Running a Mach-O program
                  under GDB 
                     
                        | [localhost:~] quinn% gdb /Applications/TextEdit.app/\
Contents/MacOS/TextEdit
[... output omitted ...]
(gdb) r |  
 |  
 
            
               | IMPORTANT:You must enter the path to the executable, not the
                  package. For example, enter
                  "/Applications/TextEdit.app/Contents/MacOS/TextEdit"
                  and not "/Applications/TextEdit.app".
 |  
 If your program is built as a CFM binary you must do a
         little more work. Rather than pointing GDB at the
         application itself, you must point it at the CFM loader
         program (LaunchCFMApp) that is responsible for loading and
         running the CFM application. Listing 3 shows how this is
         done. You must enter the path to the CFM binary (again, this
         is the executable, not the package) as an argument to the
         run (r) command. This tells LaunchCFMApp which
         CFM application you want to launch. 
            
               |  Listing 3. Running a CFM program
                  under GDB 
                     
                        | [localhost:~] quinn% gdb /System/Library/Frameworks/\
Carbon.framework/Versions/A/Support/LaunchCFMApp
[... output omitted ...]
(gdb) r /Applications/Internet\ Explorer/\
Internet\ Explorer.app/Contents/MacOS/Internet\ Explorer |  
 |  
 Breaking into GDB and Entering CommandsTo stop your program in the debugger, just switch to the
         appropriate Terminal window and enter ^C. All the threads in
         your program will stop and GDB will present you with the
         (gdb)prompt. Your program will immediately
         stop responding to events (if you trying to click on one of
         its windows you will get the spinning rainbow beach ball
         cursor of death). You can continue executing your
         program with the continue command (c).
         Listing 4 shows this entire process. 
            
               |  Listing 4. Stopping and continuing
                  a program 
                     
                        | ^C
Program received signal SIGINT, Interrupt.
0x700009a8 in mach_msg_overwrite_trap ()
(gdb) c
Continuing. |  
 |  
 When you see the GDB prompt you can enter GDB commands.
         GDB accepts commands in much the same way as MacsBug. You
         type the name of the command followed by any command
         arguments and then press Return to execute the command. Like
         MacsBug, GDB supports command line editing
         and history. You can also press Return to re-execute the
         previous command. However, there are two significant
         differences from the basic MacsBug command loop. 
            MacsBug commands tend to be short and cryptic. GDB
            commands, on the other hand, are long and explanatory.
            Fortunately, you don't have to type the entire command.
            Any unambiguous abbreviation of the command will do. For
            example, the full command to see the registers is
            info registers. A more succinct abbreviation
            isinfo reg. However, if you really hate
            typing you can abbreviate this all the way down toi r. GDB has special logic that allows one
            letter commands to work properly even if they are
            ambiguous. For example, even thoughcis the
            first letter of many commands, it is always interpreted
            as (the very commonly used)continuecommand. Finally, pressing Tab will autocomplete an
            unambiguous command and pressing Tab twice will list
            alternatives for an ambiguous command.In MacsBug you can type an expression on the command
            line and MacsBug will evaluate it and display the result.
            GDB requires you to do something with the expression. If
            you want to see its value, simply print it using the
            printcommand (orpfor short).
            Unlike MacsBug, the print command only displays the
            results in one format at a time. To control that format,
            append a '/' and a format specifier ('o' for octal, 'x'
            for hex, 'd' for decimal, 'u' for unsigned decimal, 't'
            for binary, 'f' for float, 'a' for address, 'c' for
            char). You can also append a size qualifier ('b' for 1
            byte, 'h' for 2 bytes, 'w' for 4 bytes, 'g' for 8
            bytes). 
            
               | MacsBug | GDB |  
               | command-power
 | ^C[1]
 |  
               | Gorcommand-G
 | c[2]
 |  
               | HELP <command>
 | help <command>
 |  
               |  | apropos <regex>[3]
 |  
               | <expr>
 | p/x <expr>[4]
 |  
 Notes: 
            You must enter ^Cin the Terminal window
            in which you're running GDB.cis short forcontinue.aproposwill print all GDB help entries
            that match the regular expression. This is really useful
            if you're totally lost. For example, lets say you want
            change the default radix for numbers input and printed by
            GDB.help radixis no help at all, butapropos radixis very useful.pis short forprint, while/xspecifies that the number should be
            printed in hex. You can use any of the format specifiers
            listed above. Stopping DebuggingYou can stop debugging in a variety of ways. 
            Quit your program -- GDB continues to run but is no
            longer attached to your programDetach from your program (using GDB's
            detachcommand) -- GDB will keep running but
            the connection between it and your program is
            broken.Force quit your program (using GDB's
            killcommand) -- This is akin to the MacsBugEScommand. Your program dies but GDB keeps
            running.Quit GDB (using GDB's quitcommand) --
            If you quit GDB while it's still attached to your
            program, GDB will either force quit it (if you ran the
            program from within GDB) or detach from it (if you
            attached to it) before quitting. 
 Back to top 
 MacsBug To GDB ReferenceThe following sections describe the GDB equivalent for
         various MacsBug constructs. The sections are structured
         along similar lines to the MacsBug online help. EditingGDB supports command-line editing in much the same way as
         MacsBug, but of course all the key bindings are different.
         You can configure the GDB key bindings--see the GDB
         manual for more information on this--but the default
         configuration is described below. 
            
               | MacsBug | GDB |  
               | left arrow
 | left arrowor^B
 |  
               | right arrow
 | right arrowor ^F
 |  
               | option-left arrow
 | escape B
 |  
               | option-right arrow
 | escape F
 |  
               | option-delete
 | escape delete
 |  
               | command-left arrow
 | ^A
 |  
               | command-right arrow
 | ^E
 |  
               | command-delete
 | ^U
 |  
               | command-V
 | up arrow
 |  
               | command-B
 | down arrow
 |  
 Selecting Procedure NamesLike MacsBug, GDB is smart enough to find some symbols
         even if the program wasn't compiled with debugger symbols.
         You can learn more about accessing symbols in the Symbols
         section later in this document. 
            
               | MacsBug | GDB |  
               | command-D or command-; | Tab Tab [1] |  
               |  | info func <regex> [2] |  
 Notes: 
            The first Tab tries to complete the symbol; if there
            is no unambiguous completion, the second Tab lists the
            alternatives.This lists all the functions whose names match the
            <regex>. Using an overly general regular expression
            (for example, info func .*) can take a very
            long time if your program uses large system frameworks
            (for example, the Carbon framework). ValuesFor an experienced MacsBug user GDB values are hard to
         get used to. You must remember to use $ in front of any
         register names you use. By default a numeric constant is in
         decimal (not hex), and you must prefix hex numbers with 0x
         and not $ ($ is used for registers, the value
         history and GDB convenience
         variables). There's also no support for unit suffixes
         (K, M, G, and ß). Finally, $_ behaves much the same as
         "." in MacsBug (it's the last address examined
         by "x"), while $__ is like
         ".^". One nice feature of GDB is the
         value history. Every time you print an expression GDB prints
         the value of the expression along with a dollars sign ($)
         and a number. You can use this dollars sign and number
         combination to recall the value in subsequent expressions.
         You can see an example of this in Listing 5. GDB also supports convenience variables, which are
         discussed in more detail later in this
         technote. 
            
               | MacsBug | GDB |  
               | <register>
 | $<register>
 |  
               | $<hexnum>
 | 0x<hexnum>
 |  
               | #<decnum>
 | <decnum>
 |  
               | K,M,Gandß(postfix)
 | not supported |  
               | .
 | $_
 |  
               | 
(option-;)
 | not supported |  
               | :
 | not supported |  
 
            
               | Note:GDB does let you change the default input and
                  output radix. Type
 apropos radixfor
                  details. |  
 Expressions and OperatorsGDB uses a C-like syntax for its expressions, so it
         shouldn't be hard for you to pick up. One big difference
         between this and MacsBug is that each GDB expression has an
         associated type. For example, GDB knows that the type of
         argvis a pointer to a pointer to a character.
         If you use it in an expression, GDB can work out the type of
         the result--GDB knows that**argvis a
         character. Thus, MacsBug's naive implementation of the
         indirection operation (@ or ^) is replaced by the GDB's
         C-like operator (*) which carries type information with it.
         This means that you usually don't need MacsBug's size
         postfix operators (.B,.W, and.L) because GDB already knows the right size of
         an indirection. If necessary you can override GDB's type
         information by using a cast. Some examples of this are shown
         in Listing 5. 
            
               |  Listing 5. Using casts in
                  expressions. 
                     
                        |  $r3 is an integer, but you can cast it.
(gdb) p/x *(char *)$r3
$6 = 0x0
(gdb) p/x *(short *)$r3
$7 = 0x3
(gdb) p/x *(int *)$r3
$8 = 0x3e360
 You can also use the brace notation when printing.
(gdb) p/x {char}$r3
$9 = 0x0
(gdb) p/x {short}$r3
$10 = 0x3
(gdb) p/x {long}$r3
$11 = 0x3e360
 Accessing the value history.
(gdb) p/x $11
$12 = 0x3e360 |  
 |  
 
            
               | Note:GDB's indirection operation (*) is more lenient
                  that an ANSI C compiler in that it will let you
                  treat an integer value as a pointer. So an
                  expression like *0x01000 works in GDB, but wouldn't
                  work in C without a cast.
 |  
 
            
               | Note:GDB's expression syntax is actually dependent on
                  the language you're debugging. However, when you're
                  debugging without source the default C-like syntax
                  applies.
 |  
 
            
               | MacsBug | GDB |  
               | +
 | +
 |  
               | -
 | -
 |  
               | *
 | *
 |  
               | /or÷
 | /
 |  
               | MOD
 | %
 |  
               | ANDor&
 | &or&&[1]
 |  
               | ORor|
 | |or||[1]
 |  
               | NOTor!
 | ~or![1]
 |  
               | XOR
 | ^
 |  
               | <<
 | <<
 |  
               | >>
 | >>
 |  
               | =or==
 | ==
 |  
               | <>or!=or≠
 | !=
 |  
               | <
 | <
 |  
               | >
 | >
 |  
               | <=
 | <=
 |  
               | >=
 | >=
 |  
               | @(prefix) or^(postfix)
 | *(prefix)
 |  
               | .B
 | not supported [2] |  
               | .W
 | not supported [2] |  
               | .L
 | not supported [2] |  
 Notes: 
            MacsBug makes no distinction between bitwise and
            logical operators (AND and & are identical). GDB
            supports both types, with the standard C syntax.You can achieve the same effect with a cast, as shown
            in Listing 5. Flow ControlGDB has much the same flow control facilities as MacsBug.
         Note that the commands with a trailing 'i' (the 'i' stands
         for instruction) work at the assembly level and step one
         instruction at a time. You must use these variants when
         debugging without source. When stepping one instruction at a
         time, you might want to use the displaycommand
         (as shown in Listing 6) to display the current instruction
         after each command. Alternatively, activate the MacsBug
         GDB plug-in. 
            
               |  Listing 6. Using
                  displaywhen stepping through
                  instructions 
                     
                        | Breakpoint 1, 0x70268f8c in HLock ()
(gdb) display/i $pc
2: x/i $pc  0x70268f8c <HLock>: mflr    r0
(gdb) si
0x70268f90 in HLock ()
2: x/i $pc  0x70268f90 <HLock+4>:       stw     r0,8(r1)
(gdb) si
0x70268f94 in HLock ()
2: x/i $pc  0x70268f94 <HLock+8>:       stwu    r1,-64(r1)
(gdb)  |  
 |  
 
            
               | MacsBug | GDB |  
               | Gorcommand-G
 | continueorc
 |  
               | G <addr>
 | jump *<addr>[1]
 |  
               | GG
 | delete[2]
 continue
 |  
               | GTP <addr>
 | tbreak *<addr>continue
 |  
               | Sorcommand-S
 | stepors
 |  
               |  | stepiorsi[3] [4]
 |  
               | SOorTorcommand-T
 | nextorn
 |  
               |  | nextiorni[3] [4]
 |  
               | MRP
 | finish[5]
 |  
               |  | tbreak *$lr[6]continue
 |  
 Notes: 
            Because GDB's primary
            focus is source-level debugging, the arguments to
            commands like jumpare normally a function
            name, or a line number within a source file. If you want
            to use an address, you have to prefix it with '*'. This
            convention is used by a number of commands.deleteremoves all breakpoints. You have
            to confirm this action unless you turn off confirmation
            usingset confirm off.Commands ending with 'i' operate one instruction at a
            time.You should use sifor stepping through
            normal instructions. Only usenifor
            instructions that specifically call a function (typically
            theblinstruction). Usingnifor other instructions can have weird side effects; for
            example, if the current PC is at amflrinstruction,nimistakenly works like a
            magic return [2787251].finishworks properly only if you have
            source for the current routine.This assumes that the return address is still in
            LR. Breakpoints, A-Trap Breaks, and TVector BreaksGDB has comprehensive support for breakpoint. In addition
         to the MacsBug equivalent commands listed below, you also
         have the fb(future break) command which allows
         you to set a breakpoint on a symbol that isn't yet loaded.
         The breakpoint will be set when the code containing the
         symbol is loaded. This is a very useful technique for
         setting a breakpoint in a dynamically loaded plug-in. You can create thread-specific breakpoints. These
         breakpoints only trigger if a particular thread executes the
         specified code. See the section on threads
         later in this technote for an example of how to do this. Another cool GDB feature is that you can enable and
         disable breakpoints without actually deleting them (using
         the enableanddisablecommands).
         This lets you run your program for a time without disrupting
         your breakpoint state. A final cool feature of GDB breakpoints is the ability to
         save breakpoints to a file (using the
         save-breakpointscommand) and restore them
         later (using thesourcecommand). This makes it
         easy to retain your breakpoint state between GDB
         sessions. GDB has no support for A-traps because there's no trap
         dispatcher in Mac OS X. Furthermore, there's no support for
         TVector breaks because TVectors are a CFM construct and GDB
         knows very little about CFM. This isn't a problem though:
         you can use normal breakpoints instead of A-trap and TVector
         breaks. 
            
               | MacsBug | GDB |  
               | BRP <function>
 | b <function>[1]
 |  
               | BRP <addr>
 | b *<addr>[2]
 |  
               | BRP <addr>
                  <condition>
 | b *<addr> if
                  <condition>[3]
 |  
               | BRP <addr> <count>
 | b *<addr>[4]ignore $bpnum <count>
 |  
               | BRP <addr> ;
                  <statement>
 | b *<addr>command $bpnum
 > <statement>
 > end
 |  
               | BRC <addr>
 | clear *<addr>[5]
 |  
               | BRC
 | delete
 |  
               | BRD
 | info break
 |  
 Notes: 
            If you break at the beginning of a function you can
            use tbreak $lrfollowed bycto
            step out of the function.As described in the previous
            section, if you want to set a breakpoint on a
            specific address, you have to use the "*" syntax. For
            example, to break on the address $12345678, you use the
            command b *0x12345678.You can use the conditioncommand to
            change the condition associated with a breakpoint at any
            time.$bpnumis a convenience variable that
            returns the number of the last breakpoint created.
            Convenience variables have no direct analogue in MacsBug.
            They are explained in detail in the GDB
            manual. Convenience variables can be helpful in
            places where you'd use MacsBug'splaymem.It's normally easier to clear a breakpoint by number
            (using delete <x>, where<x>is the breakpoint number) than by
            address. If you can't remember the breakpoint number, theinfo breakcommand prints it for each
            breakpoint. Watch Points
            
               | MacsBug | GDB |  
               | WP
 | not supported [1] |  
               | WPC
 | not supported [1] |  
               | WPD
 | not supported [1] |  
 Notes: 
            GDB does support watchpoints (use apropos
            watchto learn about the commands) but this
            support does not currently work on Mac OS X
            [2762873]. Then again, my experience with
            watchpoints under MacsBug is that they were kind of flaky.
            Until GDB's watchpoint support is fixed you have to find
            random memory corruption the hard way (by applying
            conditional breakpoints to get a rough idea of who's
            responsible and then stepping through code). DisassemblyFor a MacsBug user, GDB's disassemblecommand is hard to get used to: if you only supply one
         argument, it disassembles the entire function containing the
         address specified by the argument. To disassemble something
         smaller than a function you have to use two arguments to
         specify a range of addresses. Alternatively, if you just
         want to disassemble a few instructions, the examine
         (x) command with the instruction format
         modifier (/i) is easier. 
            
               | MacsBug | GDB |  
               | ILP <addr>
 | x/8i <addr>
 |  
               | ILP <addr> <len>
 | disas <addr>
                  <addr>+<len>
 |  
               | IPP PC
 | disas $pc-20 $pc+20
 |  
               |  | x/20i $pc-40
 |  
               | IRP <function>
 | disas <function>
 |  
               | DHP <addr>
 | not supported; you can use playmemto work around this |  
 EmulatorWhat emulator? HeapsThere is no direct support for debugging heap zones in
         GDB. However, you can call heap checking functions in
         various memory management libraries. For example, Listing 7
         shows how to call some of the debug functions in the System
         framework memory library. 
 
            
               |  Listing 7. Using callfor heap debugging 
                     
                        |  Prints the list of heap zones, ala MacsBug "HZ".
(gdb) call (void) malloc_zone_print(0, 0)
Scalable zone 0x5010: inUse=10056(2815KB) small=10027(1227KB) [...]
5 regions:
Region [0x5000-0x45000, 256KB]  In_use=2472
Region [0x1387000-0x13c7000, 256KB]     In_use=4110
Region [0x13f8000-0x1438000, 256KB]     In_use=1647
Region [0x154a000-0x158a000, 256KB]     In_use=1064
Region [0x16a3000-0x16e3000, 256KB]     In_use=734
Free in last zone 26898
Scalable zone 0xf4f010: inUse=645(85KB) small=645(85KB) [...]
1 regions:
Region [0xf4f000-0xf8f000, 256KB]       In_use=645
Free in last zone 158402
 Prints a detailed dump of each zone, ala MacsBug "HD". If you
just want to dump one zone, supply it at the first parameter.
(gdb) call (void) malloc_zone_print(0, 1)
Scalable zone 0x5010: inUse=10056(2815KB) small=10027(1227KB) [...]
5 regions:
Region [0x5000-0x45000, 256KB]  In_use=2472
        Sizes in use: 16[282] 32[1136] 48[344] 64[183] 80[130] [...]
Region [0x1387000-0x13c7000, 256KB]     In_use=4110
        Sizes in use: 16[360] 32[2041] 48[1088] 64[172] 80[228] [...]
Region [0x13f8000-0x1438000, 256KB]     In_use=1647
        Sizes in use: 16[316] 32[590] 48[211] 64[108] 80[75] [...]
Region [0x154a000-0x158a000, 256KB]     In_use=1064
        Sizes in use: 16[281] 32[330] 48[127] 64[65] 80[51] [...]
Region [0x16a3000-0x16e3000, 256KB]     In_use=734
        Sizes in use: 16[46] 32[225] 48[72] 64[26] 80[17] [...]
Free Sizes: >=1024[5] 592[1] 576[1] 400[1] 32[2] 16[8]
Free in last zone 26898
Scalable zone 0xf4f010: inUse=645(85KB) small=645(85KB)  [...]
1 regions:
Region [0xf4f000-0xf8f000, 256KB]       In_use=645
        Sizes in use: 16[1] 32[373] 48[122] 64[47] 80[34] [...]
Free Sizes: >=1024[3] 768[1] 160[1] 128[1] 80[4] 64[2] 16[23]
Free in last zone 158402
 Determines which zone a pointer is in.
(gdb) call (void) malloc_zone_print_ptr_info(0x16a3000)
ptr 0x16a3000 in registered zone 0x5010
 Checks all heap zones (or a specific zone if you supply
it as a parameter), ala MacsBug "HC ALL".
(gdb) call (int) malloc_zone_check(0)
$18 = 1
 Deliberately destroy our heap.
(gdb) call (void) memset(0x16a3000, 0, 256)
 Recheck the heap. Houston control, we have a problem.
(gdb) call (int) malloc_zone_check(0)
*** malloc[988]: invariant broken at region end: ptr=0x16a3010 [...]
*** malloc[988]: Region 4 incorrect szone_check_all() counter=3
*** malloc[988]: error: Check: region incorrect
$19 = 0 |  
 |  
 
            
               | Note:Remember that GDB might not be the right tool for
                  debugging heap problems. Mac OS X has a number of
                  really cool heap debugging tools that are
                  independent of GDB. Check out Inside
                  Mac OS X: Performance for details.
 |  
 
            
               | Note:The zones shown in Listing 7 are not Memory Manager
                  zones but a different concept (with the same name)
                  supported by the System framework memory library.
                  Memory Manager zones are not supported under
                  Carbon.
 |  
 SymbolsGDB, being a source-level debugger, has a gazillion
         commands to control how symbols are handled. Type
         apropos symbolto see them all. Their
         description is beyond the scope of this document. The
         GDB manual has all the gory
         details. Stack
            
               | MacsBug | GDB |  
               | SCorSC6
 | bt[1] [2]
                  [3]
 |  
 Notes: 
            btis short forbacktrace.GDB has no equivalent for SC7 but you rarely need it
            because virtually all Mac OS X code is written in a
            high-level language and thus btworks
            (almost) always.By default btcrawls the current
            thread's stack. There are commands to work with different
            threads within your process. For example, you can usethread apply all btto get a backtrace of
            all threads. See the discussion on threads
            later in this document for more details. MemoryGDB's x(examine) command is the direct
         equivalent of MacsBug'sDMcommand.xworks much likeprintin that
         you can append a '/' and then a format specifier ('o' for
         octal, 'x' for hex, 'd' for decimal, 'u' for unsigned
         decimal, 't' for binary, 'f' for float, 'a' for address, 'c'
         for char) and optionally a size qualifier ('b' for 1 byte,
         'h' for 2 bytes, 'w' for 4 bytes, 'g' for 8 bytes). The x
         command supports two additional format qualifiers that
         aren't supported byprint('s' for string, 'i'
         for instruction). It also supports a repeat counter. Listing
         8 shows some examples of using thexcommand. 
            
               |  Listing 8. Some examples of using
                  the xcommand. 
                     
                        |  Stop at SetMenuItemText.  $r5 is the itemString parameter.
Breakpoint 1, 0x738087cc in SetMenuItemText ()
 Print itemString as a C string.  This usually works pretty well.
(gdb) x/s $r5
0xbffff1e8:      "\005Close"
 Print itemString as a sequence of characters. Note the use of
 'c' as a format qualifier and 5 as a repeat count.
(gdb) x/5c $r5+1
0xbffff1e9:     67 'C'  108 'l' 111 'o' 115 's' 101 'e'
 Print the first byte of itemString as binary.
(gdb) x/tb $r5
0xbffff1e8:     00000101
 And the first half word.
(gdb) x/th $r5
0xbffff1e8:     0000010101000011
 And the first word.
(gdb) x/tw $r5
0xbffff1e8:     00000101010000110110110001101111
 Note how both the format specifier and size qualifier
 are 'sticky'.
(gdb) x/t $r5
0xbffff1e8:     00000101010000110110110001101111
(gdb) x $r5
0xbffff1e8:     00000101010000110110110001101111
 Print the first word as hex.
(gdb) x/xw $r5
0xbffff1e8:     0x05436c6f
 Hitting return on the blank line repeats the previous command.
 Like MacsBug this continues dumping the next memory location.
(gdb)
0xbffff1ec:     0x73650000
 You can use the 'i' format specifier to disassemble.
 The repeat count is especially useful in this case.
(gdb) x/i $pc
0x738087cc <SetMenuItemText+20>:        stw     r3,120(r30)
(gdb) x/4i $pc
0x738087cc <SetMenuItemText+20>:        stw     r3,120(r30)
0x738087d0 <SetMenuItemText+24>:        mr      r0,r4
0x738087d4 <SetMenuItemText+28>:        stw     r5,128(r30)
0x738087d8 <SetMenuItemText+32>:        sth     r0,124(r30) |  
 |  
 
            
               | MacsBug | GDB |  
               | DM <addr>
 | x/8xb <addr>
 |  
               | DMA <addr>
 | x/4c <addr>[1]
 |  
               | DM <addr> <type>
 | p {<type>}<addr>
 |  
               | DM <addr> pstring
 | x/s <addr>[2]
 |  
               | TMP <template>
 | info type <regex>
 |  
               | SB <addr> <val>
 | set *((char *) <addr>) =
                  <val>
 |  
               | SW <addr> <val>
 | set *((short *) <addr>) =
                  <val>
 |  
               | SL <addr> <val>
 | set *((int *) <addr>) =
                  <val>
 |  
 Notes: 
            This is actually a pretty lame substitute for DMA.
            See the MacsBug GDB plug-in for a user-defined command
            that implements this properly.This actually prints a C string, but it works pretty
            well most of the time for pstrings. x/8c
            <addr>is not a good substitute because it's
            hard to read the separate characters as a string.
            Probably the best way print a pstring is with a
            user-defined command. Registers
            
               | MacsBug | GDB |  
               | <register>=<value>
 | set $<register> =
                  <value>
 |  
               | TD
 | info reg
 |  
               | TForTV
 | info all-reg
 |  
 MacrosGDB supports user-defined commands that are much more
         powerful than MacsBug macros. User-defined commands can span
         multiple lines, and can include control flow statements like
         ifandwhile(see the GDB
         manual for details on these). They can't, however, be
         used in expressions, which is sometimes inconvenient. You can use the sourcecommand to run a file
         containing GDB commands as if you'd typed them in at the
         prompt. GDB automatically sources the ".gdbinit" file in
         your home directory and the current directory when you start
         it. This allows you to define persistent user-defined
         commands, much like adding a macro to your MacsBug "Debugger
         Prefs" file. 
            
               | MacsBug | GDB |  
               | MC <name>
                  <expansion>
 | define <name>> <expansion>
 > end
 |  
               | ®1through®9
 | $arg0through$arg9,$argc
 |  
               | MCC
 | no equivalent |  
               | MCD <name>
 | help user-defined[1]
 |  
               |  | show user
 |  
               |  | show user <name>
 |  
 Notes: 
            You can provide help for your user-defined commands
            using the documentcommand. This is
            especially useful if you need help remember how to use
            some complex command you put in your ".gdbinit" file six
            months ago. See the GDB manual
            for details. Miscellaneous
            
               | MacsBug | GDB |  
               | ES
 | kill
 |  
               | WH <addr>
 | p/a <addr>
 |  
               |  | info sym <addr>
 |  
               | FILL <addr> <num> \<val>
 | call (void) memset(<addr>,
                  <val>, <num>)[1]
 |  
               | SHOW
 | you can probably achieve the results you need
                  with display |  
               | DV
 | show version
 |  
               | STOPIF
 | GDB supports real ifandwhilestatements, see the GDB
                  manual |  
               | HELP <command>
 | help <command>
 |  
               |  | apropos <regex>
 |  
               | HELP <template>
 | ptype <type>
 |  
 Notes: 
            This technique is not without its perils. See
            The Dangers of Calling Functions
            for details. DCMDs
            
               | IMPORTANT:This section makes heavy use of GDB's ability to
                  call arbitrary functions within a process. Many of
                  these functions were specifically designed to
                  support debugging. The list of functions changes
                  over time. To see the latest list, search for all
                  functions containing "DebugPrint" or "GDB" (use
 info func DebugPrintandinfo
                  func GDB). |  
 
 
            
               | MacsBug | GDB |  
               | AEDesc
 | call (void)
                  GDBPrintHelpDebuggingAppleEvents()[1]
 |  
               | Evt
 | call (void)
                  GDBPrintEventQueue()
 |  
               | Zap
 | see discussion of MallocScribbleenvironment variable in Inside
                  Mac OS X: Performance |  
               | Leaks
 | see discussion of MallocDebug application and
                  leakscommand line tool in Inside
                  Mac OS X: Performance |  
               | Layers
 | call (void)
                  DebugPrintAllWindowGroups()[2]call (void)
                  DebugPrintWindowGroup(<group>)
 |  
               | THING
 | call (void) GDBComponentList()
 |  
               | FRAGS
 | info target
 |  
               |  | info cfm
 |  
               |  | info dyld
 |  
               |  | info sharedlibrary
 |  
               | ECHOorPRINTF
 | echo
 |  
               |  | output
 |  
               |  | print
 |  
               |  | printf
 |  
               | EBBE
 | not need because $0 through $FFF are not mapped
                  for normal applications |  
               | MLIST
 | call (void) DebugPrintMenuList()[3]call (void) DebugPrintMenu(<menu>)
 call (void) DebugPrintMenuItem(<menu>,
                  <item>)[3]
 |  
               | SCREAM
 | see the discussion on threads
                  later in this document |  
 Notes: 
            This prints out information about how to display
            Apple event descriptors. The instructions vary depending
            on the system version.You can determine <group>by
            looking through the list of groups displayed byDebugPrintAllWindowGroups.You can determine <menu>by
            looking through the list of menus displayed byDebugPrintMenuList. Other Cool Stuff
            
               | MacsBug | GDB |  
               | WINDLIST
 | call (void) DebugPrintWindowList()call (void)
                  DebugPrintPlatformWindowList()
 |  
               | DM <port> GrafPort
 | call (void)
                  QDDebugPrintPortInfo(<port>)[1]call (void)
                  QDDebugPrintCGSInfo(<port>)
 |  
               | DM <window> WindowRecord
 | call (void)
                  DebugPrintWindow(<window>)
 |  
               | DM <control>^
                  ControlRecord
 | call (void)
                  GDBShowControlInfo(<control>)
 |  
               |  | call (void)
                  GDBShowControlHierarchy(<window>)
 |  
               | PLAYMEM
 | see below |  
               | ©<func>(<param>...)
 | call (<resulttype>)
                  <func>(<param>...)
 |  
 Notes: 
            You can determine the current port by calling
            GetQDGlobalsThePort. Back to top 
 Hints and TipsThis section contains miscellaneous hints and tips for
         using GDB as an assembly-level debugger. Seeing stdout and stderr After
         AttachingIf you attach GDB to a process
         (as opposed to starting the process from within GDB), you
         won't be able to see anything that the process prints to
         stdoutorstderr. Programs
         launched by the Finder typically havestdoutandstderrconnected to "/dev/console", so the
         information they print goes to the console. You can view
         this by launching the Console application (in the Utilities
         folder), however, it's inconvenient to have to look in a
         separate window. Another alternative is to connect the
         process'sstdoutorstderrto the
         terminal device for GDB's Terminal window. Listing 9 shows
         how to do this. 
            
               |  Listing 9. Connecting
                  stdoutandstderrto
                  GDB's terminal device. 
                     
                        | (gdb) attach 795
[... output omitted ...]
(gdb) call (void) DebugPrintMenuList()
 No output )-:
 Close the stdout and stderr file descriptors.
(gdb) call (void) close(1)
(gdb) call (void) close(2)
 Determine the name of the terminal device for GDB itself.
(gdb) shell tty
/dev/ttyp1
 Reopen stdout and stderr, but connected to GDB's terminal.
 The function results should be 1 and 2; if not, something
 is horribly wrong.
(gdb) call (int) open("/dev/ttyp1", 2, 0)
$1 = 1
(gdb) call (int) open("/dev/ttyp1", 2, 0)
$2 = 2
 Try the DebugPrintMenuList again.
(gdb) call (void) DebugPrintMenuList()
 Yay output!
Index MenuRef     ID  Title
----- ---------- ---- -----
<regular menus>
00001 0x767725D3 -21629 Ed
00002 0x76772627 1128 <Apple>
00003 0x767726CF 1129 File
00004 0x76772567 1130 Edit
[... remaining output omitted ...] |  
 |  
 Remote DebuggingOne of the nice features of GDB is that you can debug
         your application across any TCP/IP network (including the
         public Internet). You do this by logging into the remote
         machine, running GDB on that machine, and then attaching to
         the application you want to debug. This has two key
         advantages. 
            You can debug programs at remote sites; it no longer
            matters if you can't reproduce the problem in your
            office!You can debug things that are disrupted by the
            debugger's user interface. Two interesting examples of
            this are application suspend/resuming handling and Drag
            Manager callbacks. Remote debugging has some caveats of which you should be
         aware. Firstly, you must enable remote login on the remote
         machine. The easiest way to do this is to check the Allow
         Remote Login checkbox in the Sharing panel of System
         Preferences. You can then log in to that machine by
         launching Terminal on your local machine and using the
         sshcommand line program to log into the remote
         machine. Obviously you have to have the user name and
         password of an account on the remote machine. Once you have
         logged into the remote machine you can use GDB in much the
         same way as you would normally. Listing 10 shows an example
         of this. 
            
               | Note:Mac OS X 10.0 did not support
 ssh; you
                  must usetelnetinstead. For more
                  information on these commands, typeman
                  sshandman telnetat the
                  command line. |  
 
            
               |  Listing 10. Using sshto log in to a remote machine. 
                     
                        | [localhost:~] quinn% ssh puppy.apple.com
quinn@puppy.apple.com's password:
Welcome to Darwin!
puppy> gdb
GNU gdb 5.0-20001113 (Apple version gdb-200) (Mon Sep  3 02:43:52 GMT 2001) (UI_OUT)
Copyright 2000 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type "show copying" to see the conditions.
There is absolutely no warranty for GDB.  Type "show warranty" for details.
This GDB was configured as "powerpc-apple-macos10".
(gdb)  |  
 |  
 Don't run a GUI application from within GDB when logged
         in remotely. A GUI application needs to connect to system
         services which aren't available if you run it from a remote
         login session. If you need to debug a GUI application, have
         the remote user launch the application from the Finder, then
         start GDB in your sshsession and attach
         to the running application. DebugStrMac OS X supports the standard DebugStrandDebuggersystem calls. However, their default
         behavior is to just print a message tostderrand continue. You must take special steps if you want these
         calls to stop program execution. Specifically, these
         routines only stop in the debugger if you have set theUSERBREAKenvironment variable to 1. You can
         set this environment variable in a number of ways. 
            Command line -- Type the following shell command
            before running GDB: setenv USERBREAK 1. You
            must run your application
            from within GDB for this to be effective.Within GDB -- Enter the following GDB command before
            running your application: set env USERBREAK
            1. You must run your
            application from within GDB for this to be
            effective.Programmatically -- You can set the
            USERBREAKenvironment variable from within
            your own program using the following C statement:setenv("USERBREAK", "1", true). You must do
            this before any other part of your program callsDebugStrorDebugger. Also,
            this only works if you call the standard C librarysetenvroutine (in System framework); for
            MSL CFM applications this means you should call the
            Mach-O version of this function via CFBundle. Finally, the underlying mechanism used to stop your
         program in the debugger when you call DebugStris theraisesystem call (part of System
         framework). You can explicitly cause your program to stop
         regardless of the setting ofUSERBREAKby
         callingraise(SIGINT). The Dangers of Calling
         FunctionsGDB, like MacsBug, lets you call functions within the
         application you're debugging. Also like MacsBug, this
         technique has its dangers. You should be aware of the
         following things. 
            The function executes within the context of your
            program. If your program has crashed, there's no
            guarantee that it will be in a fit state to execute the
            function.You have to be very careful if you call a function
            that has intricate dependencies. For example, if your
            program has a mutex that protects a critical data
            structure and you break into GDB while that mutex is
            held, don't try to call a function that needs to acquire
            the mutex.The function you call executes in the context of the
            current thread (the thread displayed by info
            thread). Other threads are not blocked while the
            function executes. You can block them explicitly using
            thethread suspendandthread
            resumecommands, but this may cause more problems
            than it solves.You should be especially careful calling functions
            that are not preemptive safe. For example, calling
            NewWindowwould be a mistake because the
            call will execute in the context of a preemptive thread
            and will access data structures that aren't preemptive
            safe. MacsBug GDB Plug-inNo technote on GDB for MacsBug users would be complete
         without mentioning the MacsBug GDB plug-in. This plug-in
         extends GDB to support a subset of the MacsBug commands and
         a MacsBug-like user interface. The plug-in is installed as
         part of the Mac OS X 10.1 developer tools in the
         "/usr/libexec/gdb/plugins/MacsBug/" directory. You can learn
         more about the plug-in from the documentation in that
         directory. To activate the plug-in, take the following steps. 
            Start GDB from the command line.Enter the command source
            /usr/libexec/gdb/plugins/MacsBug/gdbinit-MacsBug.
            You will find that most MacsBug commands are available
            (in addition to the standard GDB commands). Typehelp MacsBugfor more online help
            on these commands.To start the user interface, enter the command
            mb on. The user interface will display "Not
            Running" until you attach to the application you wish to
            debug. Use normal GDB commands to attach. 
            
               | IMPORTANT:The MacsBug-like user interface requires a terminal
                  window of at least 44 rows and 80 columns. You may
                  need to resize your Terminal window for the user
                  interface to start. This and other terminal
                  considerations are discussed in the plug-in's
                  documentation.
 |  
 
            
               | Note:If you want to always activate the plug-in, just
                  add the above commands to the ".gdbinit" file in
                  your home directory.
 |  
 The plug-in is especially useful when doing assembly
         language debugger because it maintains a lot of useful state
         on the screen (the current registers, a disassembly of the
         instructions around the current PC, and so on). PlaymemOne nice feature of MacsBug is that it allocates a small
         region of memory (512 bytes) and sets the symbol
         playmemto point to it. You can use this memory
         for all sorts of nasty debugging hacks. GDB has no dedicatedplaymem, but you can easily create your own, as
         shown in Listing 11. These commands create a 512 byte block
         of zeroed memory and set the convenience variable$playmemto point to it. 
            
               |  Listing 11. Creating
                  playmem. 
                     
                        | (gdb) call (void *) calloc(1, 512)
$1 = (void *) 0x1ed11b0
(gdb) set $playmem = $
(gdb) p/a $playmem
$2 = 0x1ed11b0 |  
 |  
 Once you have a block of play memory you can use it for a
         variety of tricky things. For example, you can use it to
         simulate the MacsBug DHPcommand, as shown in
         Listing 11. 
            
               |  Listing 12. Simulating MacsBug's
                  DHPcommand usingplaymem. 
                     
                        |  Create a mydhp user-defined command.
(gdb) define mydhp
Type commands for definition of "mydhp".
End with a line saying just "end".
>set *(int *)$playmem = $arg0
>x/i $playmem
>end
 Use the newly created command.
(gdb) mydhp 0x4e800020
0x1ed11b0:      blr |  
 |  
 This is just one example of the use of convenience
         variables. You can do many other cools things with them. To
         create a convenience variable, just name it (don't forget to
         prefix it with $) on the left of a set expression (as shown
         in Listing 11). To see the value of a convenience variable,
         just printit. To see all convenience
         variables, use theshow conveniencecommand.
         You can learn more about convenience variables in the
         GDB manual. ThreadsGDB provides a number of commands to support debugging
         threads. Listing 13 shows an example of their use. 
            
               |  Listing 13. Thread debugging
                  commands. 
                     
                        |  Show information about the current thread.
(gdb) info thread
Thread 0x1903 has current state "WAITING"
Thread 0x1903 has a suspend count of 0.
 Show a list of all threads.
(gdb) info threads
  3 process 665 thread 0x1b03  0x700009a8 in mach_msg_overwrite_trap ()
  2 process 665 thread 0x1a03  0x70059d58 in semaphore_wait_signal_trap ()
* 1 process 665 thread 0x1903  0x700009a8 in mach_msg_overwrite_trap ()
 Get information about a specific thread.
(gdb) info thread 3
Thread 0x1b03 has current state "WAITING"
Thread 0x1b03 has a suspend count of 1.
 Make another thread the current thread
 and then backtrace that thread, and then
 switch back to the previous thread.
(gdb) thread 3
[Switching to thread 3 (process 665 thread 0x1b03)]
#0  0x700009a8 in mach_msg_overwrite_trap ()
(gdb) bt
#0  0x700009a8 in mach_msg_overwrite_trap ()
#1  0x700058d4 in mach_msg_overwrite ()
#2  0x700279a0 in thread_suspend ()
#3  0x70027934 in _pthread_become_available ()
#4  0x70027658 in pthread_exit ()
#5  0x700150f8 in _pthread_body ()
#6  0x00000000 in ?? ()
(gdb) thread 1
[Switching to thread 1 (process 665 thread 0x1903)]
#0  0x700009a8 in mach_msg_overwrite_trap ()
 Apply a command to all threads. In this example,
 we "backtrace" every thread.
(gdb) thread apply all bt
Thread 3 (process 665 thread 0x1b03):
#0  0x700009a8 in mach_msg_overwrite_trap ()
#1  0x700058d4 in mach_msg_overwrite ()
#2  0x700279a0 in thread_suspend ()
[... remaining output omitted ...]
Thread 2 (process 665 thread 0x1a03):
#0  0x70059d58 in semaphore_wait_signal_trap ()
#1  0x70016300 in semaphore_wait_signal ()
#2  0x70016168 in _pthread_cond_wait ()
[... remaining output omitted ...]
Thread 1 (process 665 thread 0x1903):
#0  0x700009a8 in mach_msg_overwrite_trap ()
#1  0x70006e34 in mach_msg ()
#2  0x7017ab20 in __CFRunLoopRun ()
[... remaining output omitted ...]
                            Create a thread-specific breakpoint.
(gdb) break HandToHand thread 1
Breakpoint 1 at 0x7025e844 |  
 |  
 Back to top 
 SummaryGDB is your friend. Learn to love GDB. Soon you will
         forget your infatuation with MacsBug. Back to top 
 ReferencesRichard Stallman et al, Debugging
         with GDB, Free Software Foundation, March 2000 Apple Computer, MacsBug
         Reference and Debugging Guide, Addison-Wesley, 1990 Inside
         Mac OS X: Performance projectbuilder-users
         Mailing List -- The Apple GDB team hang out on this
         mailing list. Back to top 
 Downloadables
            
               |   | Acrobat version of this Note (120K) | Download |  
 
 Back to top |