DBX vs. Visual Studio and WinDbg: Part 2A, Configuring Multiple Breakpoints
Join the DZone community and get the full member experience.
Join For FreeThis is where we are through the series:
- Calling a function
- Configuring breakpoints
- Setting multiple breakpoints at once [this post]
- Memory access breakpoints
- Conditional 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 :-)
Published at DZone with permission of Sasha Goldshtein, DZone MVB. See the original article here.
Opinions expressed by DZone contributors are their own.
Trending
-
How to Submit a Post to DZone
-
How to LINQ Between Java and SQL With JPAStreamer
-
Avoiding Pitfalls With Java Optional: Common Mistakes and How To Fix Them [Video]
-
Extending Java APIs: Add Missing Features Without the Hassle
Comments