views:

1557

answers:

5

Hey,

Regardless of other options that may achieve the same result (i.e. adding breakpoints by hand)

Is is possible to programmatically add a breakpoint into the source code of a visual studio project?

such as

try
{
   FunctionThatThrowsErrors(obj InscrutableParameters);
}
catch(Exception ex)
{
   Log.LogTheError(ex);
   AddBreakPointToCallingFunction();
}

That way when you run in debug the next time, it will have automatically set breakpoints at all the points that caused trouble during the last run.

I'm not saying that's a particularly useful way of debugging. I'm just wondering if the capability is there.

+15  A: 

You can just call System.Diagnostics.Debugger.Break().

You can also tell Visual Studio to break on all exceptions, even handled ones, by going on the menu to Debug->Exceptions... and checking Thrown everywhere that's currently only checked "User-unhandled".

Joel Coehoorn
First of all, (+1) that's awesome, I had no idea it existed. But, the documentation indicates that "If no debugger is attached, users are asked if they want to attach a debugger." That means this method would inturrupt the *current* execution flow if running without a debugger. In my case, I want it to set a breakpoint for next time. Also, the question stated that I wanted to set the breakpoint in the calling function, because (presumably) the calling function has important contextual information that was not passed down.
You can't change the code after it's compiled. That means what you want to do involves raising your breakpoint behind a configuration flag that is always checked, and changing the the configuration in your exception. That's potentially a LOT of configuring. Best bet in this case is to break on throw, as my edit shows.
Joel Coehoorn
Also, you can check Debugger.IsAttached before calling break.
Joel Coehoorn
I've used that tool before as well, and it's also incredibly useful. But, that assumes that I'm already running in the debugger. I was hoping for a solution that is inobtrusive (unobtrusive?) when it first runs (I.E. production type environment) but is capable of being run in debug, and highlighting (without manually setting breakpoints myself) where the trouble spots were on the last run. Assuming that I can get indentical input, (e.g. I'm running a report or something. Or parsing input data.)
The best you can do is give your exceptions names/ids that you log, and when running in debug mode later check at each point and see is the current name/id was logged.
Joel Coehoorn
A: 

I don't think you can really "add a breakpoint", but you can instruct the debugger to suspend execution so that you can check what went wrong, by calling System.Diagnostics.Debugger.Break()

Thomas Levesque
+1  A: 

It's not really responsive to your question, but you can cause the debugger to break based on conditions you set using Debug.Assert. So, instead of saying "next time I run a function that caused an exception, break" you can add assertions to your function to break when conditions are not what they should be. After all, there is no guarantee that a function will throw an exception this time just because it threw an exception last time. :)

JP Alioto
+10  A: 

You inspired me to poke around with this - thanks for keeping me awake all night. :) Here's one way you can do it.

Visual Studio has really great breakpoint support. One of the cooler features is that you can tell it to run a Visual Studio macro when the breakpoint is hit. These macros have full access to the development environment, i.e. they can do whatever you could do manually at the keyboard, including setting other breakpoints.

This solution is to 1) put a top-level try/catch in your program to catch all exceptions, 2) put a breakpoint in the catch block that runs your macro, and 3) have the macro look at the exception to figure out where it came from, and put a breakpoint there. When you run it in the debugger and an exception occurs, you'll have a new breakpoint at the offending line of code.

Take this sample program:

using System;

namespace ExceptionCallstack
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                func1();
            }
            catch (Exception e)
            {
                Console.WriteLine("Oops");
                Console.ReadKey();
            }
        }

        static void func1()
        {
            func2();
        }

        static void func2()
        {
            func3();
        }

        static void func3()
        {
            throw new Exception("Boom!");
        }
    }
}

The objective is to programmatically set a breakpoint on that throw in func3 when you run it in the debugger and get the error. To do this, first create a new Visual Studio macro (I called mine SetBreakpointOnException). Paste this into a new module MyDebuggerMacros or whatever:

Imports System
Imports EnvDTE
Imports EnvDTE80
Imports EnvDTE90
Imports System.Diagnostics
Imports System.Text.RegularExpressions

Public Module DebuggerMacros

    Sub SetBreakpointOnException()

        Dim output As String = ""

        Dim stackTrace As String = DTE.Debugger.GetExpression("e.StackTrace").Value
        stackTrace = stackTrace.Trim(New Char() {""""c})
        Dim stackFrames As String() = Regex.Split(stackTrace, "\\r\\n")

        Dim r As New Regex("^\s+at .* in (?<file>.+):line (?<line>\d+)$", RegexOptions.Multiline)
        Dim match As Match = r.Match(stackFrames(0))
        Dim file As String = match.Groups("file").Value
        Dim line As Integer = Integer.Parse(match.Groups("line").Value)

        DTE.Debugger.Breakpoints.Add("", file, line)

    End Sub

End Module

Once this macro is in place, go back to the catch block and set a breakpoint with F9. Then right-click the red breakpoint circle and select "When Hit...". At the bottom of the resulting dialog there's an option to tell it to run a macro - drop down the list and pick your macro. Now you should get new breakpoints when your app throws unhandled exceptions.

Notes and caveats about this:

  • I am not a regex guru, I'm sure someone else can whip up something better.
  • This doesn't handle nested exceptions (InnerException property) - you can beat your head against that if you want. :) Check for GetExpression("e.InnerException") and recurse, perhaps.
  • It does text parsing on the excpetion's StackTrace string, not more-sophisticated object graph analysis (digging down into Exception.TargetSite and using reflection). The usual caveats apply about the fragility of this approach.
  • For some reason it seems to put the breakpoint into some "alternate space". Once the initial debugging session is over, you don't see the new breakpoint in your code. But it's there if you run the program again in the debugger, and things like "Disable All Breakpoints" affect it. It would be nice to learn about what's going on, if someone feels like finding a way to clean that up. Maybe digging around in the .suo file?

Hope this helps!

David Pope
wow!!! (I wanted to type only 'wow!!!' but it wouldn't allow less thn 15 chars :) )
Rashmi Pandit
Sweet Lordy. If there was a way I could give you +10, I would.
I need to put in a reminder in my phone to come back and upvote this answer tomorrow when I get new votes. :-)
daft
A: 

Also, Visual Basic has a keyword called Stop that will essentially act as a break point and break execution.

icemanind
I believe you may have misunderstood the question. I want the executing program to modify the pdb file, *not* the source code. That way, I could run it normally the first time, and see how it handles, and then I could attach a debugger, and it would have breakpoints already set up at all the places that failed last time. Your Stop solution will *always* stop execution.