Annotation of 42BSD/ucb/gprof/gprof.c, revision 1.1.1.1

1.1       root        1: #ifndef lint
                      2:     static     char *sccsid = "@(#)gprof.c     1.22 (Berkeley) 5/11/83";
                      3: #endif lint
                      4: 
                      5: #include "gprof.h"
                      6: 
                      7: char   *whoami = "gprof";
                      8: 
                      9:     /*
                     10:      * things which get -E excluded by default.
                     11:      */
                     12: char   *defaultEs[] = { "mcount" , "__mcleanup" , 0 };
                     13: 
                     14: main(argc, argv)
                     15:     int argc;
                     16:     char **argv;
                     17: {
                     18:     char       **sp;
                     19: 
                     20:     --argc;
                     21:     argv++;
                     22:     debug = 0;
                     23:     bflag = TRUE;
                     24:     while ( *argv != 0 && **argv == '-' ) {
                     25:        (*argv)++;
                     26:        switch ( **argv ) {
                     27:        case 'a':
                     28:            aflag = TRUE;
                     29:            break;
                     30:        case 'b':
                     31:            bflag = FALSE;
                     32:            break;
                     33:        case 'c':
                     34:            cflag = TRUE;
                     35:            break;
                     36:        case 'd':
                     37:            dflag = TRUE;
                     38:            (*argv)++;
                     39:            debug |= atoi( *argv );
                     40:            debug |= ANYDEBUG;
                     41: #          ifdef DEBUG
                     42:                printf("[main] debug = %d\n", debug);
                     43: #          else not DEBUG
                     44:                printf("%s: -d ignored\n", whoami);
                     45: #          endif DEBUG
                     46:            break;
                     47:        case 'E':
                     48:            ++argv;
                     49:            addlist( Elist , *argv );
                     50:            Eflag = TRUE;
                     51:            addlist( elist , *argv );
                     52:            eflag = TRUE;
                     53:            break;
                     54:        case 'e':
                     55:            addlist( elist , *++argv );
                     56:            eflag = TRUE;
                     57:            break;
                     58:        case 'F':
                     59:            ++argv;
                     60:            addlist( Flist , *argv );
                     61:            Fflag = TRUE;
                     62:            addlist( flist , *argv );
                     63:            fflag = TRUE;
                     64:            break;
                     65:        case 'f':
                     66:            addlist( flist , *++argv );
                     67:            fflag = TRUE;
                     68:            break;
                     69:        case 's':
                     70:            sflag = TRUE;
                     71:            break;
                     72:        case 'z':
                     73:            zflag = TRUE;
                     74:            break;
                     75:        }
                     76:        argv++;
                     77:     }
                     78:     if ( *argv != 0 ) {
                     79:        a_outname  = *argv;
                     80:        argv++;
                     81:     } else {
                     82:        a_outname  = A_OUTNAME;
                     83:     }
                     84:     if ( *argv != 0 ) {
                     85:        gmonname = *argv;
                     86:        argv++;
                     87:     } else {
                     88:        gmonname = GMONNAME;
                     89:     }
                     90:        /*
                     91:         *      turn off default functions
                     92:         */
                     93:     for ( sp = &defaultEs[0] ; *sp ; sp++ ) {
                     94:        Eflag = TRUE;
                     95:        addlist( Elist , *sp );
                     96:        eflag = TRUE;
                     97:        addlist( elist , *sp );
                     98:     }
                     99:        /*
                    100:         *      how long is a clock tick?
                    101:         */
                    102:     hz = hertz();
                    103:        /*
                    104:         *      get information about a.out file.
                    105:         */
                    106:     getnfile();
                    107:        /*
                    108:         *      get information about mon.out file(s).
                    109:         */
                    110:     do {
                    111:        getpfile( gmonname );
                    112:        if ( *argv != 0 ) {
                    113:            gmonname = *argv;
                    114:        }
                    115:     } while ( *argv++ != 0 );
                    116:        /*
                    117:         *      dump out a gmon.sum file if requested
                    118:         */
                    119:     if ( sflag ) {
                    120:        dumpsum( GMONSUM );
                    121:     }
                    122:        /*
                    123:         *      assign samples to procedures
                    124:         */
                    125:     asgnsamples();
                    126:        /*
                    127:         *      print the usual profile
                    128:         */
                    129:     printprof();       
                    130:        /*
                    131:         *      assemble and print the dynamic profile
                    132:         */
                    133:     doarcs();
                    134:     done();
                    135: }
                    136: 
                    137:     /*
                    138:      * Set up string and symbol tables from a.out.
                    139:      * and optionally the text space.
                    140:      * On return symbol table is sorted by value.
                    141:      */
                    142: getnfile()
                    143: {
                    144:     FILE       *nfile;
                    145: 
                    146:     nfile = fopen( a_outname ,"r");
                    147:     if (nfile == NULL) {
                    148:        perror( a_outname );
                    149:        done();
                    150:     }
                    151:     fread(&xbuf, 1, sizeof(xbuf), nfile);
                    152:     if (N_BADMAG(xbuf)) {
                    153:        fprintf(stderr, "%s: %s: bad format\n", whoami , a_outname );
                    154:        done();
                    155:     }
                    156:     getstrtab(nfile);
                    157:     getsymtab(nfile);
                    158:     gettextspace( nfile );
                    159:     qsort(nl, nname, sizeof(nltype), valcmp);
                    160:     fclose(nfile);
                    161: #   ifdef DEBUG
                    162:        if ( debug & AOUTDEBUG ) {
                    163:            register int j;
                    164: 
                    165:            for (j = 0; j < nname; j++){
                    166:                printf("[getnfile] 0X%08x\t%s\n", nl[j].value, nl[j].name);
                    167:            }
                    168:        }
                    169: #   endif DEBUG
                    170: }
                    171: 
                    172: getstrtab(nfile)
                    173:     FILE       *nfile;
                    174: {
                    175: 
                    176:     fseek(nfile, (long)(N_SYMOFF(xbuf) + xbuf.a_syms), 0);
                    177:     if (fread(&ssiz, sizeof (ssiz), 1, nfile) == 0) {
                    178:        fprintf(stderr, "%s: %s: no string table (old format?)\n" ,
                    179:                whoami , a_outname );
                    180:        done();
                    181:     }
                    182:     strtab = (char *)calloc(ssiz, 1);
                    183:     if (strtab == NULL) {
                    184:        fprintf(stderr, "%s: %s: no room for %d bytes of string table",
                    185:                whoami , a_outname , ssiz);
                    186:        done();
                    187:     }
                    188:     if (fread(strtab+sizeof(ssiz), ssiz-sizeof(ssiz), 1, nfile) != 1) {
                    189:        fprintf(stderr, "%s: %s: error reading string table\n",
                    190:                whoami , a_outname );
                    191:        done();
                    192:     }
                    193: }
                    194: 
                    195:     /*
                    196:      * Read in symbol table
                    197:      */
                    198: getsymtab(nfile)
                    199:     FILE       *nfile;
                    200: {
                    201:     register long      i;
                    202:     int                        askfor;
                    203:     struct nlist       nbuf;
                    204: 
                    205:     /* pass1 - count symbols */
                    206:     fseek(nfile, (long)N_SYMOFF(xbuf), 0);
                    207:     nname = 0;
                    208:     for (i = xbuf.a_syms; i > 0; i -= sizeof(struct nlist)) {
                    209:        fread(&nbuf, sizeof(nbuf), 1, nfile);
                    210:        if ( ! funcsymbol( &nbuf ) ) {
                    211:            continue;
                    212:        }
                    213:        nname++;
                    214:     }
                    215:     if (nname == 0) {
                    216:        fprintf(stderr, "%s: %s: no symbols\n", whoami , a_outname );
                    217:        done();
                    218:     }
                    219:     askfor = nname + 1;
                    220:     nl = (nltype *) calloc( askfor , sizeof(nltype) );
                    221:     if (nl == 0) {
                    222:        fprintf(stderr, "%s: No room for %d bytes of symbol table\n",
                    223:                whoami, askfor * sizeof(nltype) );
                    224:        done();
                    225:     }
                    226: 
                    227:     /* pass2 - read symbols */
                    228:     fseek(nfile, (long)N_SYMOFF(xbuf), 0);
                    229:     npe = nl;
                    230:     nname = 0;
                    231:     for (i = xbuf.a_syms; i > 0; i -= sizeof(struct nlist)) {
                    232:        fread(&nbuf, sizeof(nbuf), 1, nfile);
                    233:        if ( ! funcsymbol( &nbuf ) ) {
                    234: #          ifdef DEBUG
                    235:                if ( debug & AOUTDEBUG ) {
                    236:                    printf( "[getsymtab] rejecting: 0x%x %s\n" ,
                    237:                            nbuf.n_type , strtab + nbuf.n_un.n_strx );
                    238:                }
                    239: #          endif DEBUG
                    240:            continue;
                    241:        }
                    242:        npe->value = nbuf.n_value;
                    243:        npe->name = strtab+nbuf.n_un.n_strx;
                    244: #      ifdef DEBUG
                    245:            if ( debug & AOUTDEBUG ) {
                    246:                printf( "[getsymtab] %d %s 0x%08x\n" ,
                    247:                        nname , npe -> name , npe -> value );
                    248:            }
                    249: #      endif DEBUG
                    250:        npe++;
                    251:        nname++;
                    252:     }
                    253:     npe->value = -1;
                    254: }
                    255: 
                    256:     /*
                    257:      * read in the text space of an a.out file
                    258:      */
                    259: gettextspace( nfile )
                    260:     FILE       *nfile;
                    261: {
                    262:     unsigned char      *malloc();
                    263:     
                    264:     if ( cflag == 0 ) {
                    265:        return;
                    266:     }
                    267:     textspace = malloc( xbuf.a_text );
                    268:     if ( textspace == 0 ) {
                    269:        fprintf( stderr , "%s: ran out room for %d bytes of text space:  " ,
                    270:                        whoami , xbuf.a_text );
                    271:        fprintf( stderr , "can't do -c\n" );
                    272:        return;
                    273:     }
                    274:     (void) fseek( nfile , N_TXTOFF( xbuf ) , 0 );
                    275:     if ( fread( textspace , 1 , xbuf.a_text , nfile ) != xbuf.a_text ) {
                    276:        fprintf( stderr , "%s: couldn't read text space:  " , whoami );
                    277:        fprintf( stderr , "can't do -c\n" );
                    278:        free( textspace );
                    279:        textspace = 0;
                    280:        return;
                    281:     }
                    282: }
                    283:     /*
                    284:      * information from a gmon.out file is in two parts:
                    285:      * an array of sampling hits within pc ranges,
                    286:      * and the arcs.
                    287:      */
                    288: getpfile(filename)
                    289:     char *filename;
                    290: {
                    291:     FILE               *pfile;
                    292:     FILE               *openpfile();
                    293:     struct rawarc      arc;
                    294: 
                    295:     pfile = openpfile(filename);
                    296:     readsamples(pfile);
                    297:        /*
                    298:         *      the rest of the file consists of
                    299:         *      a bunch of <from,self,count> tuples.
                    300:         */
                    301:     while ( fread( &arc , sizeof arc , 1 , pfile ) == 1 ) {
                    302: #      ifdef DEBUG
                    303:            if ( debug & SAMPLEDEBUG ) {
                    304:                printf( "[getpfile] frompc 0x%x selfpc 0x%x count %d\n" ,
                    305:                        arc.raw_frompc , arc.raw_selfpc , arc.raw_count );
                    306:            }
                    307: #      endif DEBUG
                    308:            /*
                    309:             *  add this arc
                    310:             */
                    311:        tally( &arc );
                    312:     }
                    313:     fclose(pfile);
                    314: }
                    315: 
                    316: FILE *
                    317: openpfile(filename)
                    318:     char *filename;
                    319: {
                    320:     struct hdr tmp;
                    321:     FILE       *pfile;
                    322: 
                    323:     if((pfile = fopen(filename, "r")) == NULL) {
                    324:        perror(filename);
                    325:        done();
                    326:     }
                    327:     fread(&tmp, sizeof(struct hdr), 1, pfile);
                    328:     if ( s_highpc != 0 && ( tmp.lowpc != h.lowpc ||
                    329:         tmp.highpc != h.highpc || tmp.ncnt != h.ncnt ) ) {
                    330:        fprintf(stderr, "%s: incompatible with first gmon file\n", filename);
                    331:        done();
                    332:     }
                    333:     h = tmp;
                    334:     s_lowpc = (unsigned long) h.lowpc;
                    335:     s_highpc = (unsigned long) h.highpc;
                    336:     lowpc = (unsigned long)h.lowpc / sizeof(UNIT);
                    337:     highpc = (unsigned long)h.highpc / sizeof(UNIT);
                    338:     sampbytes = h.ncnt - sizeof(struct hdr);
                    339:     nsamples = sampbytes / sizeof (unsigned UNIT);
                    340: #   ifdef DEBUG
                    341:        if ( debug & SAMPLEDEBUG ) {
                    342:            printf( "[openpfile] hdr.lowpc 0x%x hdr.highpc 0x%x hdr.ncnt %d\n",
                    343:                h.lowpc , h.highpc , h.ncnt );
                    344:            printf( "[openpfile]   s_lowpc 0x%x   s_highpc 0x%x\n" ,
                    345:                s_lowpc , s_highpc );
                    346:            printf( "[openpfile]     lowpc 0x%x     highpc 0x%x\n" ,
                    347:                lowpc , highpc );
                    348:            printf( "[openpfile] sampbytes %d nsamples %d\n" ,
                    349:                sampbytes , nsamples );
                    350:        }
                    351: #   endif DEBUG
                    352:     return(pfile);
                    353: }
                    354: 
                    355: tally( rawp )
                    356:     struct rawarc      *rawp;
                    357: {
                    358:     nltype             *parentp;
                    359:     nltype             *childp;
                    360: 
                    361:     parentp = nllookup( rawp -> raw_frompc );
                    362:     childp = nllookup( rawp -> raw_selfpc );
                    363:     childp -> ncall += rawp -> raw_count;
                    364: #   ifdef DEBUG
                    365:        if ( debug & TALLYDEBUG ) {
                    366:            printf( "[tally] arc from %s to %s traversed %d times\n" ,
                    367:                    parentp -> name , childp -> name , rawp -> raw_count );
                    368:        }
                    369: #   endif DEBUG
                    370:     addarc( parentp , childp , rawp -> raw_count );
                    371: }
                    372: 
                    373: /*
                    374:  * dump out the gmon.sum file
                    375:  */
                    376: dumpsum( sumfile )
                    377:     char *sumfile;
                    378: {
                    379:     register nltype *nlp;
                    380:     register arctype *arcp;
                    381:     struct rawarc arc;
                    382:     FILE *sfile;
                    383: 
                    384:     if ( ( sfile = fopen ( sumfile , "w" ) ) == NULL ) {
                    385:        perror( sumfile );
                    386:        done();
                    387:     }
                    388:     /*
                    389:      * dump the header; use the last header read in
                    390:      */
                    391:     if ( fwrite( &h , sizeof h , 1 , sfile ) != 1 ) {
                    392:        perror( sumfile );
                    393:        done();
                    394:     }
                    395:     /*
                    396:      * dump the samples
                    397:      */
                    398:     if (fwrite(samples, sizeof(unsigned UNIT), nsamples, sfile) != nsamples) {
                    399:        perror( sumfile );
                    400:        done();
                    401:     }
                    402:     /*
                    403:      * dump the normalized raw arc information
                    404:      */
                    405:     for ( nlp = nl ; nlp < npe ; nlp++ ) {
                    406:        for ( arcp = nlp -> children ; arcp ; arcp = arcp -> arc_childlist ) {
                    407:            arc.raw_frompc = arcp -> arc_parentp -> value;
                    408:            arc.raw_selfpc = arcp -> arc_childp -> value;
                    409:            arc.raw_count = arcp -> arc_count;
                    410:            if ( fwrite ( &arc , sizeof arc , 1 , sfile ) != 1 ) {
                    411:                perror( sumfile );
                    412:                done();
                    413:            }
                    414: #          ifdef DEBUG
                    415:                if ( debug & SAMPLEDEBUG ) {
                    416:                    printf( "[dumpsum] frompc 0x%x selfpc 0x%x count %d\n" ,
                    417:                            arc.raw_frompc , arc.raw_selfpc , arc.raw_count );
                    418:                }
                    419: #          endif DEBUG
                    420:        }
                    421:     }
                    422:     fclose( sfile );
                    423: }
                    424: 
                    425: valcmp(p1, p2)
                    426:     nltype *p1, *p2;
                    427: {
                    428:     if ( p1 -> value < p2 -> value ) {
                    429:        return LESSTHAN;
                    430:     }
                    431:     if ( p1 -> value > p2 -> value ) {
                    432:        return GREATERTHAN;
                    433:     }
                    434:     return EQUALTO;
                    435: }
                    436: 
                    437: readsamples(pfile)
                    438:     FILE       *pfile;
                    439: {
                    440:     register i;
                    441:     unsigned UNIT      sample;
                    442:     
                    443:     if (samples == 0) {
                    444:        samples = (unsigned UNIT *) calloc(sampbytes, sizeof (unsigned UNIT));
                    445:        if (samples == 0) {
                    446:            fprintf( stderr , "%s: No room for %d sample pc's\n", 
                    447:                whoami , sampbytes / sizeof (unsigned UNIT));
                    448:            done();
                    449:        }
                    450:     }
                    451:     for (i = 0; i < nsamples; i++) {
                    452:        fread(&sample, sizeof (unsigned UNIT), 1, pfile);
                    453:        if (feof(pfile))
                    454:                break;
                    455:        samples[i] += sample;
                    456:     }
                    457:     if (i != nsamples) {
                    458:        fprintf(stderr,
                    459:            "%s: unexpected EOF after reading %d/%d samples\n",
                    460:                whoami , --i , nsamples );
                    461:        done();
                    462:     }
                    463: }
                    464: 
                    465: /*
                    466:  *     Assign samples to the procedures to which they belong.
                    467:  *
                    468:  *     There are three cases as to where pcl and pch can be
                    469:  *     with respect to the routine entry addresses svalue0 and svalue1
                    470:  *     as shown in the following diagram.  overlap computes the
                    471:  *     distance between the arrows, the fraction of the sample
                    472:  *     that is to be credited to the routine which starts at svalue0.
                    473:  *
                    474:  *         svalue0                                         svalue1
                    475:  *            |                                               |
                    476:  *            v                                               v
                    477:  *
                    478:  *            +-----------------------------------------------+
                    479:  *            |                                               |
                    480:  *       |  ->|    |<-         ->|         |<-         ->|    |<-  |
                    481:  *       |         |             |         |             |         |
                    482:  *       +---------+             +---------+             +---------+
                    483:  *
                    484:  *       ^         ^             ^         ^             ^         ^
                    485:  *       |         |             |         |             |         |
                    486:  *      pcl       pch           pcl       pch           pcl       pch
                    487:  *
                    488:  *     For the vax we assert that samples will never fall in the first
                    489:  *     two bytes of any routine, since that is the entry mask,
                    490:  *     thus we give call alignentries() to adjust the entry points if
                    491:  *     the entry mask falls in one bucket but the code for the routine
                    492:  *     doesn't start until the next bucket.  In conjunction with the
                    493:  *     alignment of routine addresses, this should allow us to have
                    494:  *     only one sample for every four bytes of text space and never
                    495:  *     have any overlap (the two end cases, above).
                    496:  */
                    497: asgnsamples()
                    498: {
                    499:     register int       j;
                    500:     unsigned UNIT      ccnt;
                    501:     double             time;
                    502:     unsigned long      pcl, pch;
                    503:     register int       i;
                    504:     unsigned long      overlap;
                    505:     unsigned long      svalue0, svalue1;
                    506: 
                    507:     /* read samples and assign to namelist symbols */
                    508:     scale = highpc - lowpc;
                    509:     scale /= nsamples;
                    510:     alignentries();
                    511:     for (i = 0, j = 1; i < nsamples; i++) {
                    512:        ccnt = samples[i];
                    513:        if (ccnt == 0)
                    514:                continue;
                    515:        pcl = lowpc + scale * i;
                    516:        pch = lowpc + scale * (i + 1);
                    517:        time = ccnt;
                    518: #      ifdef DEBUG
                    519:            if ( debug & SAMPLEDEBUG ) {
                    520:                printf( "[asgnsamples] pcl 0x%x pch 0x%x ccnt %d\n" ,
                    521:                        pcl , pch , ccnt );
                    522:            }
                    523: #      endif DEBUG
                    524:        totime += time;
                    525:        for (j = j - 1; j < nname; j++) {
                    526:            svalue0 = nl[j].svalue;
                    527:            svalue1 = nl[j+1].svalue;
                    528:                /*
                    529:                 *      if high end of tick is below entry address, 
                    530:                 *      go for next tick.
                    531:                 */
                    532:            if (pch < svalue0)
                    533:                    break;
                    534:                /*
                    535:                 *      if low end of tick into next routine,
                    536:                 *      go for next routine.
                    537:                 */
                    538:            if (pcl >= svalue1)
                    539:                    continue;
                    540:            overlap = min(pch, svalue1) - max(pcl, svalue0);
                    541:            if (overlap > 0) {
                    542: #              ifdef DEBUG
                    543:                    if (debug & SAMPLEDEBUG) {
                    544:                        printf("[asgnsamples] (0x%x->0x%x-0x%x) %s gets %f ticks %d overlap\n",
                    545:                                nl[j].value/sizeof(UNIT), svalue0, svalue1,
                    546:                                nl[j].name, 
                    547:                                overlap * time / scale, overlap);
                    548:                    }
                    549: #              endif DEBUG
                    550:                nl[j].time += overlap * time / scale;
                    551:            }
                    552:        }
                    553:     }
                    554: #   ifdef DEBUG
                    555:        if (debug & SAMPLEDEBUG) {
                    556:            printf("[asgnsamples] totime %f\n", totime);
                    557:        }
                    558: #   endif DEBUG
                    559: }
                    560: 
                    561: 
                    562: unsigned long
                    563: min(a, b)
                    564:     unsigned long a,b;
                    565: {
                    566:     if (a<b)
                    567:        return(a);
                    568:     return(b);
                    569: }
                    570: 
                    571: unsigned long
                    572: max(a, b)
                    573:     unsigned long a,b;
                    574: {
                    575:     if (a>b)
                    576:        return(a);
                    577:     return(b);
                    578: }
                    579: 
                    580:     /*
                    581:      * calculate scaled entry point addresses (to save time in asgnsamples),
                    582:      * and possibly push the scaled entry points over the entry mask,
                    583:      * if it turns out that the entry point is in one bucket and the code
                    584:      * for a routine is in the next bucket.
                    585:      */
                    586: alignentries()
                    587: {
                    588:     register struct nl *nlp;
                    589:     unsigned long      bucket_of_entry;
                    590:     unsigned long      bucket_of_code;
                    591: 
                    592:     for (nlp = nl; nlp < npe; nlp++) {
                    593:        nlp -> svalue = nlp -> value / sizeof(UNIT);
                    594:        bucket_of_entry = (nlp->svalue - lowpc) / scale;
                    595:        bucket_of_code = (nlp->svalue + UNITS_TO_CODE - lowpc) / scale;
                    596:        if (bucket_of_entry < bucket_of_code) {
                    597: #          ifdef DEBUG
                    598:                if (debug & SAMPLEDEBUG) {
                    599:                    printf("[alignentries] pushing svalue 0x%x to 0x%x\n",
                    600:                            nlp->svalue, nlp->svalue + UNITS_TO_CODE);
                    601:                }
                    602: #          endif DEBUG
                    603:            nlp->svalue += UNITS_TO_CODE;
                    604:        }
                    605:     }
                    606: }
                    607: 
                    608: bool
                    609: funcsymbol( nlistp )
                    610:     struct nlist       *nlistp;
                    611: {
                    612:     extern char        *strtab;        /* string table from a.out */
                    613:     extern int aflag;          /* if static functions aren't desired */
                    614:     char       *name;
                    615: 
                    616:        /*
                    617:         *      must be a text symbol,
                    618:         *      and static text symbols don't qualify if aflag set.
                    619:         */
                    620:     if ( ! (  ( nlistp -> n_type == ( N_TEXT | N_EXT ) )
                    621:           || ( ( nlistp -> n_type == N_TEXT ) && ( aflag == 0 ) ) ) ) {
                    622:        return FALSE;
                    623:     }
                    624:        /*
                    625:         *      can't have any `funny' characters in name,
                    626:         *      where `funny' includes  `.', .o file names
                    627:         *                      and     `$', pascal labels.
                    628:         */
                    629:     for ( name = strtab + nlistp -> n_un.n_strx ; *name ; name += 1 ) {
                    630:        if ( *name == '.' || *name == '$' ) {
                    631:            return FALSE;
                    632:        }
                    633:     }
                    634:     return TRUE;
                    635: }
                    636: 
                    637: done()
                    638: {
                    639: 
                    640:     exit(0);
                    641: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.