The “.” variable evaluates to the input address used when applying the macro, and the “+” variable evaluates to that input address incremented by the byte count of all preceding format characters. The macros were then maintained with the kernel source code.
More than a decade later, in 1997, I was working at Sun on what would become Solaris 7. This release was our first 64-bit kernel, but the kernel-debugging tool of choice was still adb just as it was in 1984, and our source base now contained hundreds of useful macro files. Unfortunately, the implementation of adb was essentially impossible to port cleanly from 32-bit to 64-bit to debug the new kernel, so it seemed the time was ripe for the development of a new clean code base with many more modern debugger features.
As I considered how best to approach the problem, I was struck by the fact that despite its brittle, unstructured code base, the key feature of adb was that its syntax was imbued deeply in the minds and behaviors of all of our most experienced and effective engineers. (As someone aptly put it at the time: “It’s in the fingers.”) So I set out to build a new modular debugger (mdb) that would support an API for advanced kernel debugging and other modern features, yet would remain precisely backward-compatible with existing syntax and macros. Sophisticated new features were added after a new prefix (“::”) so they would not break the existing syntax (for example, “::findleaks” to check for kernel memory leaks). The entire syntax was at last properly encoded as a yacc parser. Macro files were phased out in favor of compiler-generated debug information, but the “$<” syntax was left as an alias. Another decade later, mdb remains the standard tool for postmortem debugging of the OpenSolaris kernel and has been extended by hundreds of programmers.
The debugger tale illustrates that a little purpose-built language can evolve essentially at random, have no clear design, no consistent grammar or parser, and no name, and yet endure and grow in shipping operating systems for more than 40 years. In the same time period, many mainstream
figure 5: Debugging the “proc” structure on SunoS 4, circa 1984.
address $<proc
./”link”16t”rlink”16t”nxt”16t”prev”nXXXX +/”as”16t”segu”16t”stack”16t”uarea”nXXXX +/”upri”
+/”upri”8t”pri”8t”cpu”8t”stat”8t”time”8t”nice”nbbbbbb +/”slp”8t”cursig”16t”sig”bbX +/”mask”16t”ignore”16t”catch”nXXX +/”flag”16t”uid”8t”suid”8t”pgrp”nXddd +/”pid”8t”ppid”8t”xstat”8t”ticks”nddxd +/”cred”16t”ru”16t”tsize”nXXX +/”dsize”16t”ssize”16t”rssize”nXXX +/”maxrss”16t”swrss”16t”wchan”nXXX +/16+”%cpu”16t”pptr”16t”tptr”nXXX +/”real itimer”n4D +/”idhash”16t”swlocks”ndd +/”aio forw”16t”aio back”8t”aio count”8t”threadcnt”nXXXX
figure 6: fortran and Ratfor, circa 1975.
if (a .gt. b) goto 10
sw = 0 write( 6, 1) a, b goto 20
10 sw = 1 write( 6, 1) b, a
20 …
if (a <= b) { sw = 0;
write( 6, 1) a, b } else { sw = 1;
write( 6, 1) b, a
}
figure 7: c and Algol mutant from early “adb”.
#define IF if(
#define THEN ){
#define ELSE } else {
#define FI }
#define ANDF &&
...
localsym(cframe)
L _ INT cframe;
{
INT symflg;
WHILE nextsym() ANDF localok ANDF symbol.symc[0]!=’~’ ANDF ( symflg=symbol.symf)!=037
DO IF symflg>= 2 ANDF symflg<= 4
THEN localval=symbol.symv;
return(TRUE);
ELIF symflg== 1
THEN localval=leng(shorten(cframe) +symbol.symv);
return(TRUE);
ELIF symflg== 20 ANDF lastframe
THEN localval=leng( lastframe+2*symbol.symv- 10);
return(TRUE);
FI
OD return(FALSE);
}
languages came and went into the great beyond (Algol, Ada, Pascal, Cobol, and so on). Fundamentally, this debugger has survived for one reason: it concisely encoded the exact task its
users performed and thereby connected to those users. Take an address, dump out its content, find the next address, follow it to the next location of interest, dump out its content, and
APriL 2009 | voL. 53 | no. 4 | communicAtionS of the Acm
39
References:
Archives