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