views:

3156

answers:

5

One thing that annoys me when debugging programs in visual studio (2005 in my case) is that when I use "step over" (by pressing F10) to execute to the next line of code, I often end up reaching that particular line of code in a totally different thread than the one I was looking at. This means that all the context of what I was doing was lost.

Does anyone know any way of working around this?

If this is possible to do in later versions of visual studio, I'd like to hear about it as well.

Setting a breakpoint on the next line of code which has a conditional to only break for this thread is not the answer I'm looking for since it is way too much work to be useful for me :)

+6  A: 

You can freeze different thread or switch to another thrad using Threads debug window (Ctrl + Alt + H).

Lazin
Thanks. This is the closest to a good solution for this I've seen so far!
Laserallan
That's what I'm doing too, the question is ... why a single step changes the thread being executed?
SoMoS
A: 

Does 'step in' followed by 'step out' work any better? I've always avoided step over and 'run to cursor' when debugging multi-threaded code.

James Hopkin
+11  A: 

I think there is only one answer to your question, which you have discounted as being 'way too much work.' However, I believe that is because you are going about it the wrong way. Let me present steps for adding a conditional breakpoint on Thread ID, which are extremely easy, but not obvious until you know them.

  1. Stop the Debugger at a point where you are in the correct thread you want to continue debugging in. (Which I would guess is usually the first thread that gets there.)

  2. Enter $TID into the watch window.

  3. Add a break point with the condition $TID == <value of $TID from Watch Window>,
    Example: $TID == 0x000016a0

  4. Continue Execution.

$TID is a magic variable for ms compilers (since at least VS 2003) that has the value of the current Thread ID. It makes it much easier than looking at (FS+0x18)[0x24]. =D

That being said, you can get the same behavior as the Debugger's One-Shot breakpoints with some simple macros. When you step over, the Debugger, behind the scenes, sets a breakpoint, runs to that breakpoint, then removes it. The key to a consistent user interface is removing those breakpoints if ANY breakpoint is hit.

The Following two macros provide Step Over and Run To Cursor for the current thread. This is accomplished in the same mannor as the Debugger, with the breakpoints being removed after execution, regardless of which breakpoint is hit.

You will want to assign a key combo to running them.

NOTE: One caveat -- The Step Over macro only works correctly if the cursor is on the line you want to step over. This is because it determines the current location by the cursor location, and simply adds 1 to the line number. You may be able to replace the location calculation with information on the current execution point, though I was unable to locate that information from the Macro IDE.

Here they are and good luck bug hunting!!

To use these macros in Visual Studio:
1. Open the Macro IDE ( from the Menu, select: Tools->Macros->Macro IDE... )
2. Add a new Code File ( from the Menu: select: Project->Add New Item..., choose Code File, and click Add )
3. Paste in this code.
4. Save the file.

To Add Key Combos for running these macros in Visual Studio:
1. Open Options (from the Menu, select: Tools->Options )
2. Expand to Environment->Keyboard
3. In Show commands containing:, type Macros. to see all your macros.
4. Select a macro, then click in Press shortcut keys:
5. Type the combo you want to use (backspace deletes typed combos)
6. click Assign to set your shortcut to run the selected macro.

Imports System
Imports EnvDTE
Imports EnvDTE80
Imports System.Diagnostics

Public Module DebugHelperFunctions

    Sub RunToCursorInMyThread()
        Dim textSelection As EnvDTE.TextSelection
        Dim myThread As EnvDTE.Thread
        Dim bp As EnvDTE.Breakpoint
        Dim bps As EnvDTE.Breakpoints

        ' For Breakpoints.Add()
        Dim FileName As String
        Dim LineNumber As Integer
        Dim ThreadID As String

        ' Get local references for ease of use 
        myThread = DTE.Debugger.CurrentThread
        textSelection = DTE.ActiveDocument.Selection

        LineNumber = textSelection.ActivePoint.Line
        FileName = textSelection.DTE.ActiveDocument.FullName
        ThreadID = myThread.ID

        ' Add a "One-Shot" Breakpoint in current file on current line for current thread
        bps = DTE.Debugger.Breakpoints.Add("", FileName, LineNumber, 1, "$TID == " & ThreadID)

        ' Run to the next stop
        DTE.Debugger.Go(True)

        ' Remove our "One-Shot" Breakpoint
        For Each bp In bps
            bp.Delete()
        Next
    End Sub

    Sub StepOverInMyThread()
        Dim textSelection As EnvDTE.TextSelection
        Dim myThread As EnvDTE.Thread
        Dim bp As EnvDTE.Breakpoint
        Dim bps As EnvDTE.Breakpoints

        ' For Breakpoints.Add()
        Dim FileName As String
        Dim LineNumber As Integer
        Dim ThreadID As String

        ' Get local references for ease of use 
        myThread = DTE.Debugger.CurrentThread
        textSelection = DTE.ActiveDocument.Selection

        LineNumber = textSelection.ActivePoint.Line
        FileName = textSelection.DTE.ActiveDocument.FullName
        ThreadID = myThread.ID
        LineNumber = LineNumber + 1

        ' Add a "One-Shot" Breakpoint in current file on current line for current thread
        bps = DTE.Debugger.Breakpoints.Add("", FileName, LineNumber, 1, "$TID == " & ThreadID)

        ' Run to the next stop
        DTE.Debugger.Go(True)

        ' Remove our "One-Shot" Breakpoint
        For Each bp In bps
            bp.Delete()
        Next
    End Sub


End Module

Disclaimer: I wrote these macros in Visual Studio 2005. You can probably use them fine in Visual Studio 2008. They may require modification for Visual Studio 2003 and before.

Aaron
Does this work for anyone? I couldn't get it work.
dr. evil
It works for me. =D What was your error?
Aaron
@Aaron: I'm using VS 2008 when I enter $TID in the Watch window I get the error "The name does not exist in the curren context"
hab
@mnh: What version of vs2008 are you using? I just tried it in 9.0.30729.1 SP, and it worked fine for me.
Aaron
@Aaron: Same version, 9.0.30729.1 SP.
hab
@mnh: I just loaded some small project, hit F11 to step in, then typed $TID into the watch window and I got the pointer to the TIB. I don't know what more to suggest.
Aaron
Thanks for the `$TID` tip
kizzx2
+3  A: 

I have the same complaint as Laserallan. Visual Studio should have a "step in current thread" button. I understand the motivation for the current behavior; maintaining concurrency as much as possible is important to present as typical an execution behavior as possible during debugging. But in many (most?) cases it's not what's desired when stepping through code. I think Visual Studio's default behavior is a design mistake, and the current thread should be maintained by default when stepping.

In any event, there should at least be a step button that maintains the current thread. Having to define macros or set conditional breakpoints should be unnecessary and indeed requires "too much work" for a function that is so obviously useful.

A: 

Ctrl-Alt-F10 - Step Over Current Process

Ctrl-Alt-F11 - Step Into Current Process

Ctrl-Alt-Shift-F11 - Step Out Current Process