A first look at the Pharo debugger

The debugger is an essential tool in any programming environment as it allows developers to interact with a running system and explore its state. Given the seer size and complexity of today’s software systems it’s hard to imagine reasoning about the dynamic behaviour of an application without using one.

Considering that it is so useful we want the debugger to have lots and lots of features:

  • manage breakpoints (conditional/data breakpoints)
  • control execution (step through code, step over/into functions, etc.)
  • field watches
  • take snapshots
  • inspect variables
  • modify variables
  • monitor the call-stack
  • code edit/swap at runtime
  • intuitive GUIs
  • remote debugging
  • support for working with threads
  • reversible/back-in-time debugging

By all means this is not an exhaustive list. What it shows is that we expect a lot from a good debugger. The best part is today’s debuggers do provide most of these features.

But what happens when we want something that’s not there, which is bound to happen sooner, rather than later? First, we might go in denial: ‘This features has to be somewhere. I just have to find it. I can’t be the first one to have this problem.’ So we start looking. We search online, ask colleagues, read documentation, etc. In the end, given the huge amount of information that’s out there, we find something that addresses our problem. It can be a workaround, a hack, a pice of code that we do not understand but works, some cleaver way of combining several tools, etc.  It can be a strange or ugly solution, but hey, if it solves the problem it is perfect.

I follow this reasoning a lot of times. I’m sure a lot of you do too. However, there is one small thing that few of us take into account: if there is a feature missing in the debugger why don’t I just implement it? I believe most answers boil down to ‘Because it’s hard’.

The debugger is just a software application. To extend it we have to understand it. And as it turns out this can be rather difficult. To see this let’s look at how the debugger is implemented in Pharo 2.0. As Pharo is a *Smalltalk inspired* environment, and Smalltalk prides itself on its live environment, one might think that extending the debugger is straightforward.

As it turns out the entire debugger is implemented in a single class, named Debugger. This contains both the low level logic of working with a process and the logic for creating and updating the user interface. Everything is nicely tight together and heavily optimised. The class has 1453 lines of code grouped in 158 methods that access 23 attributes (including inherited ones); it has 120 lines of documentation. To give you a feel of how the class is organised here is a Class Blueprint and a System Attraction view showing its internal structure:

We can see that methods are heavily interconnected. There are large methods both in the public and private interface. Attributes are accessed both directly and using accessor methods.

Does this mean this implementation of the debugger is wrong? After all it provides most of the features mentioned above,  the code is in one place and it has been like that for a while. Like many other things in software I believe the answer is change: as long as we don’t need to modify it or extended it, the implementation is just fine. However, as soon as we need to add new features, the current implementation only gets in the way.

Now the question becomes: ‘Why would one want to modify the debugger?’. Why not continue using the available one and when new problems appears hope to find somewhere something someone thought can be of help to you?

To answer this, consider for a moment the explosion of new frameworks and libraries that happened in the last years. With the right library or framework all our problems can be solved in one or two lines. We add layers over layers of abstraction to reduce the number of lines of code we need to write. Just then what happens when things do not work? Most of the times we are lost in a world of abstractions. The debugger does not serves its purpose anymore. The fixed semantics of what it does and show are so far away from the reality of our systems that it becomes less and less effective. And, given the highly particular situations we encounter, searching for solutions revels less and less useful insight.

“We become what we behold. We shape our tools and thereafter our tools shape us”.

Said almost fifty hears ago in the context of media it describes well how we currently work with debuggers. We create large monolithic debuggers with lots of features and little support for extension. Then, when we encounter new situations we haven’t considered before, we try to solve them using the available ones even if they are ill suited for that. We don’t attempt to adapt the debugger, as we created a debugger that does not allow us to do that.

What would happen if instead of creating large debuggers with lots of functionalities we would instead focus on moldable debuggers that have extensibility as their main feature?

Would we then, when building a large and complex framework, invest perhaps a fraction of our energy in creating a dedicated debugger for that framework? Now only could this help us to better understand the framework, but would surely make the life of our users much easier. And wouldn’t this then increase the value of our framework? Let’s say you have to choose between to parsers. They have more or less the same functionalities, but one also ships with a dedicated infrastructure for debugging; the other has none. Which one would you choose?

To make this vision possible we need to build moldable debuggers from the start. We saw in this post that the current implementation of the Pharo debugger is a rigid one. In the next post we will look at a new design of the debugger that encourages extensibility.

6 thoughts on “A first look at the Pharo debugger

    1. No we shouldn’t. We should fix our tools. I don’t see anything here that couldn’t be added to gcc or llvm. Until you get to feature parity with those you are wasting your time. When you get to feature parity you have to ask was it worth it. Then when you add your bonus features you have to ask, would this time have been better spent adding these features to gcc/llvm where it would find a much larger audience? If you add to gcc/llvm you may even get help implementing your features.

      1. Sometimes you can only fix a tool by reinventing it. I’m sure one could add all the features that I mentioned to gcc or llvm. However, to do this you need to be quite knowledgeable with gcc/llvm. My goal is not to just implement several new features but rather to have a debugging infrastructure with a low learning curve that one can easily adapt and extend. And Pharo is great for that. I might not have such a larger audience, but I do not mind.

  1. I believe you to be on the correct track. I was the head of a team that created a GIS system that specialized in facilities management for utilities. It went head to head with the big guns of that industry and was judged by most to have advanced features not provided by the others, many are not included in the systems today. This was in 1992 on 486 PCs running OS/2 using Smalltalk/V. Digitalk’s version of Smalltalk was great because it allowed access to the OS which was important for high performance graphics. Later we ported the product to PARC’s version, which was hard because it was more closed.

    One of the things we learned early was that in OO it is best to assemble small, tight and correct objects into a larger application. The behavior of the small objects had been thoroughly tested and made the application easier to test and later expand. In my view, a modular debugger is the correct approach to take. Besides I have an interest in seeing that done as I have an extension to Smalltalk that will require the availability of that kind of debugger. Where would I submit my proposal for my enhancements?

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 )

Connecting to %s