How to break in WinDbg when particular function returns specific value??

If you are debugging a lot, you might come across a need where you need to put a breakpoint on a function and special requirement is you need to break only when that function returns a specific value!! e.g. I am checking for CoCreateInstance method from COM library and want to break only when it returns 0x80040154 (i.e. Class Not Registered!). Obviously, we can put normal breakpoint by using following command,

bp Ole32!CoCreateInstance

but it will break everytime that function will be called, say if you have 3000 COM component instances created in your code, you will loose some of your hairs reaching interest of your break in WinDbg.

So, here is the technique to the rescue. I will first explain the logic how its done.

  • Little background, In Windbg, we can put conditional breakpoints where we can check register values (like eax, ebx, eip, esp etc.) when particular breakpoint is hit. Conditional breakpoints will break only if condition is satisfied.
  • If we can get the return address for our function of interest, we can put breakpoint on that address checking for eax register value (return value is always stored in eax register when function returns). And that way, we can break while returning the function if particular value is set in eax register.

Here is a sample example.. What we need to do is, we want to break in WinDbg only when myClass::test1 returns 100 otherwise go ahead.

    1:  class myClass
    2:  {
    3:  public:
    4:      myClass();
    5:      int test1(int param1);
    6:      void Crash();
    7:  };
    8:   
    9:  myClass::myClass()
   10:  {
   11:      //DO Nothing.. let runtime construct the object..
   12:  }
   13:   
   14:  int myClass::test1(int param1)
   15:  {
   16:      return param1 * 2;
   17:  }
   18:   
   19:  void myClass::Crash()
   20:  {
   21:      int another_var=0;
   22:      int p = 200/another_var;
   23:  }
   24:   
   25:  void CWinDbgDlg::OnBnClickedButton1()
   26:  {
   27:      // TODO: Add your control notification handler code here
   28:      myClass *obj = new myClass();
   29:      int retVal = obj->test1(50);
   30:      if(retVal == 100)
   31:      {
   32:          //Some faulty condition has occurred, you dont want to come here,
   33:          //catch this condition before this happens (in debugger)..
   34:          obj->Crash();
   35:      }
   36:  }

I am debugging above code. Following are few things that should be carried out in WinDbg. Attach to the process you want to debug.

Find out the address of function (not the return address) you are interested in.

SHOT0028

Put a breakpoint on that function (this is just to find out return address of the function.. If you can findout return address by some other technique let me know!)

SHOT0029

Hit 'g' and execute application till it calls this function. When it breaks for that function, give k* command to see call stack, as it will show the return address in function call stack!

SHOT0028

Here, 0x00412e93 is the return address we are interested in! So, put the conditional breakpoint as follows,

 bp 00412e93 "j @eax = 0x00000064  '';'gc'"

Notice here one thing? Yes, you are right. We are putting breakpoint for eax register while checking for value 0x00000064 which is Hex equivallent of 100 decimal (dont forget this step, otherwise you will never hit the breakpoint!)

After doing above, hit 'g' and you will break only when test1 function returns 100. (In my case, it will break immediately because I have hardcoded the parameter 50).

Stay tuned.. Wave

Comments

  • Anonymous
    October 29, 2007
    PingBack from http://kobyk.wordpress.com/2007/10/29/breaking-when-a-function-returns-a-specific-value-without-depending-on-its-call-site/

  • Anonymous
    August 20, 2009
    You mention getting a function’s return address in the callstack view and then setting a breakpoint there.  The problem is that the return address is the address in the calling method for that particular invocation.  If multiple functions call you, then you have multiple return addresses. Your example only sets a breakpoint at the 1 callsite you happen to hit when you first run the function. You could see this problem if you added another call to test1(50) through a different function.   One solution is to set the breakpoint at the ‘ret’ instruction within the target function (test1)  and then check for eax there. This usually works because even if a function has multiple C++ return statements, the codegen tends to jump them all to the same label so that they can share the epilogue. Mike http://blogs.msdn.com/jmstall