佳为好友

转:Breakpoint conditions with GDB

创建:10-3-29

转:http://blog.timac.org/?p=118

When debugging your application, you use breakpoints. The program will return control to GDB every time it reaches a breakpoint you set. This may not be desirable if you have breakpoint on a method that is called many times and you want to break only with certain values passed to that method. GDB provides several ways to do conditional breakpoints that I’ll try to explain.

 

1- The problem

Let take a really simple application which calls 10 times a function.

  1. #import <foundation foundation.h="">  
  2.   
  3. void myFunction()  
  4. {  
  5.    // Initialize the counter to 0  
  6.    static int sFunctionCounter = 0;  
  7.   
  8.    // Each time we enter the function,  
  9.    // increment the counter  
  10.    sFunctionCounter++;  
  11.   
  12.    NSLog(@"function called: %d", sFunctionCounter);  
  13. }  
  14.   
  15. int main (int argc, const char * argv[])  
  16. {  
  17.    NSAutoreleasePool * pool =[[NSAutoreleasePool alloc] init];  
  18.    int i;  
  19.   
  20.    // Call 10 times myFunction.  
  21.    for(i = 0 ; i< 10 ; i++)  
  22.         myFunction();  
  23.   
  24.    [pool release];  
  25.    return 0;  
  26. }  
  27. </foundation>  
#import   void myFunction() {    // Initialize the counter to 0    static int sFunctionCounter = 0;     // Each time we enter the function,    // increment the counter    sFunctionCounter++;     NSLog(@"function called: %d", sFunctionCounter); }  int main (int argc, const char * argv[]) {    NSAutoreleasePool * pool =[[NSAutoreleasePool alloc] init];    int i;     // Call 10 times myFunction.    for(i = 0 ; i< 10 ; i++)         myFunction();     [pool release];    return 0; } 

Now imagine that you want to break in myFunction, but only the 4th time you enter that function. One easy solution would be to modify the function by adding a if statement and setting a breakpoint accordingly.

myFunction

This method works fine in that case but you need to modify your function, you need to rebuild, you can't do it if you don't have the sources...

2- Conditional breakpoints

GDB can let you add breakpoints to stop whenever a certain point in the program is reached. The problem is that if you set a breakpoint to the function myFunction, you will break 10 times and will need to continue until you get the right occurrence.
Hopefully you can specify a condition (boolean expression) on your breakpoint so that your program stops only if the condition is true.

Let's take the sample of the previously detailed program.
We will use the Console in Xcode but you can use directly GDB in command line if you prefer.

Here is what to do:
- First we set a breakpoint at the entry of the main function of the program.
- Set a breakpoint to stop on myFunction: break myFunction.
- Finally tell the debugger to only break if sFunctionCounter is equal to 4: condition 9 sFunctionCounter == 4

The program will run and will stop at the 4th call of the function myFunction as you can see on this screenshot:


Console


3- Breakpoints commands

Conditional breakpoints are useful but not really flexible. GDB provides another solution: the breakpoint commands.
You can give any breakpoint a series of commands to execute when your program stops due to that breakpoint. For example, you can display the values of certain expressions or enable/disable some breakpoints.
We will take another simple example. Our new program call 10 times 2 methods with detachNewThreadSelector. The calls to method1: and method2: are asynchronous so that we can't tell if method1: will be executed before method2:.

  1. #import <foundation foundation.h="">  
  2.   
  3. @interface Test : NSObject  
  4. {  
  5. }  
  6.   
  7. -(void)method1:(id)sender;  
  8. -(void)method2:(id)sender;  
  9.   
  10. @end  
  11.   
  12. @implementation Test  
  13.   
  14. -(void)method1:(id)sender  
  15. {  
  16.    // Do something  
  17. }  
  18.   
  19. -(void)method2:(id)sender  
  20. {  
  21.    // Do something  
  22. }  
  23.   
  24. @end  
  25.   
  26. int main (int argc, const char * argv[])  
  27. {  
  28.    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];  
  29.   
  30.    int i;  
  31.    Test *myTest = [[Test alloc] init];  
  32.   
  33.    // Call 10 times the methods method1: and method2:.  
  34.    // The calls are asynchronous so that we can't tell if method1:  
  35.    // will be executed before method2:.  
  36.   
  37.    for(i = 0 ; i< 10 ; i++)  
  38.    {  
  39.       NSLog(@"iteration: %d", i);  
  40.   
  41.       // Call method1: asynchronously  
  42.       [NSThread detachNewThreadSelector:@selector(method1:) toTarget:myTest withObject:nil];  
  43.   
  44.       // Call method2: asynchronously  
  45.       [NSThread detachNewThreadSelector:@selector(method2:) toTarget:myTest withObject:nil];  
  46.   
  47.       // Wait before continuing to loop  
  48.       [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];  
  49.    }  
  50.   
  51.    [myTest release];  
  52.   
  53.    [pool release];  
  54.    return 0;  
  55. }  
  56. </foundation>  
#import   @interface Test : NSObject { }  -(void)method1:(id)sender; -(void)method2:(id)sender;  @end  @implementation Test  -(void)method1:(id)sender {    // Do something }  -(void)method2:(id)sender {    // Do something }  @end  int main (int argc, const char * argv[]) {    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];     int i;    Test *myTest = [[Test alloc] init];     // Call 10 times the methods method1: and method2:.    // The calls are asynchronous so that we can't tell if method1:    // will be executed before method2:.     for(i = 0 ; i< 10 ; i++)    {       NSLog(@"iteration: %d", i);        // Call method1: asynchronously       [NSThread detachNewThreadSelector:@selector(method1:) toTarget:myTest withObject:nil];        // Call method2: asynchronously       [NSThread detachNewThreadSelector:@selector(method2:) toTarget:myTest withObject:nil];        // Wait before continuing to loop       [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:1]];    }     [myTest release];     [pool release];    return 0; } 

What we would like to do, is to break in method2: only if method2: is called before method1:.
This is not trivial to do with conditional breakpoints, and not easy to do by modifying the source code.


Let's talk bout the powerful breakpoint commands, which can solve this. You will need to write a little txt file. This file contains the commands to execute after a breakpoint is reached.

  1. # Define two integers which will be incremented  
  2. # each time you enter the corresponding method.  
  3. set $method1_Counter = 0  
  4. set $method2_Counter = 0  
  5.   
  6. # We create a breakpoint using "future break"  
  7. # If we break due to this breakpoint, the command  
  8. #following will be executed.  
  9. fb method1:  
  10. command  
  11.    set $method1_Counter = $method1_Counter + 1  
  12.    printf "method1 calledn"  
  13.    continue  
  14. end  
  15.   
  16. # We create the second breakpoint and  
  17. # the coressponding command.  
  18. fb method2:  
  19. command  
  20.    set $method2_Counter = $method2_Counter + 1  
  21.    printf "method2 calledn"  
  22.   
  23.    if($method1_Counter < $method2_Counter)  
  24.       printf "method2 was called before method1!n"  
  25.    else  
  26.       continue  
  27.    end  
  28. end  
# Define two integers which will be incremented # each time you enter the corresponding method. set $method1_Counter = 0 set $method2_Counter = 0  # We create a breakpoint using "future break" # If we break due to this breakpoint, the command #following will be executed. fb method1: command    set $method1_Counter = $method1_Counter + 1    printf "method1 calledn"    continue end  # We create the second breakpoint and # the coressponding command. fb method2: command    set $method2_Counter = $method2_Counter + 1    printf "method2 calledn"     if($method1_Counter < $method2_Counter)       printf "method2 was called before method1!n"    else       continue    end end 

As you can see, this little txt file is really simple. We define 2 breakpoints, one in method1: and one in method2:. For each breakpoint defined, we define a command (kind of a small program) which will be executed.


The command related to the breakpoint method1: prints "method1 called" and we tell the debugger to continue the execution of the program.
The command related to the breakpoint method2: prints "method2 called" and we only tell GDB to continue the program if method1_Counter < method2_Counter (the number of calls to method2: is greater than the number of calls to method1:). If method1: was called before method2:, we continue the execution of the program.


Now that we have our txt file, we can start our application, load the txt file using the source command and see that indeed we only break if method2: is called before method1:


Console


+++++

posted on 2012-12-25 09:50 佳为好友 阅读(252) 评论(0)  编辑 收藏 引用 所属分类: Debug-GDB


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


导航

<2024年11月>
272829303112
3456789
10111213141516
17181920212223
24252627282930
1234567

留言簿(1)

随笔分类

搜索

最新评论

评论排行榜