Over a million developers have joined DZone.

DBX vs. Visual Studio and WinDbg: Part 2A, Configuring Multiple Breakpoints

·

This is where we are through the series:

  • Calling a function
  • Configuring breakpoints
  • Tracing execution
  • Execution control
  • Displaying data, including STL collections
  • Runtime application checking
  • Miscellaneous commands

Today’s post is about configuring multiple breakpoints at once in a convenient fashion. If you’re used to setting your breakpoints one at a time, you may find it very convenient to reason about breakpoints in a more flexible way, which is something DBX excels at.

DBX
Here are some of the breakpoint types that you can use in DBX to configure multiple breakpoints at once:

stop inmember execute Will insert breakpoints in all methods called “execute”, very useful for virtual methods or when you’re too lazy to type the exact class name
stop inclass vector Will insert breakpoints in all the methods of the class “vector”, and an additional -recurse switch can control whether to consider derived classes
stop inobject &obj Will insert breakpoints in all the member functions of the class that is the type of obj, and stop only when obj is the this parameter (i.e. stop only when the methods are called on the specified instance)

Here’s a couple of examples:

(dbx) restore
stopped in main at line 11 in file "stl.cc"
   11     std::map<int,std::vector<float> > m;
(dbx) list
   11     std::map<int,std::vector<float> > m;
   12     std::vector<float> v;
   13     v.push_back(4.0f);
   14     v.push_back(5.0f);
   15     m[5] = v;
   16     global = 2;
   17     getchar();
   18   }
(dbx) stop inclass std::vector<float,std::allocator<float> >
(3) stop inclass std::vector<float,std::allocator<float> >
(dbx) cont
stopped in std::vector<float, std::allocator, <float>void>::vector at line 182 in file "stl_vector.h"
  182         : _Base(__a) { }
(dbx) cont
stopped in std::vector<float, std::allocator, <float>void>::push_back at line 558 in file "stl_vector.h"
  558       if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage)
(dbx) where
=>[1] std::vector<float, std::allocator, <float>void>::push_back(this = 0x8047950, __x = 4.0), line 558 in "stl_vector.h"
  [2] main(), line 13 in "stl.cc"

...

(dbx) stop infunction main
(2) stop infunction main
(dbx) run
Running: stl
(process id 3740)
stopped in main at line 13 in file "stl.cc"
   13     v.push_back(4.0f);
(dbx) stop inobject &v2
(3) stop inobject (class vector<float,std::allocator<float> > *) &v2 (0x8047950)
(dbx) cont
stopped in std::vector<float, std::allocator, <float>void>::push_back at line 558 in file "stl_vector.h"
  558       if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage)
(dbx) where
=>[1] std::vector<float, std::allocator, <float>void>::push_back(this = 0x8047950, __x = 6.0), line 558 in "stl_vector.h"
  [2] main(), line 15 in "stl.cc"

These options are very flexible and work incredibly well—and it seems to me that once you’re used to them, it’s rather hard to let go :-)

WinDbg
The only thing WinDbg has that can be used to match the power of the DBX commands above is the bm command, which configures a symbol breakpoint based on a pattern. The pattern syntax has basic regular expression features such as [A-z], f[0-9]+, and so on.

With this command in mind, the following are examples of how to approximate the inmember and inclass breakpoint types:

0:000> bm *!std::vector*
  1: 01084e40 @!"myapp!std::vector<float,std::allocator<float> >::reserve"
  2: 010836d0 @!"myapp!std::vector<float,std::allocator<float> >::capacity"
  3: 01083720 @!"myapp!std::vector<float,std::allocator<float> >::clear"
  4: 01082630 @!"myapp!std::vector<float,std::allocator<float> >::~vector<float,std::allocator<float> >"
  5: 01083c10 @!"myapp!std::vector<float,std::allocator<float> >::_Orphan_range"
  6: 01083990 @!"myapp!std::vector<float,std::allocator<float> >::_Destroy"
  7: 010851a0 @!"myapp!std::vector<float,std::allocator<float> >::erase"
  8: 010838b0 @!"myapp!std::vector<float,std::allocator<float> >::_Buy"
  9: 010826d0 @!"myapp!std::vector<float,std::allocator<float> >::operator="
10: 01082900 @!"myapp!std::vector<float,std::allocator<float> >::size"
11: 01088440 @!"myapp!std::vector<float,std::allocator<float> >::_Ucopy<float *>"
12: 01087550 @!"myapp!std::vector<float,std::allocator<float> >::_Assign_rv"
13: 01083b30 @!"myapp!std::vector<float,std::allocator<float> >::_Tidy"
14: 01085140 @!"myapp!std::vector<float,std::allocator<float> >::max_size"
15: 01085440 @!"myapp!std::vector<float,std::allocator<float> >::_Grow_to"
16: 01083a00 @!"myapp!std::vector<float,std::allocator<float> >::_Inside"
17: 010824a0 @!"myapp!std::vector<float,std::allocator<float> >::push_back"
18: 01086860 @!"myapp!std::vector<float,std::allocator<float> >::vector<float,std::allocator<float> >"
19: 01082430 @!"myapp!std::vector<float,std::allocator<float> >::vector<float,std::allocator<float> >"
20: 010854f0 @!"myapp!std::vector<float,std::allocator<float> >::_Xlen"
21: 01086930 @!"myapp!std::vector<float,std::allocator<float> >::_Make_iter"
22: 01083a80 @!"myapp!std::vector<float,std::allocator<float> >::_Reserve"
23: 01085020 @!"myapp!std::vector<float,std::allocator<float> >::begin"
24: 010850b0 @!"myapp!std::vector<float,std::allocator<float> >::end"
25: 01088790 @!"myapp!std::vector<float,std::allocator<float> >::_Umove<float *>"
26: 01087cf0 @!"myapp!std::vector<float,std::allocator<float> >::get_allocator"

0:000> bm /a *!*CreateProcess*
27: 61c51118 @!"MSVCR100D!_imp__CreateProcessW"
28: 61c5110c @!"MSVCR100D!_imp__CreateProcessA"
29: 751ec9c5 @!"kernel32!CreateProcessAsUserW"
30: 751d4a7a @!"kernel32!BasepCreateProcessParameters"
31: 751d37bf @!"kernel32!BasepConstructSxsCreateProcessMessage"
32: 751c0654 @!"kernel32!_imp__RtlCreateProcessParametersEx"
33: 751d4f21 @!"kernel32!BasepReleaseSxsCreateProcessUtilityStruct"
34: 75243b81 @!"kernel32!NtVdm64CreateProcessInternalW"
35: 751c8de0 @!"kernel32!NtWow64CsrBasepCreateProcess"
36: 751dad8f @!"kernel32!BasepSxsCreateProcessCsrMessage"
37: 751d5115 @!"kernel32!CsrBasepCreateProcess"
38: 751d3bf3 @!"kernel32!CreateProcessInternalW"
39: 751c1072 @!"kernel32!CreateProcessA"
40: 751c103d @!"kernel32!CreateProcessW"
41: 751da4b7 @!"kernel32!CreateProcessInternalA"
42: 770215ac @!"KERNELBASE!_imp__RtlpCreateProcessRegistryInfo"
43: 77026afc @!"KERNELBASE!NtWow64CsrBasepCreateProcess"
44: 77a7ffdc @!"ntdll!NtCreateProcessEx"
45: 77a80804 @!"ntdll!NtCreateProcess"
46: 77a980b7 @!"ntdll!RtlpCreateProcessRegistryInfo"
breakpoint 45 redefined
45: 77a80804 @!"ntdll!ZwCreateProcess"
breakpoint 44 redefined
44: 77a7ffdc @!"ntdll!ZwCreateProcessEx"
47: 77b01d35 @!"ntdll!RtlCreateProcessReflection"
48: 77b0e7ab @!"ntdll!RtlCreateProcessParameters"
49: 77aabd9b @!"ntdll!RtlCreateProcessParametersEx"

However, the inobject breakpoint type is harder to approximate, because you need a conditional breakpoint that would verify the value of the this parameter:

0:000> dv /i /V
prv param  0024fd50 @ebp+0x08 argc = 1
prv param  0024fd54 @ebp+0x0c argv = 0x00554b18
...
prv local  0024fcfc @ebp-0x4c v = class std::vector<float,std::allocator<float> >

0:000> bm *!std::vector<float* ".if (@ecx == 0024fcfc) {} .else {gc}"
  2: 01154e40 @!"myapp!std::vector<float,std::allocator<float> >::reserve"
  3: 011536d0 @!"myapp!std::vector<float,std::allocator<float> >::capacity"
  4: 01153720 @!"myapp!std::vector<float,std::allocator<float> >::clear"
...

0:000> g
eax=0024fc00 ebx=7efde000 ecx=0024fcfc edx=0024fcfc esi=0024fbec edi=0024fd3c
eip=011524a0 esp=0024fbe4 ebp=0024fd48 iopl=0         nv up ei pl zr na pe nc
cs=0023  ss=002b  ds=002b  es=002b  fs=0053  gs=002b             efl=00000246
myapp!std::vector<float,std::allocator<float> >::push_back:
011524a0 55              push    ebp

One disadvantage of the WinDbg way is that it will later require you to remove the breakpoints one-by-one, while in DBX each command you issued is translated to a single “unified” breakpoint that you can remove.

Visual Studio
Visual Studio does not have a built-in way to configure a breakpoint on a particular object or all the methods of a particular class. However, this is one example of a situation where Visual Studio macros may come in handy.

Here is one solution, from stackoverflow’s Richard Szalay:

Sub AddBreakpointInAllClassMembers() 
  Dim debugger As EnvDTE.Debugger = DTE.Debugger
  Dim sel As EnvDTE.TextSelection = _
      DTE.ActiveDocument.Selection
  Dim editPoint As EnvDTE.EditPoint = _
      sel.ActivePoint.CreateEditPoint()
  Dim classElem As EnvDTE.CodeElement = _
      editPoint.CodeElement(vsCMElement.vsCMElementClass) 

  If Not classElem Is Nothing Then
    For Each member As EnvDTE.CodeElement _
                    In classElem.Children
      If member.Kind = vsCMElement.vsCMElementFunction Then
        debugger.Breakpoints.Add(member.FullName)
      End If
    Next
  End If
End Sub

It has the obvious limitation of not working on arbitrary symbols, and requiring source code to actually set the breakpoints. If the former is desired, I suppose it’s possible to integrate the .x Immediate Window command with a macro to set breakpoints automatically similarly to WinDbg’s bm. The .x command performs a symbol lookup like the x command in WinDbg, for example:

.x myapp!*push_back
0x012724f0 myapp!std::vector<float,std::allocator<float> >::push_back

Clearly, it would not be difficult to add to the above macro support for breaking only when a specific object’s methods are called (e.g. by specifying a condition on this). For the specific situation when you want a single-method breakpoint on a specific object, the documentation specifies there is an easier workaround: if you want a breakpoint on the method MyClass::foo only when called on the instance x, then use the Immediate Window to find &x and then create a function breakpoint on

((MyClass*)0x1234abcd)->foo

Unfortunately, from my testing (with Visual Studio 2010 Ultimate), this simply doesn’t work. If any of you had ever seen this working, I’d love to hear about it :-)

Topics:

Published at DZone with permission of Sasha Goldshtein, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

The best of DZone straight to your inbox.

SEE AN EXAMPLE
Please provide a valid email address.

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.
Subscribe

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}