Clicky

T is for… Tracepoint - Jim O'Neil - Technology Evangelist - Site Home - MSDN Blogs

T is for… Tracepoint

Jim O'Neil

Technology Evangelist

E-mail  Twitter  LinkedIn  RSS Feed  About me

Check out my new blog at http:>//codocent.com

T is for… Tracepoint

Rate This
  • Comments 6

t

I’ve presented various sessions on Debugging Tips and Tricks as part of the Northeast Roadshow and MSDN Events series, and one of the undiscovered gems in that presentation is that of tracepoints.  Tracepoints have actually been around since Visual Studio 2005, but weren’t all that discoverable until Visual Studio 2008.

What exactly is a tracepoint?  It’s essentially a breakpoint that doesn’t halt the execution of your application in the debugger.  Instead, it provides the opportunity to write information to the output window, with much the same effect that Trace.WriteLine has, but without requiring code to do it.  You can also use tracepoints to run macros for more advanced debugging scenarios.

To get an idea of how to employ tracepoints, here’s a YACE (yet-another-contrived-example) that builds a list of the prime numbers between two input numbers.  There are far better algorithms than this for performing this task, so don’t focus too much on the logic, lack of bounds checking, coding style, performance issues, etc.

 

   1:  List<Int32> primeList = new List<Int32>();
   2:  Boolean isPrime;
   3:   
   4:  if ((startVal <= 2) && (endVal >= 2)) primeList.Add(2);
   5:   
   6:  for (int candidate = Math.Max(3, startVal); candidate <= endVal; 
   7:           candidate++)
   8:  {
   9:      isPrime = true;
  10:      int maxTest = (int) Math.Sqrt(candidate) + 1;
  11:   
  12:      for (int testVal = 2; (testVal < maxTest) && isPrime; testVal++)
  13:          if (candidate % testVal == 0)
  14:              isPrime = false;
  15:   
  16:      if (isPrime) primeList.Add(candidate);
  17:  }

 

If I employ this logic in a simple console application, and set startVal to say 700 and endVal to 800, I’ll get output such as the following:

Prime console output

Now, lets say I’m looking at the output and really wondering why 703 and 713 didn’t make the list.  They “look” prime – at least they don’t pass those handful of quick tests we learned in junior high – so I’m curious as to what factor kicked them out of the list.  It’s the code at lines 13 and 14 where a divisor is detected for a candidate prime number, and that divisor is testVal.

I could put some code in there using System.Diagnostics.Trace to output testVal at that point, but it’s going to require me to introduce more code, as well as some logic, perhaps, to print out only when it’s dealing with the values 703 and 713.  And, maybe it’s not even my code to be tinkering with.

One option here is to introduce a tracepoint on line 14 to capture just the information you want but without causing execution to stop as a breakpoint would.  You can insert a tracepoint by right-clicking on a line of code and selecting the option from the context menu as you see here:

Tracepoint context menu

Alternatively, you can click on the gutter to add a breakpoint, and then use the When hit… menu item to convert it from a breakpoint into a tracepoint (note the diamond shape versus the circle in the gutter):

 

When hit...

 

Both of these mechanisms bring up a dialog with two main options

  • Print a message, or
  • Run a macro

Regardless of which option you choose, you can elect to continue execution or break (as a normal breakpoint would).  By default, when you select either the Print a message or Run a macro option, the Continue execution box will be checked.

 

Print a message

 

When Breakpoint Is Hit... dialog

 

When you choose to Print a message, the verbiage no longer has the disabled look, and you can use the textbox to indicate what you want to display in the output window.  The Function and Thread information are there as a default and demonstrate that you have access to a number of debugging environment values (which are described on the dialog).  You can also include your own variables and expressions enclosed in curly braces.  For our scenario here, I’ve set up the tracepoint as follows:

 

Tracepoint settings

 

When I execute the application now, it runs to completion, but within the Output window, I get the information I was looking for, and I can see that 703 is divisible by 19 and 713 by 23.

 

Output window

 

Keep in mind it’s still a breakpoint too, so the other options on a breakpoint apply to activating the tracepoint as well.  For instance, if I really want the trace information written for only 703 and 713, then I can set up a breakpoint condition such as the following:

 Breakpoint Condition dialog

 

Output windowThe output will show as on the right, and execution will continue.  In such a case, the tracepoint glyph will include a white cross

Tracepoint glyph

indicating that there are advanced options set.  

 

 

Run a macro

The Run a macro option gives you even more power for handling a tracepoint, but presumes you’re willing to work for it by writing a bit of Visual Basic for Applications script to implement a macro.  The dropdown list already provides a number of options; these are macros that come with Visual Studio, and the ones in the Macros.Samples.VSDebugger namespace are the most germane here. 

 Available macros

Now, the ShowCurrentProcess macro here isn’t all that interesting, but when engaged will display the path to the current process in the output window.

 

ShowCurrentProcess output

 

You can examine the implementation of this macro and the other others listed in the dropdown list by opening the Macros IDE (Alt+F11).

Macros IDE

A closer look at the implementation below reveals the use of the EnvDTE namespace and the OutputWindowPane, Process, and DTE objects within that namespace. 

 

' This function displays the current debugger mode in the output window.
 Sub ShowCurrentProcess()
     Dim outputWinPane As EnvDTE.OutputWindowPane
     Dim proc As EnvDTE.Process
 
     outputWinPane = Utilities.GetOutputWindowPane("Debugger")
     proc = DTE.Debugger.CurrentProcess
     If (proc Is Nothing) Then
         outputWinPane.OutputString("No process is being debugged")     
     Else
         outputWinPane.OutputString("" + Str(proc.ProcessID) + ": " 
             + proc.Name + vbCrLf)
     End If
 

End Sub

 

DTE is the top most object in the Visual Studio automation model and provides access to the debugger as well as the IDE including toolbars, commands, the active document, and more.  With programmatic control of these objects, you can create more advanced breakpoints and tracepoints, like, for instance, one that would analyze the stack trace and break only if method bar was called by method foo

Check out the MSDN documentation on Visual Studio Extensibility for more information on building your own macros.

Join the Conversation
Leave a Comment
  • Please add 3 and 3 and type the answer here:
  • Post
Read What Other's Think
  • Thanks so much for this! Seriously, i hate that there are so many undiscovered (to me) trivial features to VS that could of helped save me  countless hours of work.  This being one of them for sure.

  • Thank you for the positive feedback!  There are definitely a number of cool things buried in the debugger.  You might want to check out my colleague's Channel 9 videos that cover the same materials I used on our Roadshow.  He's got three up there now, but I think there are more coming:  http://channel9.msdn.com/Search/?Term=Brian Hitney

  • Thanks for the article. I have successfully implemented the way you have descibled using the "When Breakpoint is Hit" dialog.

    Now, I want to redirect the output to a text file instead of the Output window. Can you please provide some tips how to go about it?

  • I don't think there's anything built in.  There's an option to redirect output window to immediate window, which doesn't help here, but I'm thinking that if they did that surely they would have considered writing to a file too and yet there is no similar option.  I suspect this might be doable via Visual Studio extensibility, and I've asked a few folks to find out if something like this might already be floating out there.

  • How do you add a tracepoint programatically?

    I can add a breakpoint programmatically, but I want to add a breakpoint that triggers a macro.

  • I noticed the EnvDTE90a.Breakpoint3 interface has what you need.  I hacked this together and it seems to work (it doesn't look like a tracepoint - with the diamond - until you run the debugger)

    Public Module Module1

       Public Sub addTracepoint()

           DTE.Debugger.Breakpoints.Add(Function:="Main", Line:=1)

           Dim bp As EnvDTE90a.Breakpoint3

           bp = DTE.Debugger.Breakpoints.Item(1)

           bp.Macro = "Macros.MyMacros.Module1.foo"

           bp.BreakWhenHit = False

       End Sub

       Public Sub foo()

           MsgBox("Got here!")

       End Sub

    End Module

Page 1 of 1 (6 items)