| Brian's profileInside F#BlogGuestbookNetwork | Help |
|
|
May 17 DebuggerVisualizers in F#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. 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: 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): When selected, it will pop up a dialog box that displays the list contents:
Installing the debugger visualizerFor 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:
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.
Authoring debugger visualizersThough 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 codeHere's the code: #light 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 [<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 Comments (6)
Brian McNamara
has turned off comments on this page.
TrackbacksWeblogs that reference this entry
|
|
|