6

First the problem I am trying to solve: I'm debugging a C# application that has huge object graphs (think Building Information Models, a kind of object oriented CAD). When I hit a breakpoint, I generally have long lists of objects I'd first need to transform to be useful for debugging.

In code, I use LINQ and lambdas to do this. But you can't do that in the Watch window and the Immediate window.

How could I go about adding an IronPython shell extension to Visual Studio 2010 that lets me snoop the same information available to the Immediate window / Watch window?

EDIT: I can figure out how to make a debugger visualizer. But from the API it seems I would only have access to the object being visualized - while I'd actually prefer to have access to all local variables.

EDIT: From the documentation on msdn it seems a DE (Debug Engine) with an EE (Expression Evaluator?) can do the trick. This is for integrating your own language into Visual Studio. I'm trying to hook into the existing DE or at least provide my own EE.

Daren Thomas
  • 67,947
  • 40
  • 154
  • 200

2 Answers2

4

It turns out its rather easy to write a Visual Studio 2010 Addin: Just download and install the Visual Studio 2010 SDK. Next, create an Addin project.

The OnConnection Method in the Connect class of your Addin will provide you with a DTE2 instance. This can be used to poke around in the Visual Studio Debuggers Expression Evaluator:

DTE2 application; // fill this in OnConnection
application.Debugger.GetExpression("some c# code goes here")

The results are Expression instances, COM objects. Check the Value property.

Homework: Figure out how to wrap that up in a nice pythonic framework to make it seem seamless.

Daren Thomas
  • 67,947
  • 40
  • 154
  • 200
  • Anyone interested on how this is coming along: Just comment here, I'll gladly post the code to a project host in exchange for some help! – Daren Thomas Jul 09 '10 at 09:40
  • I've just asked about something similar here http://stackoverflow.com/questions/4749974/getting-debugger-context-in-f-interactive and then I found this question. I'm interested in how things are going with your implementation. – Max Jan 20 '11 at 18:59
  • @Max, I lost interest ;) you can find the code at: http://code.google.com/p/ironpythondebugger/ – Daren Thomas Feb 08 '11 at 11:49
1

I don't know whether this will help, but there's a good series about writing debuggers and extensions in managed code at:

http://devhawk.net/blog/2009/2/27/writing-an-ironpython-debugger-mdbg-101

Before I start writing any debugger code, I thought it would help to quickly review the .NET debugger infrastructure that is available as well as the design of the MDbg command line debugger. Please note, my understanding of this stuff is fairly rudimentary – Mike Stall is “da man” if you’re looking for a .NET debugger blogger to read.

The CLR provides a series of unmanaged APIs for things like hosting the CLR, reading and writing CLR metadata and – more relevant to our current discussion – debugging as well as reading and writing debugger symbols. These APIs are exposed as COM objects. The CLR Debugging API allows you to do those all the things you would expect to be able to do in a debugger: attach to processes (actually, app domains), create breakpoints, step thru code, etc. Of course, being an unmanaged API, it’s pretty much unavailable to be used from IronPython. Luckily, MDbg wraps this unmanaged API for us, making it available to any managed language, including IronPython.

The basic design of MDbg looks like this:

image

At the bottom is the “raw” assembly, which contains the C# definitions of the unmanaged debugger API – basically anything that starts with ICorDebug and ICorPublish. Raw also defines some of the metadata API, since that’s how type information is exposed to the debugger.

The next level up is the “corapi” assembly, which I refer to as the low-level managed debugger API. This is a fairly thin layer that translates the unmanaged paradigm into something more palatable to managed code developers. For example, COM enumerators such as ICorDebugAppDomainEnum are exposed as IEnumerable types. Also, the managed callback interface gets exposed as .NET events. It’s not perfect – the code is written in C# 1.0 style so there are no generics or yields.

Where corapi is the low-level API, “mdbgeng” is the high-level managed debugger API. As you would expect, it wraps the low-level API and provides automatic implementations of common operations. For example, this layer maintains a list of breakpoints so you can create them before the relevant assembly has been loaded. Then when assemblies are loaded, it goes thru the list of unbound breakpoints to see if any can be bound. It’s also this layer that automatically creates the main entrypoint breakpoint.

Finally, at the top we have the MDbg application itself, as well as any MDbg extensions (represented by the … in the diagram above). The mdbgext assembly defines the types shared between MDbg.exe and the extension assemblies. MDbg has some cool extensions – including an IronPython extension – but for now I’m focused on building something as lightweight as possible, so I’m going to forgo an extensibility mechanism, at least for now.

My initial prototype was written against the high-level API. There were two problems with this approach. The first is that there’s no support for Just My Code in the high-level API. As I mentioned in my last post, JMC support is critical for this project. Adding JMC support isn’t hard, but I’m trying to make as few changes as possible to the MDbg source, since I’m not interested in forking and maintaining that code. Second, while the low-level API provides an event-based API (OnModuleLoad, OnBreakpoint, OnStepComplete, etc), the high-level API provides a more console-oriented looping API. I found the event-driven API to be cleaner to work with and I’m thinking it will work better if I ever build a GUI version of ipydbg. So I’ve decided to work against the low-level API (aka corapi).

I mentioned above that I didn’t want to change the MDbg source, but I did make one small change. The separation of corapi and raw into two separate assemblies is an outdated artifact of an earlier version of MDbg. So I decided to combine these two into a single assembly called CorDebug. Other than some simple cleanup to assembly level attributes to make a single assembly possible, I haven’t changed the source code at all.

vard
  • 4,057
  • 2
  • 26
  • 46
Matthew Steeples
  • 7,858
  • 4
  • 34
  • 49
  • Thanks. I had seen MDbg, but failed to compile the python extension). This seems a bit of a round-about root, since it is a standalone debugger and I'd like to just hook into the Visual Studio Debugger. Alas, I think I'm gonna lose this one :( – Daren Thomas Jul 08 '10 at 12:16