Anyone remember Deblector? It was an add-in for Reflector 6 which provided debugging support. It became open source in 2007, but ceased development at Reflector 6.5 a few years ago.
In the last few weeks I’ve picked it back up, in order to investigate whether debugging could make it into the next version of Reflector Desktop as a novel feature. This is a request we’ve had a few times over email and in the forums, when users either can’t start up Visual Studio (or prefer not to), but still want to be able to easily pinpoint bugs in their code.
As a working prototype, Deblector was a superb starting point for this investigation.
The add-in was based on Mdbg, Microsoft’s managed debugger sample, which runs on the command line. Mdbg runs inside a shell in Deblector, and all of Deblector’s commands are actually passed to Mdbg for execution, which means that Deblector has all the functionality available to Mdbg, such as running/attaching to processes, setting breakpoints and stepping through code. This makes Deblector quite powerful as a debugging tool, as it allows you to navigate through code at IL or source level while the code is running.
One of the largest problems with Deblector is its inability to accurately step through source code for third-party assemblies decompiled by Reflector. Those of you familiar with Reflector or debuggers will know that this is because there isn’t a PDB file available for that assembly. So a really nice feature I was looking to add was to hook some PDB generation capability (which our Visual Studio Extension already does) into Deblector and Reflector Desktop, allowing the tool to step at source level. This, in turn, required making Deblector into a fully-fledged feature of Reflector.
You can see an example of this low-level stepping failure in the screenshot below. We’ve stepped into a lambda, and that’s taken us to
System.MulticastDelegate inside mscorlib. However, the instruction pointer (IP) in the bottom pane is showing
MAPPING_APPROXIMATE, meaning it doesn’t have an IL or source mapping for the step:
So the first thing to do was to get Deblector to start up with Reflector, and this was as simple as adding a few lines of code to Reflector to get it to load the add-in on start-up. The add-in also needed some minor changes so it would no longer ‘feel’ like an add-in, and this meant changing some of the buttons and menu items to create a consistent user experience. As an aside, if you’re interested in how add-ins interact with Reflector, see Jason Haley’s tutorial on making your own add-in. It’s not completely up to date, but still relevant as we haven’t made any drastic changes to the add-in model in quite a while.
Having integrated Deblector into Reflector so that the debugger started seamlessly, I turned to the rest of the team for help planning the next stages of development. We held a session to break the work down into tasks, and decided that we really needed to prioritise ease of use before improving the debugger’s features. So the next step was to improve the user interface, and there were some blatant problems I had already noticed while initially testing Deblector; for example the shell, although powerful, was clunky and unnecessarily complex.
For this task I required a lot of help from Ryan (our UX expert), so he and I came up with several designs and discussed the pros and cons of each before I ultimately decided on which one to investigate more deeply. I focussed on one particular design which I thought would be familiar to users (so the controls are similar to other debuggers) but innovative (to try and remove unnecessary clutter from the main Reflector window). Before I wrote a single line of code for this design, I built a full paper prototype to test out our assumptions about how users would interact with it.
If you’re not familiar with paper prototyping, there’s a great article on A List Apart explaining the basics and some obvious benefits, like saving time writing code and improving design flaws. But the greatest benefit is that you centre the design around the users, so that you know your program will be easier to use by building usability testing in at such an early stage. Since the intended interface changes were so major, prototyping was a clear choice here, but I would recommend it for even seemingly minor changes whenever you are altering a workflow which some users will already know or expect.
To test our design with the paper prototype, I also came up with two scenarios which I thought were typical tasks for a debugger. The first involved using the debugger to confirm the existence of a bug by stepping through the code and checking the values of variables as the code runs. Admittedly this could be achieved through testing, but I wanted to see whether they could get accustomed to the controls I’d provided. The second scenario required the user to find where a piece of code was causing an uncaught exception, and navigate the call stack to fix it.
The prototype was tested on a cross-section of developers and testers here at Red Gate, and brought up several interesting issues with my interface design.
Some things I had designed well; for example everyone wanted to hover over variables to get their current value when stopped on a breakpoint, and most people managed to use the ‘Debug’ menu in the top command bar.
Some other things weren’t so good, though; my lack of a call stack window and the positions of the buttons on the screen caused a little confusion. There were also some problems with the prototype itself, and it was difficult to model all the intricacies of the debugging workflow on paper. Naturally, this improved in each successive test, as I added more functionality to the prototype every time someone tried to do something I hadn’t accounted for!
This process was useful in itself, because it quickly led me to realise that, as debugging is a problem-solving exercise, everyone has their own preferred way of tackling it. The fact that I had trouble anticipating these methods, and had to keep widening the functionality scope of the prototype helped me to realise that we may have been trying to bite off more than we could chew.
Test early; Test often
But there was a more shocking problem, and it was the general reaction to the concept of a debugger inside Reflector desktop. No one completed the first scenario because they didn’t feel comfortable with the extra features available to them. And for the second scenario, everyone (without exception) said they would just open the code inside Visual Studio.
There is a point to note here about how realistic my scenarios were; it would have been better to get examples of tasks such as remote debugging, which people might realistically want to use Reflector Desktop for, but this would have been very difficult to model on paper. Although paper is cheap, the time cost of making really convoluted prototypes begins to outweigh the benefits of prototyping in the first place. More to the point, if you have to construct elaborate prototypes, it implies that your application design is too complicated!
This seriously bought into question the viability of fully integrating Deblector into Reflector. Not only would the project take a large amount of development work, but users also expected to have access to the full breadth of features available to them in Visual Studio. Unfortunately, due to the limitations of Mdbg (it is only a sample after all), we would never be able to meet those expectations. So, after much discussion, we decided to shelve the project in favour of other features (which we’re not going to reveal just yet!)
Conclusion and Take-aways
So what have we gained from all this? Well, the early investigation and prototyping has saved us a huge amount of time and resources. Paper prototyping especially has been extremely valuable, as it really highlighted the flaws in what we were trying to do before even a single line of code was written. Without the insight offered by the whole prototyping process (designing the workflows, building the interface, and testing with real users), I doubt the team would have realised we were attempting to turn Reflector into Visual Studio until it was too late, and we had invested heavily (and wastefully) in that direction.
Either way, as a direct result of the project, I have rebuilt the Deblector add-in with a few improvements, and have released it back onto Codeplex (with the permission of the original author Felice Pollano). I haven’t made any major changes, so if you’ve used Deblector before it should be as you remember it, just now with compatibility for Reflector 7.6.
It’s also still open source, so if you have a keen interest then feel free to contribute; I’d love to know your opinions on the project! And if debugging in Reflector Desktop (through Deblector or otherwise) is something you’d be interested in in the future, leave a comment and let me know – I’ll get in touch to find out more.