To gdb or not to gdb?

Whether 'tis nobler in the mind to suffer
The slings and arrows of outrageous fortune,
Or to take arms against a sea of troubles

Spoiler alert: the answer is 1 or true

Before delving back in the next planned blog post of another important nuts-and-bolts Devel::Trepan topic — position and status information — I'd like to take a step back and talk a little bit philosophical like I did in the first blog.

In that blog, I wrote something a bit contradictory. I wrote that I wrote Devel::Trepan to follow gdb. But at the end of the blog I wrote that I wanted it to be more of a platform for (cough, cough) research.

The coughing noise you hear is because as far as I can tell there has been very little research on debuggers that I am aware of. If you know of some, let me know. I think there has been an emergence or awareness of dtrace-like implementations and protocols which are more popular in Ruby than in Perl . There is the sadly and largely ignored work on protocols for debugging such as DBGP for which there is even an implementation in Perl. There is possibly reverse debugging which is new in gdb 7. But this idea goes back a while.

So let me back off expectations of "research". Think of what I mean more in the category of software-engineering usability issues in what a debugger might offer to make it easier to debug programs.

Still, there is the contradiction: how can one do research and still follow gdb? And the answer is simple: you can''t completely.

So what I meant is: be like gdb, unless there is reason not to.

To prove that I really mean to be like gdb, I will mention a recent bug where I was caught in erroneously thinking that in gdb "d" meant "down" (it means "delete"), while "u" meant "up" (it means "until"). Fixing this means making an incompatible change to possibly 7 or so other debuggers for which I'll probably have to take the heat for. But when I am wrong, I am wrong.

But let's now go into areas where we don't strictly follow gdb.

One area alluded to in the second blog had to do with macros and aliases; gdb doesn't have command aliases. It has macros which are more powerful and subsumes command aliases. But because it is a little more cumbersome, I thought I would add this simpler mechanism which covers most of what I think will be programmers' needs. And we don't follow gdb's macros because Perl already is a good programming language for writing debugger macros. Also, people using the Perl debugger probably already are familiar with Perl. So why invent another language? gdb has to do that because it is not a Perl, or Python, or Lisp, or POSIX Shell, or Ruby debugger. It is a debugger for C, C++, Objective-C, Java, Fortran, Pascal, Modula-2 or Ada which is generally a different crowd. Most of those languages don't have an eval() function. And even if they did, would you want to write debugger macros in a statically typed language where you have to declare variables? Even if some variables were pre-defined, I'm not sure I would want to write my debugger macros in, say C.

The point I am trying to make here is that in this case, for macros, it doesn't make sense to follow gdb. And while I have written debuggers in POSIX shell, Perl, Python and Ruby, the macro language used in each debugger is not one common language (as it is in gdb). Instead, the underlying of the language of the program getting debugged.

The example of macros introduces two other broad areas. First, that while gdb is a debugger for static languages, Perl is not a static language. Second, each programming language has its own idiosyncrasies. An example of this second category for Perl is in the dynamically-scoped "local" variables. Furthermore, different implementations have their own run-time environments and these too add their own twists. Fortunately for Perl, there's basically the one implementation — if you don't count Perl 6. If you do count Perl 6, a big difference I think is that the parrot interpreter is instruction based while the Perl 5 interpreter is tree-based.

But let me take an example from Ruby where I have more experience. There is a Ruby implementation where the run-time environment is written mostly written in C. This implementation is called MRI. There is another implementation that is written mostly in Ruby, called Rubinius. Imagine in Perl if most of the built-in functions like shift(), substr(), grep(), and even many of the "operators" like hash/array indexing, and slicing were written in Perl. That sort of gives you a feel of Rubinius. MRI, has callbacks to the debugger/tracer when you are entering and leaving a C function, but of course not stepping inside the C function. Rubinius on the other hand allows C extensions through FFI and tracing/debugging doesn't show these foreign calls.

Now consider now the difference in debugging the same Ruby program in MRI versus Rubinius. Since many primitives of the MRI implementation are written in Ruby in Rubinius, single stepping in Rubinius is more tedious. Consequently, in a debugger for Rubinius it is more imperative to provide mechanisms to make stepping less cumbersome where "step over" instead of "step in" is more used.

It turns out though that improvements in ways to step is beneficial on other Ruby's as well as Perl.

A discussion about stepping may too one day be a blog entry. But it also is one area where I think it may be useful to extend what is available in gdb. To be fair, gdb has also fairly recently extended its step command. And so, sometimes in the past it has happened I have not followed gdb only to later change things more towards gdb when it catches up.


Leave a comment

About rockyb

user-pic I blog about Perl.