Movatterモバイル変換


[0]ホーム

URL:


mikeash.com: just this guy, you know?
Home
Book
The Complete Friday Q&A, advanced topics in Mac OS X and iOS programming.
Blog
GitHub
My GitHub page, containing various open-source libraries for Mac and iOS development, and some miscellaneous projects
Glider Flying
HD Ridge Running Video
List of Landouts
Day in the Life
Skyline Soaring Club
Soaring Society of America
Getting Answers
Ten simple points to follow to get good answers on IRC, mailing lists, and other places
Miscellaneous Pages
Miscellaneous old, rarely-updated content
mike@mikeash.com
E-mail me

Posted at 2011-06-17 19:49 |RSS feed (Full text feed) |Blog Index
Next article:Friday Q&A Delayed Again
Previous article:Friday Q&A 2011-06-03: Objective-C Blocks vs. C++0x Lambdas: Fight!
Tags:debuggingfridayqnagdb
Friday Q&A 2011-06-17: gdb Tips and Tricks
byMike Ash  

It has been said that programming is simply the art of debugging an empty file. While this is a bit of a narrow minded view of things, the fact is that debugging is hugely important to any serious programming. There are a lot of different ways to debug programs, ranging from writing logs to blinking a light to simply inspecting the contents of memory on a crashed system. However, the debugger is one of the most powerful tools for debugging, as you might expect, since it allows deep inspection and modification of a running program without having to anticipate your debugging needs in advance. Althoughlldb, the new debugger from the LLVM project, is quickly gaining functionality, the gold standard for debugging Cocoa apps is stillgdb. Today I'm going to discuss how to usegdb and various tips and tricks for getting the most out of it, a topic suggested by Luis de la Rosa.

Getting Started
Most Cocoa programmers accessgdb though Xcode's debugger, which is really just a graphical wrapper aroundgdb. While this is useful, it can also get in the way, and it fails to expose a lot of useful capabilities. Because of this, I'm going to cover the use ofgdb directly. Virtually everything I discuss can be used at the(gdb) prompt in Xcode's debugger console as well, but by learning to usegdb on its own, you'll have a better idea of just what's happening behind the scenes. It can also be inconvenient to use the Xcode debugger in some circumstances, and in those cases it's good to be fluent in usinggdb directly.

First, let's create an example program to debug:

#import<Foundation/Foundation.h>intmain(intargc,char**argv){NSAutoreleasePool*pool=[[NSAutoreleasePoolalloc]init];NSLog(@"Hello, world!");[poolrelease];return0;}

We'll compile it with the-g flag to tell the compiler to emit debug symbols:

gcc-g-frameworkFoundationtest.m

To run it in the debugger, simply invokegdb with the binary as the parameter, which in this case isa.out:

gdba.out

A bunch of legal mumbo jumbo gets dumped, but eventually we are presented with a prompt:

(gdb)

The debugger is loaded as well as the program we just built. Note, however, that the program has not actually been executed yet. The debugger stops here to give us a chance to set things up before starting the program to be debugged. Here, there's no setup to do, so we'll just run it:

(gdb)runStartingprogram:/Users/mikeash/shell/a.outReadingsymbolsforsharedlibraries.++++.......................done2011-06-1620:28:53.658a.out[2946:a0f]Hello,world!Programexitednormally.(gdb)

Let's insert a bug into the program and try it again. Without repeating the entire thing, here are the pertinent bits:

intx=42;NSLog("Hello, world! x = %@",x);

And now we get a nice crash when running in the debugger:

(gdb)runStartingprogram:/Users/mikeash/shell/a.outReadingsymbolsforsharedlibraries.++++.......................doneProgramreceivedsignalEXC_BAD_ACCESS,Couldnotaccessmemory.Reason:13ataddress:0x00000000000000000x00007fff84f1011cinobjc_msgSend()(gdb)

Rather than just tossing us back to the shell as would happen if we ran the program directly,gdb halts the program but leaves it in memory, allowing us to inspect it. The first thing we want to do is see where the program crashed. From the initial output fromgdb, we can see that it crashed inobjc_msgSend_fixup, but that doesn't tell us where it's crashing inour code. To find this out, we want to get a listing of the call stack of the current thread. This can be done with thebacktrace command, or simply abbreviated asbt:

(gdb)bt#00x00007fff84f1011cinobjc_msgSend()#10x00007fff864ff30bin_CFStringAppendFormatAndArgumentsAux()#20x00007fff864ff27din_CFStringCreateWithFormatAndArgumentsAux()#30x00007fff8657e9efin_CFLogvEx()#40x00007fff87beab3einNSLogv()#50x00007fff87beaad6inNSLog()#60x0000000100000ed7inmain()attest.m:10

Now we can see that it's crashing in the call toNSLog. Let's see exactly where the call is. Although this is obvious in such a small program, this can be handy in bigger ones. First we need to jump up to the stack frame which contains our code, in this case#6. We'll do this using theup command, which allows us to move between stack frames:

(gdb)up6#60x0000000100000ed7inmain()attest.m:109NSLog("Hello, world! x = %@",x);

This not only shows us the stack frame, but also the line of code in question. If we wanted to go back down the stack from here, thedown command will do this. To get more context, we can use thelist command, abbreviated asl:

(gdb)l56intmain(intargc,char**argv)7{8NSAutoreleasePool*pool=[[NSAutoreleasePoolalloc]init];9intx=42;10NSLog("Hello, world! x = %@",x);11[poolrelease];1213return0;14}

Of course, we can see all of the code by simply openingtest.m in a text editor and looking at line 9, but it's sometimes convenient to inspect the code directly from the debugger.

At this point, now that we see the line in question, it should be obvious that we're missing a@ from the beginning of the format string. Let's fix that and re-run the program.

(gdb)runStartingprogram:/Users/mikeash/shell/a.outReadingsymbolsforsharedlibraries.++++.......................doneProgramreceivedsignalEXC_BAD_ACCESS,Couldnotaccessmemory.Reason:KERN_INVALID_ADDRESSataddress:0x000000000000002a0x00007fff84f102b3inobjc_msgSend_fixup()(gdb)bt#00x00007fff84f102b3inobjc_msgSend_fixup()#10x0000000000000000in??()

Unfortunately, it still crashes. Worse, the stack got screwed up and we can't even see whereobjc_msgSend_fixup is being called from. To figure out which line of code is at fault, let's set a breakpoint so that the debugger will stop on the first line of code. This is done using thebreak command, abbreviated asb. It can take a symbol or afile:line combination:

(gdb)btest.m:8Breakpoint1at0x100000e8f:filetest.m,line8.

The debugger tells us that the breakpoint is set. This is breakpoint 1, which is useful in order to disable, enable, or delete the breakpoint later (using, surprise, thedisable,enable, anddelete commands). Now we can run the program again:

(gdb)runTheprogrambeingdebuggedhasbeenstartedalready.Startitfromthebeginning?(yorn)yStartingprogram:/Users/mikeash/shell/a.outBreakpoint1,main(argc=1,argv=0x7fff5fbff628)attest.m:88NSAutoreleasePool*pool=[[NSAutoreleasePoolalloc]init];

The debugger is now stopped on the line of code in question. This lets us take control before the crash actually occurs. Now, we'll step through the code line by line to see which line is crashing. Thenext command, abbreviated asn, will do this:

(gdb)n9intx=42;(gdb)10NSLog(@"Hello, world! x = %@",x);(gdb)ProgramreceivedsignalEXC_BAD_ACCESS,Couldnotaccessmemory.Reason:KERN_INVALID_ADDRESSataddress:0x000000000000002a0x00007fff84f102b3inobjc_msgSend_fixup()

Note that simply hitting return will tell the debugger to repeat the last command, so the above doesnext three times. Now we can see that the crash is once again on theNSLog, even though we can't get a good backtrace. This is hopefully enough for us to realize that this second crash is due to using%@ instead of%d to print anint. The address that it crashed on,0x000000000000002a, is 42 in hexadecimal, and comes from treatingx like a pointer.

Printing Values
Printing the value of variables or expressions is extremely useful for debugging. This can be done using theprint command, abbreviated asp:

(gdb)px$1=42

The format of the output can be controlled by using/format after thep command, whereformat is a format letter known togdb. Useful format letters includex for hexadecimal,c for char, anda for address:

(gdb)p/xx$2=0x2a

Thepo command, short forprint-object will print the description of an Objective-C object. It works by sending thedebugDescription message to the object, which is usually equivalent to the more familiardescription message. For example, we can check out that lovely autorelease pool:

(gdb)popool<NSAutoreleasePool:0x10010e820>

These commands can also take complete expressions. For example, let's check out the method signature ofNSObject's-debugDescription method:

(gdb)po[NSObjectinstanceMethodSignatureForSelector:@selector(debugDescription)]<NSMethodSignature:0x10010f320>numberofarguments=2framesize=224isspecialstructreturn?NOreturnvalue:--------------------------------typeencoding(@)'@'flags{isObject}modifiers{}frame{offset=0,offsetadjust=0,size=8,sizeadjust=0}memory{offset=0,size=8}argument0:--------------------------------typeencoding(@)'@'flags{isObject}modifiers{}frame{offset=0,offsetadjust=0,size=8,sizeadjust=0}memory{offset=0,size=8}argument1:--------------------------------typeencoding(:)':'flags{}modifiers{}frame{offset=8,offsetadjust=0,size=8,sizeadjust=0}memory{offset=0,size=8}

When using class methods like this, the debugger occasionally becomes obstinate and refuses to recognize class names. To work around this, useNSClassFromString to obtain the class in the expression:

(gdb)po[NSClassFromString(@"NSObject")instanceMethodSignatureForSelector:@selector(debugDescription)]

We can also print the result of messages which return primitive values. However,gdb is not smart enough to figure out the return type in this case, so we have to tell it by adding a cast to the expression:

(gdb)p[NSObjectinstancesRespondToSelector:@selector(doesNotExist)]Unabletocallfunction"objc_msgSend"at0x7fff84f100f4:noreturntypeinformationavailable.Tocallthisfunctionanyway,youcancastthereturntypeexplicitly(e.g.'print(float)fabs(3.0)')(gdb)p(char)[NSObjectinstancesRespondToSelector:@selector(doesNotExist)]$5=0'\000'

gdb also isn't always smart about Cocoa-specific typedefs likeBOOL, so in this case I've used the equivalentchar. While you want to call things by their proper names when writing code, when debugging it's usually perfectly fine to play a little fast and loose with things like type names, as long as they're equivalent on the architecture you're currently debugging.

You'll notice that when usingp, the expression is printed with something like$1 = in front of it. These are actuallygdb variables, and they can be used to refer to the value of that expression later. For example, we canmalloc some memory, zero it out, then free it without having to manually copy/paste the address each time:

(gdb)p(int*)malloc(4)$6=(int*)0x100105ab0(gdb)p(void)bzero($6,4)$7=void(gdb)p*$6$8=0(gdb)p(void)free($6)$9=void

Note that, as with methods, we usually have to explicitly informgdb of the return type of functions we tell it to call. The same technique can be used with objects, although unfortunately thepo command doesn't save its return value to a variable, so we have to first do ap and then follow it with apo of the new variable:

(gdb)p(void*)[[NSObjectalloc]init]$10=(void*)0x100105950(gdb)po$10<NSObject:0x100105950>(gdb)p(long)[$10retainCount]$11=1(gdb)p(void)[$10release]$12=void

Inspecting Memory
Sometimes, printing a single value isn't enough. Instead, we want to print out the contents of a whole bunch of memory at once. The oddly namedx command can do this for us.

Thex command looks like this:x/format address. The address is an expression which evaluates to the address of the memory you want to inspect. The format gets a little trickier, because the syntax is extremely dense. The format consists of three components. The first component is the number of items to print, as a plain text integer. The second component is a format letter. The full list can be found by typinghelp x at the(gdb) prompt, but useful ones includex for hexadecimal,d for decimal, andc for char. The last component is a size letter. The size letter is one ofb,h,w, org, which correspond to 1, 2, 4, and 8 bytes, respectively. For example, to print eight four-byte hex components atptr:

(gdb)x/4xwptr

More concretely, let's inspect the contents of the NSObject class:

(gdb)x/4xg(void*)[NSObjectclass]0x7fff70adb468<OBJC_CLASS_$_NSObject>:0x00007fff70adb4400x00000000000000000x7fff70adb478<OBJC_CLASS_$_NSObject+16>:0x0000000100105ac00x0000000100104ac0

I won't get into the details of interpreting all of these values, but you can see that it's easy to get a raw dump of the contents. Let's try another one, this time taking a look at what anNSObject instance looks like:

(gdb)x/1xg(void*)[NSObjectnew]0x100105ba0:0x00007fff70adb468

Notice how the beginning of this object contains the same value as the location of theNSObject class shown just above.

Using thei format, forinstruction, we can even dump a disassembly of a particular location in memory:

(gdb)x/5iwmain0x100000e50<main>:push%rbp0x100000e51<main+1>:mov%rsp,%rbp0x100000e54<main+4>:sub$0x20,%rsp0x100000e58<main+8>:mov%edi,-0x14(%rbp)0x100000e5b<main+11>:mov%rsi,-0x20(%rbp)

There is also a dedicateddisas command, which produces largely the same output.

Setting Variables
Sometimes it's useful not only to inspect data, but actually alter it. To sent the contents of a variable, simply use theset command:

(gdb)setx=43

An arbitrary expression can be used for the new value. To assign a variable to a new object, you can do:

(gdb)setobj=(void*)[[NSObjectalloc]init]

This can be handy to create places in your program where you want it to wait for you to manipulate it with the debugger for some reason. Write what appears to be an infinite loop, checking a variable which never changes:

NSLog(@"Waiting for debugger....");volatileintdebugged=0;while(!debugged)sleep(1);

Once it reaches this loop, you can hop into the debugger and make the program continue by changing the variable:

(gdb)setdebugged=1(gdb)continue

Note that it's necessary to declare the variable asvolatile to ensure that the compiler doesn't simply optimize away the check altogether.

Breakpoints
A breakpoint is a location in the program where, if execution reaches that location, the program is stopped and the debugger activates. As mentioned previously, a breakpoint can be made with thebreak command, abbreviated asb. There are several forms which can be used to indicate the target of a breakpoint:

Breakpoints can be toggled on and off usingenable anddisable on the breakpoint number. If you want to remove one completely, usedelete. To get a list of all existing breakpoints, use theinfo breakpoints command, which can be abbreviated asinfo b or even justi b.

Breakpoints can be made conditional by addingif followed by an expression after the breakpoint target. In this case, the debugger only pauses execution of the program when the condition evaluates to true. For example, to break on a method only if the parameter is equal to 5:

(gdb)b-[ClassmyMethod:]ifparameter==5

Finally, breakpoints can have commands added to them. To do this, typecommands followed by the breakpoint number. If you want to add commands to a breakpoint you just set, you don't need to provide the number. Breakpoint commands are just a list of standardgdb commands terminated byend. This is useful if you know you always want to do something whenever a particular breakpoint is hit. For example, let's say you wanted a backtrace from everyNSLog call:

(gdb)bNSLogBreakpoint4at0x7fff87beaa62(gdb)commandsTypecommandsforwhenbreakpoint4ishit,oneperline.Endwithalinesayingjust"end".>bt>end

Now, wheneverNSLog is hit, the debugger will automatically print a backtrace before pausing the program and showing the(gdb) prompt.

Sometimes you simply want the debugger to print some information but not pause the program. This effectively allows you to add logging statements on the fly. To do this, simply putcontinue at the end of your breakpoint commands, and execution will immediately resume. Here's an example of setting a breakpoint which dumps a backtrace, prints a parameter, and then continues:

(gdb)b-[ClassmyMethod:]Breakpoint5at0x7fff864f1404(gdb)commandsTypecommandsforwhenbreakpoint5ishit,oneperline.Endwithalinesayingjust"end".>bt>pparameter>continue>end

One final useful tidbit is thereturn command, which causes the currently executing function to return control to its caller. If a parameter is given, that value is returned. This can be used to disable functions or methods altogether. For example, to turn offNSLog:

(gdb)commandsTypecommandsforwhenbreakpoint6ishit,oneperline.Endwithalinesayingjust"end".>return>continue>end

While these tricks are extremely useful, it's important to note that any timegdb gets involved with the target program, performance takes a huge hit. A trick like the above will disableNSLog, but it will also make each call to NSLog take significantly longer, because each call has to break into the debugger, the debugger has to execute the commands given, and every action involves a bunch of tricky cross-process manipulation. Usually this is fine, but you generally wouldn't want to do something like put a conditional breakpoint onobjc_msgSend, as it will cause your app to take forever to do anything.

Function and Method Arguments Without Source
Sometimes you end up in code where you don't have the source. Frequently you end up with a crash or other misbehavior occurring deep in Cocoa, and you need to figure out what's going wrong. One of the most useful things you can do at this point is to inspect the arguments to the function or method in question to try to figure out what's going on.

Clark Cox hasan excellent reference on how parameters are represented at the machine level on various architectures. It doesn't cover ARM (iOS devices), but fortunately ARM is easy: parameters are passed in order in registers$r0,$r1,$r2, and$r3. Note that for all of the architectures which pass parameters in registers (all of them besidesi386), these can only be reliably used at the very beginning of the function, as it's likely that the registers in question will be reused for other data as the function proceeds.

As an example, we can print the argument passed toNSLog:

Breakpoint2,0x00007fff87beaa62inNSLog()(gdb)po$rdiHello,world!

This can also be extremely useful when using breakpoint conditions. As an example, let's say that a mysterious call toNSLog is resulting in a crash, but it's impractical to put a general breakpoint there because it's called too frequently. However, we can write a breakpoint condition based on the content of the log:

(gdb)breakNSLogif(char)[$rdihasPrefix:@"crashing"]

When doing this sort of thing, keep in mind that the first two parameters to methods are taken up byself and_cmd, so your explicit parameters will start out at$rdx (on x86_64) or$r2 (on ARM).

Debugging Exceptions
Exceptions are thrown in Objective-C with the runtime functionobjc_exception_throw. It's extremely useful to keep a breakpoint on this function, since exceptions are usually rare in Objective-C and when they do happen, it's often the first sign of something going seriously wrong. Without a breakpoint here, you'll generally only find out about the exception after it's been caught, so you have no information about where it was thrown.

With a breakpoint set, you'll stop as the exception is being thrown, which gives you the opportunity to see the full stack trace of what's throwing it, and inspect your program to figure out why. However, the breakpoint stopsbefore any information about the exception is logged. Fortunately, this is easy to work around. The exception being thrown is the first (and only) argument toobjc_exception_throw, so it can be printed using the techniques discussed above for accessing the arguments of a function without that function's source code.

Threads
In this modern age, we work with a lot of multithreaded code, and it's important to be able to deal with that in the debugger. Here's some quick GCD code that spins off a few background threads and just sits:

dispatch_apply(3,dispatch_get_global_queue(0,0),^(size_tx){sleep(100);});

I'll run this in the debugger, then use control-C to stop execution while it's sleeping:

(gdb)runStartingprogram:/Users/mikeash/shell/a.outReadingsymbolsforsharedlibraries.+++........................done^CProgramreceivedsignalSIGINT,Interrupt.0x00007fff88c6ff8ain__semwait_signal()(gdb)bt#00x00007fff88c6ff8ain__semwait_signal()#10x00007fff88c6fe19innanosleep()#20x00007fff88cbcdf0insleep()#30x0000000100000ea7in__main_block_invoke_1(.block_descriptor=0x1000010a0,x=0)attest.m:12#40x00007fff88cbbbc8in_dispatch_apply2()#50x00007fff88cb31e5indispatch_apply_f()#60x0000000100000e6ainmain(argc=1,argv=0x7fff5fbff628)attest.m:11

This is pretty much what we'd expect. Notice howdispatch_apply is smart enough to borrow the main thread to execute one of the blocks. But how about the other two blocks, which are running on a background thread somewhere? We can get a list of all threads with theinfo threads command:

(gdb)infothreads3"com.apple.root.default-priorit"0x00007fff88c6ff8ain__semwait_signal()2"com.apple.root.default-priorit"0x00007fff88c6ff8ain__semwait_signal()*1"com.apple.root.default-priorit"0x00007fff88c6ff8ain__semwait_signal()

The* next to thread 1 indicates that this is the one we're currently on. Let's see what thread 2 is up to. We can switch withthread 2, then get a backtrace from there:

(gdb)thread2[Switchingtothread2(process4794),"com.apple.root.default-priority"]0x00007fff88c6ff8ain__semwait_signal()(gdb)bt#00x00007fff88c6ff8ain__semwait_signal()#10x00007fff88c6fe19innanosleep()#20x00007fff88cbcdf0insleep()#30x0000000100000ea7in__main_block_invoke_1(.block_descriptor=0x1000010a0,x=1)attest.m:12#40x00007fff88cbbbc8in_dispatch_apply2()#50x00007fff88c4f7f1in_dispatch_worker_thread2()#60x00007fff88c4f128in_pthread_wqthread()#70x00007fff88c4efc5instart_wqthread()

There we can see that this second block is running on a dispatch worker thread being managed by thepthread_wqthread system. This is useful, but a bit tedious. We only have three threads, but what if we had 300? We can apply a command to all threads usingthread apply all. To get a backtrace of each thread, we can dothread apply all backtrace, or as an amusingly dense shortcut,t a a bt:

(gdb)taabtThread3(process4794):#00x00007fff88c6ff8ain__semwait_signal()#10x00007fff88c6fe19innanosleep()#20x00007fff88cbcdf0insleep()#30x0000000100000ea7in__main_block_invoke_1(.block_descriptor=0x1000010a0,x=2)attest.m:12#40x00007fff88cbbbc8in_dispatch_apply2()#50x00007fff88c4f7f1in_dispatch_worker_thread2()#60x00007fff88c4f128in_pthread_wqthread()#70x00007fff88c4efc5instart_wqthread()Thread2(process4794):#00x00007fff88c6ff8ain__semwait_signal()#10x00007fff88c6fe19innanosleep()#20x00007fff88cbcdf0insleep()#30x0000000100000ea7in__main_block_invoke_1(.block_descriptor=0x1000010a0,x=1)attest.m:12#40x00007fff88cbbbc8in_dispatch_apply2()#50x00007fff88c4f7f1in_dispatch_worker_thread2()#60x00007fff88c4f128in_pthread_wqthread()#70x00007fff88c4efc5instart_wqthread()Thread1(process4794):#00x00007fff88c6ff8ain__semwait_signal()#10x00007fff88c6fe19innanosleep()#20x00007fff88cbcdf0insleep()#30x0000000100000ea7in__main_block_invoke_1(.block_descriptor=0x1000010a0,x=0)attest.m:12#40x00007fff88cbbbc8in_dispatch_apply2()#50x00007fff88cb31e5indispatch_apply_f()#60x0000000100000e6ainmain(argc=1,argv=0x7fff5fbff628)attest.m:11

Here we can easily see the entire program's thread state at a glance. If we want to inspect a particular thread more thoroughly, we can easily switch to a given thread using thethread command, then use other debugger commands as usual.

Attaching to a Running Process
The debugger is normally used by having it start the target program, but sometimes that isn't practical. Maybe your program started misbehaving in the middle of a run where you didn't use the debugger, or maybe you need to investigate a situation where the program is already being started by something else, likelaunchd, and it's inconvenient to get the debugger involved in the start.

In this scenario, it's handy to be able to attach the debugger to a process which is already running. This can be done by tellinggdb to attach to a particular pid, either when startinggdb from the command line:

$gdbattach12345

Or from thegdb command prompt after starting it separately:

(gdb)attach12345

Once attached, everything behaves pretty much like it would if you had started the program directly from the debugger. Once exception to this is that if you quit the debugger, it will simply detach the program rather than killing it as it would normally do.

Command Line Arguments and Environment Variables
Providing command line arguments and environment variables is easy but not completely straightforward. If you try to provide command line arguments in the shell,gdb will think that you're giving it more files to examine, and it will complain:

$gdb/bin/echohelloworldExcesscommandlineargumentsignored.(world)[...]/Users/mikeash/shell/hello:Nosuchfileordirectory

Instead, command line arguments are provided to therun command at the gdb prompt:

(gdb)runhelloworldStartingprogram:/bin/echohelloworldReadingsymbolsforsharedlibraries+.donehelloworld

Environment variables can be set in the shell prior to startinggdb, but they will be visible togdb as well. Often, this is fine, but if you're manipulating something which can have a major effect on every program that it touches, like settingDYLD_INSERT_LIBRARIES, it's a bad idea to put them intogdb as well. In this case, theset env command can be used to set environment variables only for the target:

(gdb)setenvDYLD_INSERT_LIBRARIES/gdb/crashes/if/this/is/inserted.dylib

Conclusion
That wraps up today's exploration ofgdb. It's a difficult but powerful tool. Being able to fluently usegdb from the command line is often the difference between throwing up your hands in despair or tediously adding logging statements, and quickly diving in and getting the information you need to solve your problem.

gdb goes much deeper than I discussed here. For more information, consult its extensive internal help system by enteringhelp at the prompt. Every command has help as well, which can be accessed withhelp command.

As usual, come back in two weeks for the next Friday Q&A. Until that time, enjoygdb. As always, Friday Q&A is driven by reader suggestions, so if you have a topic you'd like to see discussed here, pleasesend it in!

Did you enjoy this article? I'm selling whole books full of them! Volumes II and III are now out! They're available as ePub, PDF, print, and on iBooks and Kindle.Click here for more information.

Comments:

Randy Beckerat2011-06-17 21:07:39:
Great article, as always. I do wish you had mentionedx/s. I often use it to inspect selectors that will be passed toobjc_msgSend().
Vasiat2011-06-17 21:33:16:
If you want to provide arguments to the target at the command-line, you can use:

gdb --args /path/to/executable arg1 arg2 etc
Evgeniy Dzhurovat2011-06-18 10:44:34:
Thanks for the article, it's quite useful. And I have one question to add. As I'm used to running gdb from Xcode and occasionally throw in commands as I learn, are there any significant capabilities that I'm missing out by not running the debugger separately as per your advice. Perhaps something must-know for day to day Xcode development?
Dadat2011-06-19 20:06:47:
GOLD! Great stuff! Thanks Mike.
mikeashat2011-06-20 01:43:08:
Running gdb separately is really only for when you can't, or at least don't want to, get Xcode involved at all. For example, debugging processes belonging to a different user, where you need to run the debugger as root, or getting gdb attached to some process for which there's no corresponding Xcode project, or unfortunate situations where you trigger bugs in Xcode which cause it to crash or otherwise fail at debugging. It's a nice skill to have because of that, but it doesn't really help except for specific situations.
Pádraig Bradyat2011-06-20 11:56:01:
My most common use of gdb is with: gdb -tui -args ....
That starts a curses interface which you can see in action here:
http://www.pixelbeat.org/programming/debugger/
SSteveat2011-06-20 21:03:09:
The first code example shouldn't have the@ before"Hello World!". You obviously need to try harder to write bugs. :-)
mikeashat2011-06-20 21:31:22:
If you mean the very first one under Getting Started, that's intended to be a working program. The bugs come later. It was tough to come up with realistic bugs, though....
sspat2011-06-20 21:38:10:
Very nice writeup, thanks a lot!

For curiosity's sake: care to give an example of something that doesn't work when using gdb from Xcode? I had the impression that – among the GUI-stuff – Xcode just offers a gdb console as well.
SSteveat2011-06-20 22:00:15:
Oh, sorry. I didn't notice the@ got removed. I thought that line of code was just a placeholder. I obviously need to try harder to read bugs. :-)
mikeashat2011-06-21 01:28:12:
Xcode does allow you to access nearly everything gdb can do, but there are a couple of gotchas.

One is that if you ever need to run gdb as root, say, to debug a process that runs as root, Xcode gets really inconvenient.

The other is that Xcode's GUI wrapper is buggy. I've had bugs in my code which completely destroyed Xcode's debugger due to weird data propagating up to the UI layer, but which command-line gdb handled fine.
sspat2011-06-21 06:09:13:
Thanks for the examples, Mike.
SefTarbellat2011-06-24 14:40:07:
Excellent article, I have found myself struggling with anything in gdb outside the basic p & po. This gave me a bunch of new things to try!
Mohamedat2011-06-29 20:56:31:
Very nice tutorial. Very nice.
Benat2011-07-06 19:44:24:
Awesome article, interesting to see the things it can do with Objective-C.

Don't forget about thestep andfinish commands (abbreviateds andf). They let you step into and out of the current function and are very useful for navigating shallow stacks when debugging in a localized area.
Sagyuwaraat2011-07-08 08:01:56:
Very simple and clear article. Thanks a lot.

Is it allowed to translate and post it on other forums?
mikeashat2011-07-08 14:20:49:
Please feel free to post translations, just provide attribution and a link to the original. If you do this, I'd love to see a link to the translation even if I can't understand it!
Sagyuwaraat2011-07-14 02:27:56:
I'm sorry for not responding sooner.
The link ishttp://www.cocoachina.com/bbs/read.php?tid-66525.html.
Thanks again.
mikeashat2011-07-15 01:13:12:
Very cool, thanks!
Hugo Rumensat2012-08-23 13:46:10:
This article is a great resource.

One thing I would add is I've found that putting in a cast for the return type when doing a print-object is not always necessary, e.g. with a method returning a string, both of these work:


(gdb) po [someObject someMethodReturningNSString]
or
(gdb) po (NSString *) [someObject someMethodReturningNSString]


However, if trying to call a method that takes an argument:


- (NSString *)someMethod:(NSDictionary *)dict;


calling


(gdb) po (NSString *) [someObject someMethod:[NSDictionary dictionary]]


gives you the infamous (even though there is a return type cast)

Unable to call function "objc_msgSend" at 0x7fff84f100f4: no return type information available.

You have to cast the argument as well:


(gdb) po (NSString *) [someObject someMethod:(NSDictionary *) [NSDictionary dictionary]]


I've also found you can't pass nil - this won't work:


(gdb) po (NSString *) [someObject someMethod:(NSDictionary *) nil]

Comments RSS feed for this page

Add your thoughts, post a comment:

Spam and off-topic posts will be deleted without notice. Culprits may be publicly humiliated at my sole discretion.

Name:
The Answer to the Ultimate Question of Life, the Universe, and Everything?
Comment:
Formatting:<i> <b> <blockquote> <code>.
NOTE: Due to an increase in spam, URLs are forbidden! Please provide search terms or fragment your URLs so they don't look like URLs.
Code syntax highlighting thanks toPygments.
Hosted atDigitalOcean.

[8]ページ先頭

©2009-2025 Movatter.jp