Reflections on OS integration Eric Schrock's Weblog
Musings about Fishworks, Operating Systems, and the software that runs on them.

Thursday Jul 01, 2004

In a departure from my usual Solaris propaganda, I thought I'd try a little bit of history. This entry is aimed at all of you C programmers out there that enjoy the novelty of Obfuscated C. If you think you're a real C hacker, and haven't heard of the obfuscated C contenst, then you need to spend a few hours browsing their archives of past winners1.

If you've been reading manpages on your UNIX system, you've probably been using some form of troff2. This is an early typesetting language processor, dating back to pre-UNIX days. You can find some history here. The nroff and troff commands are essentially the same; they are built largely from the same source and differ only in their options and output formats.

The original troff was written by Joe F. Ossanna in assembly language for the PDP-11 in the early 70s. Along came this whizzy portable language known as C, so Ossana rewrote his formatting program. However, it was less of a rewrite and more of a direct translation of the assembly code. The result is a truly incomprehensible tangle of C code, almost completely uncommented. To top it off, Ossana was tragically killed in a car accident in 1977. Rumour has it that attempts were made to enhance troff, before Brian Kernighan caved in and rewrote it from scratch as ditroff.

If you're curious just how incomprehensible 7000 lines of uncommented C code can be, you can find a later version of it from The Unix Tree, an invaluable resource for the nostalgic among us. To begin with, the files are named n1.c, n2.c, etc. To quote from 'n6.c':

setch(){
	register i,*j,k;
	extern int chtab[];

	if((i = getrq()) == 0)return(0);
	for(j=chtab;*j != i;j++)if(*(j++) == 0)return(0);
	k = *(++j) | chbits;
	return(k);
}
find(i,j)
int i,j[];
{
	register k;

	if(((k = i-'0') >= 1) && (k <= 4) && (k != smnt))return(--k);
	for(k=0; j[k] != i; k++)if(j[k] == 0)return(-1);
	return(k);
}

If this doesn't convince you to write well-structured, well-commented code, I don't know what will. The scary thing is that there are at least 18 bugs in our database open against nroff or troff; one of the side-effects of promising full backwards compatibility. Anyone who has the courage to putback nroff changes earns a badge of honor here - it is a dark place that has claimed the free time of a few brave programmers3. Whenever an open bug report includes such choice phrases as this, you know you're in trouble:

I've seen this problem on non-Sun Unix as well, like Ultrix 3.1 so the problem likely came from Berkeley. The System V version of *roff (ditroff ?) doesn't have this problem.


1One of my personal favorites is this little gem, a 2000 winner 'natori'. It should be a full moon tomorrow night...

#include <stdio.h>
#include <math.h>
double l;main(_,o,O){return putchar((_--+22&&_+44&&main(_,-43,_),_&&o)?(main(-43,
++o,O),((l=(o+21)/sqrt(3-O*22-O*O),l*l<4&&(fabs(((time(0)-607728)%2551443)/
405859.-4.7+acos(l/2))<1.57))[" #"])):10);}

2On Solaris, most manpages are written in SGML, and can be found in /usr/share/man/sman*.

3I'd like to think that the x86 disassembler is a close second, but maybe that's just because I'm a survivor.

During S10 development, there have been numerous enhancements to the ptools (see proc(1)). Here are two recent additions that may have slipped through the cracks with all the hype surrounding Solaris 10. They're not quite as ground breaking as DTrace or Zones, but well-suited for some blog exposure.

pargs -l

The pargs command has a new option to display the command and all its arguments on a single line. This makes it possible to cut and paste to restart running commands with the same set of arguments.

$ pargs -l `pgrep sleep`
/usr/bin/sleep 10 here are some args
$

java support in pstack

This one won't hit the streets until build 59, which is due out as the next Solaris Express build. Thanks to the JVM guys, we've added support for pstack to display java frames. If you're using the latest java release (1.5, err... 5.0) and you run pstack on a java process, you'll get to see all the java functions, including line numbers. Note the java frames with an asterisk in the example below.

$ cat Main.java
public class Main {
        
        public static int go(int a) {

                if (a == 0) {
                        for (;;)
                                continue;
                }

                return (1 + go(a - 1));
        }

        public static void main(String[] argv) {
                System.out.println("running...");
                go(10);
        }
}
$ pstack `pgrep java`
144381: /usr/jdk/instances/jdk1.5.0/bin/java Main
-----------------  lwp# 1 / thread# 1  --------------------
 9940dfae * Main.go(I)I+0
 99402a3f * Main.go(I)I+11 (line 10)
 99402a3f * Main.go(I)I+11 (line 10)
 99402a3f * Main.go(I)I+11 (line 10)
 99402a3f * Main.go(I)I+11 (line 10)
 99402a3f * Main.go(I)I+11 (line 10)
 99402a3f * Main.go(I)I+11 (line 10)
 99402a3f * Main.go(I)I+11 (line 10)
 99402a3f * Main.go(I)I+11 (line 10)
 99402a3f * Main.go(I)I+11 (line 10)
 99402a3f * Main.go(I)I+11 (line 10)
 99402a3f * Main.main([Ljava/lang/String;)V+10 (line 15)
 9f4dbbe4 * StubRoutines (1)
 9f4dbbe4 __1cCosUos_exception_wrapper6FpFpnJJavaValue_pnMmethodHandle_pnRJavaCa
llArguments_pnGThread__v2468_v_ (8047130, 8047038, 8047068, 8074538, 804702c, 9f
4dbee8) + 14
 9f4dbbe4 __1cCosUos_exception_wrapper6FpFpnJJavaValue_pnMmethodHandle_pnRJavaCa
llArguments_pnGThread__v2468_v_ (9f4dbc90, 8047130, 8047038, 8047068, 8074538) +
 14
 9f4dbee8 __1cJJavaCallsEcall6FpnJJavaValue_nMmethodHandle_pnRJavaCallArguments_
pnGThread__v_ (8047130, 8074ac4, 8047068, 8074538) + 28
 9f6ee200 __1cRjni_invoke_static6FpnHJNIEnv__pnJJavaValue_pnI_jobject_nLJNICallT
ype_pnK_jmethodID_pnSJNI_ArgumentPusher_pnGThread__v_ (80745f4, 8047130, 0, 0, 8
072681, 804713c) + 180
 9f59f7af jni_CallStaticVoidMethod (80745f4, 80750a0, 8072681, 80750b0) + 10f
 080526ee main     (0, 806fbf8, 8047a04) + a4c
 08051c0a ???????? (2, 8047ae0, 8047b05, 0, 8047b0a, 8047b31) + 8051c0a
-----------------  lwp# 2 / thread# 2  --------------------
 9fb53e3c lwp_cond_wait (8160740, 8160728, 0, 0)
 9f4b6182 __1cHMonitorEwait6Mil_i_ (81208c8, 1, 0) + 432
 9f6bfcad __1cNGCTaskManagerIget_task6MI_pnGGCTask__ (81606c0, 0) + 90

[ ... ]