Inside F#

Brian's thoughts on F# and .NET

DebuggerVisualizers in F#

Posted by Brian on May 17, 2008

One of my very favorite features of Visual Studio is debugger visualizers.  These let you pop up a custom display of values, like those values that appear in the "locals" window, for example.

The F# team has not yet invested much effort in the F# debugging experience.  (EDIT, Jan 2010: the out-of-the-box debugging experience is much improved now.  This entry is still useful to demonstrate custom visualizers, but you won’t need them for basic types.)  One thing that needs improvement is the appearance of values in the locals window.  For example, look at how these list variables ("lc" and "li") appear by default:

DebugDefault

The "Value" in the locals window at the bottom just displays as "{Microsoft.FSharp.Collections.List<char>._Cons}", for instance.  That doesn’t help much when you’re debugging your program – typically you want to see the values in the list (e.g. "[‘a’; ‘b’; ‘c’]").

Though we’ll undoubtedly improve the out-of-the-box story here in a future release of F#, the current situation gives me a good excuse to talk about debugger visualizers.  When you install a visualizer, a little magnifying-glass icon appears next to values, along with a drop-down menu that lets you select among the visualizers.  In today’s blog entry, I’ll show how to install a "list visualizer" which can be selected in the debugger as suggested in the screenshot below (note the "FSharp List Visualizer" drop-down near the very bottom of the picture):

DebugVizMagnifyingGlass

When selected, it will pop up a dialog box that displays the list contents:

DebugViz

 

Installing the debugger visualizer

For those of you who just want the install instructions to try this out, I’ll summarize them:

From the Visual Studio "Tools\Options" menu, under "Debugging\General", uncheck "warn if no symbols on launch" (see screenshot below).

Build the F# code that appears at the end of this blog into an exe.  Then copy the exe into the VS debugger visualizers directory, using a command-line like:

copy MyDebugViz.exe "c:\Program Files\Microsoft Visual Studio 9.0\Common7\Packages\Debugger\Visualizers"

Close Visual Studio.  Re-open Visual Studio.

That’s it – now you’re ready to go.  Just put a breakpoint on the "printf" line as suggested by the screenshot below, and you should see the magnifying glass icon in the locals window next to list values, as in the previous screenshots.

DebugOptions

 

Authoring debugger visualizers

Though the documentation for visualizers is a little sparse, simple visualizers are pretty easy to write.  I won’t go into all the nitty gritty details, but I’ll give the high-level overview.  There are three main parts. 

First, there is the "object source", which runs in the debuggee (the process being debugged).  It is used to serialize "some data about the object we want to debug" into a stream which we can send to the debugger.  Here’s an example, that uses <sprintf "%A"> as a way to pretty-print an F# value to a string, which is then written to a stream.

type FSharpObjectSource() =
    inherit VisualizerObjectSource()
    override this.GetData(target : Object, outgoingData : System.IO.Stream) =
        let formatter = new BinaryFormatter()
        formatter.Serialize(outgoingData, sprintf "%A" target)
        ()

Next, there is the "debugger visualizer", which runs in the debugger, and receives the data from the debuggee via an "object provider".  It can display the information it receives in a dialog box.  In the code below, we receive our string data, and then just create a Form with a RichTextBox to display the data.

type FSharpDebuggerVisualizer() =
    inherit DialogDebuggerVisualizer()

    override this.Show(windowService : IDialogVisualizerService,
                       objectProvider : IVisualizerObjectProvider) =
        let data = objectProvider.GetObject()
        use displayForm = new Form()
        let text = new RichTextBox(Dock = DockStyle.Fill,
                                   Text = data.ToString(),
                                   Font = new Font("Lucida Console",10.0f,FontStyle.Bold),
                                   ForeColor = Color.DarkBlue)
        displayForm.Controls.Add(text)
        displayForm.Text <- "F# list visualizer"
        windowService.ShowDialog(displayForm) |> ignore

Finally, there is an attribute which associates an object source and a visualizer with a specific data type and gives it a name:

[<assembly:DebuggerVisualizer(typeof<FSharpDebuggerVisualizer>,
                              typeof<FSharpObjectSource>,
                              Target = typedefof<Microsoft.FSharp.Collections.List<Object> >,
                              Description = "FSharp List Visualizer")>]

The "Target" here says that this visualizer will work on any variable with type list<‘a> (in the CLR, the type we want to target is called "List`1" – the uninstantiated-generic type for List, and this is what the F# "typedefof" operator yields).  The "Description" is the text that appears in the drop-down menu next to the magnifying glass icon (as in the second screenshot in today’s blog entry).  And the two "typeof" arguments just point to the classes that contain the code that should run in the debugger and debuggee when this visualizer is invoked.

That’s pretty much all there is to it.  If you find yourself often debugging some code that uses data that doesn’t display well naturally in the debugger, visualizers are a great way to extend the debugger to provide your own visualization of the data in your running program.

The source code

Here’s the code:

namespace FSharpDebuggerVisualizers

#r "C:\Program Files\Microsoft Visual Studio 9.0\Common7\IDE\PublicAssemblies\Microsoft.VisualStudio.DebuggerVisualizers.dll"

open System
open System.Diagnostics
open System.Runtime.Serialization.Formatters.Binary
open System.Collections.Generic
open System.Windows.Forms
open System.Drawing
open Microsoft.VisualStudio.DebuggerVisualizers

type FSharpObjectSource() =
    inherit VisualizerObjectSource()
    override this.GetData(target : Object, outgoingData : System.IO.Stream) =
        let formatter = new BinaryFormatter()
        formatter.Serialize(outgoingData, sprintf "%A" target)
        ()

type FSharpDebuggerVisualizer() =
    inherit DialogDebuggerVisualizer()

    override this.Show(windowService : IDialogVisualizerService,
                       objectProvider : IVisualizerObjectProvider) =
        let data = objectProvider.GetObject()
        use displayForm = new Form()
        let text = new RichTextBox(Dock = DockStyle.Fill,
                                   Text = data.ToString(),
                                   Font = new Font("Lucida Console",10.0f,FontStyle.Bold),
                                   ForeColor = Color.DarkBlue)
        displayForm.Controls.Add(text)
        displayForm.Text <- "F# list visualizer"
        windowService.ShowDialog(displayForm) |> ignore

namespace Global

open System
open System.Diagnostics
open FSharpDebuggerVisualizers

module Main =

  [<assembly:DebuggerVisualizer(typeof<FSharpDebuggerVisualizer>,
                                typeof<FSharpObjectSource>,
                                Target = typedefof<Microsoft.FSharp.Collections.List<Object> >,
                                Description = "FSharp List Visualizer")>]
  do
      let li = [1; 2; 3]
      let lc = [‘a’; ‘b’; ‘c’]
      printfn "%A %A" li lc

    printfn "press a key to quit"
    System.Console.ReadKey() |> ignore

Advertisements

6 Responses to “DebuggerVisualizers in F#”

  1. Brian said

    I should add that you can use DebuggerDisplayAttribute to change what appears in the Value field.  For example, add this code right above the existing [<assembly:…>] attribute and watch what happens:

    module ListMethods =
        let AsPrettyString(l : list<\’a>) = sprintf "%A" l
    [<assembly:DebuggerDisplay("{Global.ListMethods.AsPrettyString(this)}",
        Target = typedefof<Microsoft.FSharp.Collections.List<Object> >)>]

  2. Unknown said

    Fantastic! This kind of thing has me really excited [0] about IDE research, the vast majority of which does seem to be coming out of that little-known-company in Redmond. My question upon the first capture was "so do they expose the language model and let me extend these", immediately answered in the affirmative…I hope this kind of thing finds its way into modern language design beyond F# (which looks grand, by the way — well done to you and your team).Does F# grow at all from the inspired work that was FC++ (in case I don\’t read any further before you reply)?Good to see you\’re keeping it real, Brian! This looks like really, really fun work.[0] (I\’m less thrilled about the Windows Live Gay Spaces account required to comment. C\’est la vie!)

  3. Daniel Furrer said

    Thanks for this nice article.This really makes debugging F# apps easier while there is no support in default by F#.What would be nice IMO is to have viewers for all the common F# types.I tried to create a viewer using an annotation like:[<assembly:DebuggerVisualizer(typeof<FSharpDebuggerVisualizer>,                               typeof<FSharpObjectSource>,                               Target = typedefof<Object>,                               Description = "FSharp List Visualizer")>] to have the viewer available for all Objects but that doesn\’t seem to work for Union types. Do you know anything more here?

  4. Brian said

    Good question.  You can\’t use typedefof<Object> as a Target in a DebuggerVisualizer (by design of DebuggerVisualizers).  This is unfortunate, as since Union types do not have a common base class there is no way to make visualizers "for all Unions".  However we intend to have a better default debugging experience for union types in a future release of F#, so hopefully this will be less important after that.

  5. Daniel Furrer said

    Thanks for your quick answer.It\’s very nice that F# will be fully integrated in Visual Studio. But how am I going to debug my applications until then?Is it possible to attach F# interactive to your debugging session, so that I can use printfs to inspect variables etc? (This would make debugging very powerful in general). Or do you have any other hints on F# debugging?Unfortunately VS is not open source, for otherwise I would hack into it myself ;-)

  6. Brian said

    The "immediate window" is your friend (though it still uses C# syntax).  Check out this screenshot for a suggestion.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: