home *** CD-ROM | disk | FTP | other *** search
/ GEMini Atari / GEMini_Atari_CD-ROM_Walnut_Creek_December_1993.iso / zip / gnu / mntlib16.lzh / MNTLIB16 / GMON.C < prev    next >
C/C++ Source or Header  |  1993-07-29  |  18KB  |  673 lines

  1. /*
  2.  * monitor(3), mcount() and profil(2) clones for gcc-Tos library
  3.  *
  4.  * Note: these routines need tuning up. they are very space
  5.  * inefficient. the implementation is totally biased towards support
  6.  * for gprof rather than prof (does anyone use prof anymore? why?)
  7.  *
  8.  *    ++jrb    bammi@dsrgsun.ces.cwru.edu
  9.  */
  10. #include <stddef.h>
  11. #include <memory.h>
  12. #include <unistd.h>
  13. #include <fcntl.h>
  14. #include <assert.h>
  15. #include <osbind.h>
  16. #include <basepage.h>
  17. #include <sysvars.h>
  18. #include <xbra.h>
  19.  
  20. /* gmon header */
  21. struct gm_header {
  22.     void        *low;        /* low pc  */
  23.     void        *high;        /* hi  pc  */
  24.     unsigned long    nbytes;        /* bytes in header + histo size */
  25. };
  26.  
  27. typedef unsigned short CHUNK; /* type of each histogram entry */
  28.  
  29. struct gm_call {    /* gm call record */
  30.     void    *from;    /* the caller                 */
  31.     void    *to;    /* the called function (callee)        */
  32.     unsigned long ncalls; /* # of calls from FROM to  TO    */
  33. };
  34.  
  35. #define    GMON_FILE    "gmon.out"    /* name of GMON file */
  36.  
  37. /* format of gmon file
  38.  *    gm_header
  39.  *    ((gm_header.nbytes - sizeof(gm_header))/sizeof(CHUNK)) histo entries
  40.  *    gm_call records upto EOF
  41.  */
  42.  
  43.  
  44. /* histogram variables and defines */
  45. #define    HIST_SCALE    2 /* text space scaled into size/HIST_SCALE  CHUNKS */
  46. #define HIST_SHIFT    1 /* HIST_SCALE == 2 ** HIST_SHIFT (assumption) */
  47.               /* 1 <= HIST_SHIFT <= 8          (assumption) */
  48.  
  49. static CHUNK *hist_buffer;    /* histogram buffer */
  50. static unsigned long hist_size; /* size of histogram in bytes */
  51.  
  52. /* call graph variables and defines */
  53. typedef struct  {    /* a to chain element */
  54.     void    *selfpc;    /* the callee's pc */
  55.     unsigned long count;    /* number of times called */
  56.     unsigned short link;    /* link to next in chain (an index) */
  57. } tostruct ;
  58.  
  59. tostruct       *tos;        /* pool of to chain elements */
  60. unsigned short *froms;        /* called from hash chain heads (an index) */
  61.  /* inherent assumption: typeof froms == typeof CHUNK, otherwise
  62.     change code in monstartup() */
  63.  
  64. #define MINARCS    64        /* min # of to's, a rand() # */
  65. #define ARCDENSITY 2        /* scaling of to's (as a % of  textsize) */
  66. #define HASHFRACTION 1        /* scaling of froms over textsize. 
  67.                    note this is very memory wasteful,
  68.                    but the alternatives are worse 
  69.                    beacuse of two reasons:
  70.                    - increase compute requirements(in mcount)
  71.                    - bsr, followed by bsr will loose!
  72.                    the coding of mcount below almost
  73.                    assumes that HASHFRACTION==1
  74.                    anyone else have some brilliant ideas?
  75.                    */
  76. #define HASH_SHIFT 0    /* HASHFRACTION = 2 ** HASH_SHIFT (assumption) */
  77.  
  78. /* housekeeping variables */
  79. static long          profiling;    /* profiling flag */
  80. static unsigned long  textsize;        /* size of profiled text area */
  81. static unsigned short tolimit;        /* max to == 65534, min == MINARCS */
  82. static unsigned short toalloc;        /* next free to record index  */
  83. static void           *s_lowpc;        /* low  pc rounded down to multiples
  84.                        of histo density =
  85.                        (CHUNK size * HIST_SCALE)
  86.                        (assumption: its mult of 2)
  87.                      */
  88.  
  89. #define USL(X)    ((unsigned long)(X))    /* cast X to unsigned long */
  90.  
  91. /* round X down to last multiple of Y */ /* see assumption above */
  92. #define ROUNDDOWN(X,Y)    ( USL(X) & (~(USL((Y)-1))) ) 
  93.  
  94. /* round X up to next multiple of Y */
  95. #define ROUNDUP(X,Y)    ( USL((X)+((Y)-1)) & (~(USL((Y)-1))) )
  96.  
  97. /* functions */
  98. #if __STDC__
  99. #define _P(x) x
  100. #else
  101. #define _P(x)
  102. #endif
  103.  
  104. void monstartup _P((void *lowpc, void *highpc));
  105. void monitor _P((void *lowpc, void *highpc, void *buffer,
  106.          unsigned long bufsize,  unsigned int nfunc));
  107. void moncontrol _P((long flag));
  108. void _mcleanup _P((void));
  109. int profil _P((void *buff, unsigned long bufsiz, unsigned long offset,
  110.            int shift));
  111. static void tick _P((void));
  112. static void term _P((void));
  113. static void install_handlers _P((void));
  114. static void remove_handlers _P((void));
  115. static void unlink_handler _P((xbra_struct *me, int exc));
  116. static void build_graph _P((void *caller, void *callee));
  117.  
  118. #undef _P
  119.  
  120. /*
  121.  * allocate space for histogram and call graph given the sampling
  122.  * range. call monitor to start up profiling.
  123.  */
  124.  
  125. void monstartup(lowpc, highpc)
  126. void *lowpc, *highpc;
  127. {
  128.     unsigned long    monsize; /* size of hist buffer + gm_header rounded */
  129.     void        *buf;    /* hist + gm_header space */
  130.     unsigned long    i;
  131.  
  132.     assert(USL(lowpc) < USL(highpc));
  133.  
  134. #if 0    /* dont define: screws up gmon because of reloc assumptions */
  135.     s_lowpc = lowpc = (void *)
  136.     (ROUNDDOWN(USL(lowpc), sizeof(CHUNK)<<HIST_SHIFT ));
  137. #else
  138.     s_lowpc = lowpc;
  139. #endif
  140.     highpc = (void *)
  141.     (ROUNDUP(USL(highpc), sizeof(CHUNK)<<HIST_SHIFT ));
  142.     textsize = USL(highpc) - USL(lowpc);
  143.  
  144.     /* allocate histogram buffer + gm_header buffer */
  145.     monsize = (textsize >> HIST_SHIFT) * sizeof(CHUNK) +
  146.            sizeof(struct gm_header);
  147.     monsize = ROUNDUP(monsize, sizeof(short));
  148.  
  149.     if((buf = (CHUNK *)malloc(monsize)) == (CHUNK *)0)
  150.     {
  151.     Cconws("monitor: No memory for histogram buffer\r\n");
  152.     froms = (unsigned short *)0;
  153.     tos = (tostruct *)0;
  154.  
  155.     return;
  156.     }
  157.  
  158.     /* allocate space for graph data structs */
  159.     i = (textsize>>HASH_SHIFT) * sizeof(*froms);
  160.     i = ROUNDUP(i, sizeof(long));
  161.     if((froms = (unsigned short *)malloc(i)) == (unsigned short *)0)
  162.     {
  163.     Cconws("monitor: No memory for FROMs\r\n");
  164.     free(buf);
  165.     tos = (tostruct *)0;
  166.     return;
  167.     }
  168.     bzero(froms, i);
  169.     
  170.     i = textsize * ARCDENSITY / 100;
  171.     if( i < MINARCS)
  172.     i = MINARCS;
  173.     else if ( i > 65534)
  174.     i = 65534;
  175.     tolimit = (unsigned short)i;
  176.     i = ROUNDUP(i*sizeof(tostruct), sizeof(long));
  177.     if((tos = (tostruct *)malloc(i)) == (tostruct *)0)
  178.     {
  179.     Cconws("monitor: No memory for TOs pool\r\n");
  180.     free(froms);
  181.     free(buf);
  182.     froms = (unsigned short *)0;
  183.     return;
  184.     }
  185.     bzero(tos, i);
  186.     toalloc = 0;    /* index of next available element in TOs pool */
  187.  
  188.     monitor(lowpc, highpc, buf, monsize, (unsigned int)tolimit);
  189. }
  190.  
  191.     
  192. /*
  193.  * monitor(3) interface to profil(2)
  194.  * last arg is silly and not used
  195.  */
  196. void monitor(lowpc, highpc, buffer, bufsize, nfunc)
  197. void *lowpc, *highpc, *buffer;
  198. unsigned long bufsize;
  199. unsigned int nfunc;
  200. {
  201.     struct gm_header *hdr;
  202.  
  203.     if(lowpc == 0)
  204.     { /* finished */
  205.         moncontrol(0L);
  206.         _mcleanup();
  207.         return;
  208.     }
  209.  
  210.     s_lowpc = lowpc;    /* in case user is calling */
  211.     /* initialize gm_header */
  212.     hdr = (struct gm_header *)buffer;
  213.     hdr->low = lowpc;
  214.     hdr->high = highpc;
  215.     hdr->nbytes = bufsize;
  216.  
  217.     hist_size = bufsize - sizeof(struct gm_header); /* sizof hist buffer */
  218.     hist_buffer = (CHUNK *)(USL(buffer) + sizeof(struct gm_header));
  219.  
  220.     /* integ. check, (user can call monitor) */
  221.     if((hist_size == 0) ||
  222.            (hist_size <
  223.         (((USL(highpc) - USL(lowpc))>>HIST_SHIFT)*sizeof(CHUNK))) )
  224.     {
  225.         return;
  226.     }
  227.     /* note: difference in scaling semantics from unix */
  228.     moncontrol(1L); /* begin */
  229. }
  230.  
  231. /*
  232.  * control profiling
  233.  */
  234. void moncontrol(flag)
  235. long flag;
  236. {
  237.     if(flag)
  238.     { /* start */
  239.     profil(hist_buffer, hist_size, (unsigned long)s_lowpc, HIST_SHIFT);
  240.     profiling = 0;
  241.     }
  242.     else
  243.     {
  244.     /* stop */
  245.     profil((void *)0, 0L, 0L, 0);
  246.     profiling = 3;
  247.     }
  248. }
  249.  
  250. /*
  251.  * mcount
  252.  *    called as a part of the entry prologue of a profiled function.
  253.  *    the function that calls mcount is the CALLEE, and the function
  254.  *    that called the CALLEE is the CALLER. mcount grabs the
  255.  *    address of the CALLEE and the address of the CALLER off the
  256.  *    stack, and then calls build_graph that incrementally
  257.  *     constructs the call graphs in the FROMs and TOs structures,
  258.  *    keeping track of the number of times the CALLER called CALLEE.
  259.  *    on entry the stack look like:
  260.  *
  261.  *        sp-> |    ret address of CALLEE    |
  262.  *             |--------------------------|
  263.  *             .  CALLEE's locals        .
  264.  *             .__________________________.
  265.  * CALLEEs    fp-> |  CALLERS saved fp    |
  266.  *             |--------------------------|
  267.  *             |  ret address of CALLER    |
  268.  *             |--------------------------|
  269.  *
  270.  * Note: 
  271.  *    -this is true becuase -fomit-frame-pointer and -pg are
  272.  *     incompatible flags (gcc will say so if you try)
  273.  *
  274.  *    -on the 68k, the address of a long count location is passed in a0
  275.  *     we dont use this, it was a convention for the old prof stuff.
  276.  */
  277.  
  278.     __asm__("\
  279.           .text; .even
  280.          .globl mcount    /* note: no `_' */
  281.      mcount:
  282.          movl    sp@,d0        /* CALLEE's address */
  283.          movl    d0,sp@-
  284.          movl    a6@(4),d0    /* CALLERs  address */
  285.          movl    d0,sp@-
  286.          jbsr    _build_graph    /* build_graph(caller, callee) */
  287.          addqw    #8,sp
  288.          rts
  289.          ");
  290.  
  291. /*
  292.  * build_graph
  293.  *    incrementally build the call graph. at each call the CALLER
  294.  *    and CALLEE are specified. this function builds an arc from
  295.  *    CALLER to CALLEE, or increments the arc count if it already
  296.  *    exists.
  297.  *    the graph is maintianed in the structures FROMs and TOs. each
  298.  *    entry in FROMs is the head of a chain of records of all
  299.  *    functions called from FROM. The CALLERs address is hashed
  300.  *    into FROMs
  301.  */
  302. static void build_graph(caller, callee)
  303. void *caller, *callee;
  304. {
  305.     unsigned short    *fromp;      /* hashed ptr into froms         */
  306.     tostruct        *top;      /* current hash chain element        */
  307.     unsigned short    ti;      /* index of current chain element    */
  308.     tostruct        *last;      /* previous element               */
  309.     
  310.     if(profiling)
  311.     return;    /* out if we are not profiling or this is a recursive call */
  312.     profiling++;
  313.     
  314.     /* hash callee, to a pointer into FROMs */
  315.     fromp = (unsigned short *)(USL(caller) - USL(s_lowpc)); /* lowpc orig */
  316.     if(USL(fromp) > textsize)
  317.     {    /* not within profiled text area */
  318.     profiling--;
  319.     return;
  320.     }
  321.     /* scale to an index */
  322.     fromp = (unsigned short *)(USL(fromp) >> (HASH_SHIFT + sizeof(*froms)));
  323.     fromp = &froms[((long)fromp)]; /* hash bucket pointer */
  324.     ti = *fromp;    /* head of the chain */
  325.     if(ti == 0)
  326.     {    /* head is null, first time in the bucket, start a new chain */
  327.     if((ti = ++toalloc) >= tolimit) /* allocate an element from tos pool */
  328.     {    /* ran out */
  329.         profiling = 3; /* give up profiling */
  330.         return;
  331.     }
  332.     *fromp = ti;
  333.     top = &tos[ti];
  334.     top->selfpc = callee;
  335.     top->count  = 1;
  336.     top->link   = 0;
  337.     profiling--;
  338.     return;
  339.     }
  340.     /* otherwise  search the chain */
  341.     for(last = top = &tos[ti]; top->link != 0;  last = top,
  342.                         top = &tos[top->link])
  343.     {
  344.     if(top->selfpc == callee)
  345.     { /* found it */
  346.         top->count++;    /* increment call count */
  347.         if(top == last)
  348.         { /* at the head of the chain already */
  349.         profiling--;
  350.         return;
  351.         }
  352.         /* otherwise bring it to the head */
  353.         ti = last->link;
  354.         last->link = top->link;
  355.         top->link = *fromp;
  356.         *fromp = ti;
  357.         profiling--;
  358.         return;
  359.     }
  360.     }
  361.     /* not found */
  362.     if((ti = ++toalloc) >= tolimit) /* allocate an element from tos pool */
  363.     {    /* ran out */
  364.     profiling = 3; /* give up profiling */
  365.     return;
  366.     }
  367.     /* put it at head of the chain */
  368.     top = &tos[ti];
  369.     top->count = 1;
  370.     top->selfpc = callee;
  371.     top->link = *fromp;
  372.     *fromp = ti;
  373.     
  374.     profiling--;
  375. }
  376.  
  377. /*
  378.  * _mcleanup
  379.  *    dump out the gmon file
  380.  */
  381. void _mcleanup()
  382. {
  383.     int        fd;
  384.     unsigned long   i;
  385.     unsigned short  j;
  386.     unsigned long   frompc;
  387.     struct gm_call  arc;
  388.     
  389.     if((fd = open(GMON_FILE, O_WRONLY|O_CREAT|O_TRUNC, 0644)) < 0)
  390.     {
  391.     Cconws(GMON_FILE); Cconws(": error opening\r\n");
  392.     return;
  393.     }
  394.     
  395.     /* dump the header + histogram */
  396.     if(_write(fd, (void *)(USL(hist_buffer) - sizeof(struct gm_header)),
  397.        hist_size + sizeof(struct gm_header)) != 
  398.        (hist_size + sizeof(struct gm_header)) )
  399.     {
  400.     Cconws(GMON_FILE); Cconws(": error writing\r\n");
  401.     close(fd); return;
  402.     }
  403.  
  404.     /* dump the call graph */
  405.     for( i = 0; i < (textsize >> (HASH_SHIFT + sizeof(*froms))); i++)
  406.     {
  407.     if(froms[i] != 0)
  408.     {
  409.         frompc = USL(s_lowpc) + ( i << (HASH_SHIFT + sizeof(*froms)));
  410.         for(j = froms[i]; j != 0; j = tos[j].link)
  411.         {
  412.         arc.from = (void *)frompc;
  413.         arc.to   = tos[j].selfpc;
  414.         arc.ncalls = tos[j].count;
  415.         if(_write(fd, &arc, sizeof(arc)) != sizeof(arc))
  416.         {
  417.             Cconws(GMON_FILE); Cconws(": error writing\r\n");
  418.             close(fd); return;
  419.         }
  420.         }
  421.     }
  422.     }
  423.     close(fd);
  424. }
  425.  
  426. #ifdef _USE_TIMER_C_
  427. static unsigned short countdown;
  428. #endif
  429. static short installed = 0;    /* reset to 0 before exit */
  430. static unsigned short *bufr;
  431. static unsigned long maxidx;
  432. static unsigned long off;
  433. static unsigned long shift_val;
  434.  
  435. static xbra_struct tick_xbra = _XBRA_INIT(tick);
  436. static xbra_struct term_xbra = _XBRA_INIT(term);
  437.  
  438. extern BASEPAGE *_base;
  439. static BASEPAGE **act_pd;
  440. static BASEPAGE *my_base;
  441.  
  442. /*
  443.  * profil
  444.  *    record pc every N ms into buffer
  445.  *    index into buffer == (pc - offset) >> shift
  446.  *        (note difference in scaling semantics)
  447.  *     turned off by shift == 0
  448.  *    ineffective by bufsiz == 0
  449.  *
  450.  *    on the St, we hook into the Timer C interrupt, and record
  451.  *    every 4'th tick (20 ms).
  452.  *    this method was chosen over user Timer A, so that applications
  453.  *    that use the timer can be profiled.
  454.  *    vbl was not considered because we dont have the flexibility
  455.  *    of changing the time resolution, and because its frequency is
  456.  *    screen rez dependent (plus its harder to get at the user pc!)
  457.  *
  458.  *    xbra protocol to hook in/out handlers. we hook into the terminate
  459.  *    vector independent of the rest of the library to make sure we
  460.  *    unhook before process termination. this is also necessary because
  461.  *    the user can call up these routines independent of gcrt0
  462.  */
  463. int profil(buff, bufsiz, offset, shift)
  464. void *buff;
  465. unsigned long bufsiz, offset;
  466. int shift;
  467. {
  468.     if(shift == 0)
  469.     {
  470.     if(installed)
  471.         remove_handlers();
  472.     installed = 0;
  473.     return 0;
  474.     }
  475.     /* set the params atomically */
  476.     Jdisint(5);
  477. #ifdef _USE_TIMER_C_
  478.     countdown = 4;
  479. #endif
  480.     bufr = (unsigned short *)buff;
  481.     maxidx = bufsiz>>1;    /* max index into short array */
  482.     off = offset;
  483.     shift_val = shift;
  484.  
  485.     if(!installed)
  486.     {
  487.     installed = 1;
  488.     install_handlers();
  489.     }
  490.     Jenabint(5);
  491.     return 0;
  492. }
  493.  
  494. #ifdef _USE_TIMER_C_
  495. /*
  496.  * tick handler
  497.  *    if countdown = 0, record pc
  498.  */
  499. __asm__ ("\
  500.      .text; .even
  501. _tick:
  502.      subqw    #1,_countdown
  503.      jne    1f
  504.  
  505.      movw    #4,_countdown
  506.      moveml    d0-d1/a0,sp@-
  507.      movl    sp@(14),d0    /* get user pc from exception frame */
  508.      subl    _off,d0
  509.     jcs    2f        /* branch if below */
  510.      movl    _shift_val,d1    /* shift it */
  511.      lsrl    d1,d0
  512.      cmpl    _maxidx:l,d0    /* compare with max index */
  513.      jhi    2f        /* branch if out of range */
  514.  
  515.      lsll    #1,d0        /* word index */
  516.     movl    _bufr,a0
  517.      addl    d0,a0        /* incr hist word */
  518.      addqw    #1,a0@
  519. 2:
  520.      moveml    sp@+,d0-d1/a0
  521. 1:
  522.     movl    _tick_xbra+8,sp@-
  523.      rts ");
  524. #else
  525. /*
  526.  * tick handler
  527.  *    in etv_timer timer handoff vector chain (called every 4th tick)
  528.  *    stack at  this point:
  529.  *    <exception frame user pc, sr>            2
  530.  *    <saved d0-d7/a0-a6>                60
  531.  *    <timer calibration .w>                2
  532.  *    <return address to timer C intr routine>    4
  533.  *                            ---
  534.  *                            68 (offset to user pc)
  535.  */
  536. __asm__ ("\
  537.      .text; .even
  538. _tick:
  539.      movl    sp@(68),d0    /* get user pc from exception frame */
  540.      subl    _off,d0
  541.     jcs    1f        /* branch if below */
  542.      movl    _shift_val,d1    /* shift it */
  543.      lsrl    d1,d0
  544.      cmpl    _maxidx:l,d0    /* compare with max index */
  545.      jhi    1f        /* branch if out of range */
  546.  
  547.      lsll    #1,d0        /* word index */
  548.     movl    _bufr,a0
  549.      addl    d0,a0        /* incr hist word */
  550.      addqw    #1,a0@
  551. 1:
  552.     movl    _tick_xbra+8,sp@-    /* call next handler in chain */
  553.      rts ");
  554. #endif
  555.  
  556. /*
  557.  * terminate vector
  558.  */
  559. static void term()    
  560. {
  561.     /* validate  process id */
  562.     if(_base != *act_pd)
  563.     {
  564.     __asm__ volatile("\
  565.              unlk    a6
  566.              jmp    %0@"
  567.              :
  568.              : "a"(term_xbra.next));
  569.     }
  570.     if(installed)
  571.         remove_handlers();
  572.     /* go on to the next guy */
  573.     __asm__ volatile("\
  574.              unlk    a6
  575.              jmp    %0@"
  576.              :
  577.              : "a"(term_xbra.next));
  578. }
  579.  
  580. /*
  581.  * install tick and terminate handlers at the head of the xbra chains
  582.  *    coding thanks to edgar roeder
  583.  */
  584. static void  install_handlers()
  585. {
  586.     long    *sysbase;
  587.  
  588.     sysbase = (long *) get_sysvar((void *) _sysbase);
  589.     switch(sysbase[6])
  590.     {
  591.       case 0x11201985L:
  592.       case 0x02061986L:
  593.       case 0x04241986L:
  594.     act_pd = (BASEPAGE **) 0x602C;
  595.     break;
  596.       default:
  597.     act_pd = (BASEPAGE **) sysbase[10];
  598.     }
  599.  
  600. #ifdef _USE_TIMER_C_
  601.     tick_xbra.next = (xptr) Setexc(276>>2, _XBRA_VEC(tick_xbra));
  602. #else
  603.     tick_xbra.next = (xptr) Setexc(0x100, _XBRA_VEC(tick_xbra));
  604. #endif
  605.     term_xbra.next = (xptr) Setexc(0x102, _XBRA_VEC(term_xbra));
  606.     my_base = _base;
  607. }
  608.  
  609. /*
  610.  * unlink a handler in a xbra friendly manner from the exc chain
  611.  */
  612. static void unlink_handler(me, exc)
  613. xbra_struct *me;
  614. int exc;
  615. {
  616.     xbra_struct *this, *prev;
  617.     long save_ssp;
  618.     
  619.     this = (xbra_struct *)    /* get head of chain */
  620.     ((unsigned long)Setexc(exc, -1L) - offsetof(xbra_struct, jump));
  621.     if(this == me)
  622.     {    /* at the head, just unlink */
  623.     Setexc(exc, me->next);
  624.     return;
  625.     }
  626.     /* otherwise find me in the chain and unlink */
  627.     save_ssp = Super(0L);
  628.     for(prev = this; this && (this != me); prev = this, this = this->next)
  629.     {
  630.     /* validate the xbra */
  631.     if(this->xbra_magic != _XBRA_MAGIC) 
  632.     {    /* shame on you */
  633.         Super(save_ssp);
  634.         Setexc(exc, me->next); /* nuke it, otherwise it may call ME */
  635.         return;           /* after i am deinstalled */
  636.     }
  637.     }
  638.     
  639.     if(this == me)
  640.     {     /* unlink me from middle of the chain */
  641.     prev->next = this->next;
  642.     Super(save_ssp);
  643.     return;
  644.     }
  645.     /* we are screwed */
  646.     Super(save_ssp);
  647.     Cconws("\r\nwhat the fuck!\r\n\n");
  648. }
  649.  
  650.  
  651. static void remove_handlers()
  652. {
  653.     /* first validate pid */
  654.     if(_base == *act_pd)
  655.     {
  656.     if(_base != my_base)    /* in vfork()ed parallel addr space */
  657.         _base -= 2;
  658.     else
  659.     {
  660.         /* do i need to Super and raise IPL here ?? */
  661.  
  662. #ifdef _USE_TIMER_C_
  663.         unlink_handler(&tick_xbra, 276>>2);
  664. #else
  665.         unlink_handler(&tick_xbra, 0x100);
  666. #endif
  667.         unlink_handler(&term_xbra, 0x102);
  668.         installed = 0;
  669.     }
  670.     }
  671. }
  672.  
  673.