home *** CD-ROM | disk | FTP | other *** search
/ Atari FTP / ATARI_FTP_0693.zip / ATARI_FTP_0693 / Mint / mntlib32.zoo / gmon.c < prev    next >
C/C++ Source or Header  |  1993-05-23  |  18KB  |  700 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. #if __GNUC__ > 1
  276. void mcount (void) asm ("mcount");
  277.  
  278. void
  279. mcount ()
  280. {
  281.   void *callee, *caller;
  282.   callee = (void *) __builtin_return_address (0);
  283.   caller = (void *) __builtin_return_address (1);
  284.   build_graph (caller, callee);
  285. }
  286. #else
  287.     __asm__("\
  288.           .text; .even
  289.          .globl mcount    /* note: no `_' */
  290.      mcount:
  291.          movl    sp@,d0        /* CALLEE's address */
  292.          movl    d0,sp@-
  293.          movl    a6@(4),d0    /* CALLERs  address */
  294.          movl    d0,sp@-
  295.          jbsr    _build_graph    /* build_graph(caller, callee) */
  296.          addqw    #8,sp
  297.          rts
  298.          ");
  299. #endif
  300.  
  301. /*
  302.  * build_graph
  303.  *    incrementally build the call graph. at each call the CALLER
  304.  *    and CALLEE are specified. this function builds an arc from
  305.  *    CALLER to CALLEE, or increments the arc count if it already
  306.  *    exists.
  307.  *    the graph is maintianed in the structures FROMs and TOs. each
  308.  *    entry in FROMs is the head of a chain of records of all
  309.  *    functions called from FROM. The CALLERs address is hashed
  310.  *    into FROMs
  311.  */
  312. static void build_graph(caller, callee)
  313. void *caller, *callee;
  314. {
  315.     unsigned short    *fromp;      /* hashed ptr into froms         */
  316.     tostruct        *top;      /* current hash chain element        */
  317.     unsigned short    ti;      /* index of current chain element    */
  318.     tostruct        *last;      /* previous element               */
  319.     
  320.     if(profiling)
  321.     return;    /* out if we are not profiling or this is a recursive call */
  322.     profiling++;
  323.     
  324.     /* hash callee, to a pointer into FROMs */
  325.     fromp = (unsigned short *)(USL(caller) - USL(s_lowpc)); /* lowpc orig */
  326.     if(USL(fromp) > textsize)
  327.     {    /* not within profiled text area */
  328.     profiling--;
  329.     return;
  330.     }
  331.     /* scale to an index */
  332.     fromp = (unsigned short *)(USL(fromp) >> (HASH_SHIFT + sizeof(*froms)));
  333.     fromp = &froms[((long)fromp)]; /* hash bucket pointer */
  334.     ti = *fromp;    /* head of the chain */
  335.     if(ti == 0)
  336.     {    /* head is null, first time in the bucket, start a new chain */
  337.     if((ti = ++toalloc) >= tolimit) /* allocate an element from tos pool */
  338.     {    /* ran out */
  339.         profiling = 3; /* give up profiling */
  340.         return;
  341.     }
  342.     *fromp = ti;
  343.     top = &tos[ti];
  344.     top->selfpc = callee;
  345.     top->count  = 1;
  346.     top->link   = 0;
  347.     profiling--;
  348.     return;
  349.     }
  350.     /* otherwise  search the chain */
  351.     for(last = top = &tos[ti]; top->link != 0;  last = top,
  352.                         top = &tos[top->link])
  353.     {
  354.     if(top->selfpc == callee)
  355.     { /* found it */
  356.         top->count++;    /* increment call count */
  357.         if(top == last)
  358.         { /* at the head of the chain already */
  359.         profiling--;
  360.         return;
  361.         }
  362.         /* otherwise bring it to the head */
  363.         ti = last->link;
  364.         last->link = top->link;
  365.         top->link = *fromp;
  366.         *fromp = ti;
  367.         profiling--;
  368.         return;
  369.     }
  370.     }
  371.     /* not found */
  372.     if((ti = ++toalloc) >= tolimit) /* allocate an element from tos pool */
  373.     {    /* ran out */
  374.     profiling = 3; /* give up profiling */
  375.     return;
  376.     }
  377.     /* put it at head of the chain */
  378.     top = &tos[ti];
  379.     top->count = 1;
  380.     top->selfpc = callee;
  381.     top->link = *fromp;
  382.     *fromp = ti;
  383.     
  384.     profiling--;
  385. }
  386.  
  387. /*
  388.  * _mcleanup
  389.  *    dump out the gmon file
  390.  */
  391. void _mcleanup()
  392. {
  393.     int        fd;
  394.     unsigned long   i;
  395.     unsigned short  j;
  396.     unsigned long   frompc;
  397.     struct gm_call  arc;
  398.     
  399.     if((fd = open(GMON_FILE, O_WRONLY|O_CREAT|O_TRUNC, 0644)) < 0)
  400.     {
  401.     Cconws(GMON_FILE); Cconws(": error opening\r\n");
  402.     return;
  403.     }
  404.     
  405.     /* dump the header + histogram */
  406.     if(_write(fd, (void *)(USL(hist_buffer) - sizeof(struct gm_header)),
  407.        hist_size + sizeof(struct gm_header)) != 
  408.        (hist_size + sizeof(struct gm_header)) )
  409.     {
  410.     Cconws(GMON_FILE); Cconws(": error writing\r\n");
  411.     close(fd); return;
  412.     }
  413.  
  414.     /* dump the call graph */
  415.     for( i = 0; i < (textsize >> (HASH_SHIFT + sizeof(*froms))); i++)
  416.     {
  417.     if(froms[i] != 0)
  418.     {
  419.         frompc = USL(s_lowpc) + ( i << (HASH_SHIFT + sizeof(*froms)));
  420.         for(j = froms[i]; j != 0; j = tos[j].link)
  421.         {
  422.         arc.from = (void *)frompc;
  423.         arc.to   = tos[j].selfpc;
  424.         arc.ncalls = tos[j].count;
  425.         if(_write(fd, &arc, sizeof(arc)) != sizeof(arc))
  426.         {
  427.             Cconws(GMON_FILE); Cconws(": error writing\r\n");
  428.             close(fd); return;
  429.         }
  430.         }
  431.     }
  432.     }
  433.     close(fd);
  434. }
  435.  
  436. #ifdef _USE_TIMER_C_
  437. static unsigned short countdown;
  438. #endif
  439. static short installed = 0;    /* reset to 0 before exit */
  440. static unsigned short *bufr;
  441. static unsigned long maxidx;
  442. static unsigned long off;
  443. static unsigned long shift_val;
  444.  
  445. static xbra_struct tick_xbra = _XBRA_INIT(tick);
  446. static xbra_struct term_xbra = _XBRA_INIT(term);
  447.  
  448. extern BASEPAGE *_base;
  449. static BASEPAGE **act_pd;
  450. static BASEPAGE *my_base;
  451.  
  452. /*
  453.  * profil
  454.  *    record pc every N ms into buffer
  455.  *    index into buffer == (pc - offset) >> shift
  456.  *        (note difference in scaling semantics)
  457.  *     turned off by shift == 0
  458.  *    ineffective by bufsiz == 0
  459.  *
  460.  *    on the St, we hook into the Timer C interrupt, and record
  461.  *    every 4'th tick (20 ms).
  462.  *    this method was chosen over user Timer A, so that applications
  463.  *    that use the timer can be profiled.
  464.  *    vbl was not considered because we dont have the flexibility
  465.  *    of changing the time resolution, and because its frequency is
  466.  *    screen rez dependent (plus its harder to get at the user pc!)
  467.  *
  468.  *    xbra protocol to hook in/out handlers. we hook into the terminate
  469.  *    vector independent of the rest of the library to make sure we
  470.  *    unhook before process termination. this is also necessary because
  471.  *    the user can call up these routines independent of gcrt0
  472.  */
  473. int profil(buff, bufsiz, offset, shift)
  474. void *buff;
  475. unsigned long bufsiz, offset;
  476. int shift;
  477. {
  478.     if(shift == 0)
  479.     {
  480.     if(installed)
  481.         remove_handlers();
  482.     installed = 0;
  483.     return 0;
  484.     }
  485.     /* set the params atomically */
  486.     Jdisint(5);
  487. #ifdef _USE_TIMER_C_
  488.     countdown = 4;
  489. #endif
  490.     bufr = (unsigned short *)buff;
  491.     maxidx = bufsiz>>1;    /* max index into short array */
  492.     off = offset;
  493.     shift_val = shift;
  494.  
  495.     if(!installed)
  496.     {
  497.     installed = 1;
  498.     install_handlers();
  499.     }
  500.     Jenabint(5);
  501.     return 0;
  502. }
  503.  
  504. #ifdef __MBASE__
  505. #define Countdown __MBASESTR__ "@(_countdown)"
  506. #define Off __MBASESTR__ "@(_off)"
  507. #define Shift_val __MBASESTR__ "@(_shift_val)"
  508. #define Maxidx __MBASESTR__ "@(_maxidx)"
  509. #define Bufr __MBASESTR__ "@(_bufr)"
  510. #define Tick_xbra __MBASESTR__ "@(_tick_xbra+8)"
  511. #else
  512. #define Countdown "_countdown"
  513. #define Off "_off"
  514. #define Shift_val "_shift_val"
  515. #define Maxidx "_maxidx"
  516. #define Bufr "_bufr"
  517. #define Tick_xbra "_tick_xbra+8"
  518. #endif
  519. #ifdef _USE_TIMER_C_
  520. /*
  521.  * tick handler
  522.  *    if countdown = 0, record pc
  523.  */
  524. __asm__ ("\
  525.      .text; .even
  526. _tick:
  527.      subqw    #1," Countdown "
  528.      jne    1f
  529.  
  530.      movw    #4," Countdown "
  531.      moveml    d0-d1/a0,sp@-
  532.      movl    sp@(14),d0    /* get user pc from exception frame */
  533.      subl    " Off ",d0
  534.     jcs    2f        /* branch if below */
  535.      movl    " Shift_val ",d1    /* shift it */
  536.      lsrl    d1,d0
  537.      cmpl    " Maxidx ",d0    /* compare with max index */
  538.      jhi    2f        /* branch if out of range */
  539.  
  540.      lsll    #1,d0        /* word index */
  541.     movl    " Bufr ",a0
  542.      addl    d0,a0        /* incr hist word */
  543.      addqw    #1,a0@
  544. 2:
  545.      moveml    sp@+,d0-d1/a0
  546. 1:
  547.     movl    " Tick_xbra ",sp@-
  548.      rts ");
  549. #else
  550. /*
  551.  * tick handler
  552.  *    in etv_timer timer handoff vector chain (called every 4th tick)
  553.  *    stack at  this point:
  554.  *    <exception frame user pc, sr>            2
  555.  *    <saved d0-d7/a0-a6>                60
  556.  *    <timer calibration .w>                2
  557.  *    <return address to timer C intr routine>    4
  558.  *                            ---
  559.  *                            68 (offset to user pc)
  560.  */
  561. __asm__ ("\
  562.      .text; .even
  563. _tick:
  564.      movl    sp@(68),d0    /* get user pc from exception frame */
  565.      subl    " Off ",d0
  566.     jcs    1f        /* branch if below */
  567.      movl    " Shift_val ",d1    /* shift it */
  568.      lsrl    d1,d0
  569.      cmpl    " Maxidx ",d0    /* compare with max index */
  570.      jhi    1f        /* branch if out of range */
  571.  
  572.      lsll    #1,d0        /* word index */
  573.     movl    " Bufr ",a0
  574.      addl    d0,a0        /* incr hist word */
  575.      addqw    #1,a0@
  576. 1:
  577.     movl    " Tick_xbra ",sp@-    /* call next handler in chain */
  578.      rts ");
  579. #endif
  580.  
  581. /*
  582.  * terminate vector
  583.  */
  584. static void term()    
  585. {
  586.     /* validate  process id */
  587.     if(_base != *act_pd)
  588.     {
  589.     __asm__ volatile("\
  590.              unlk    a6
  591.              jmp    %0@"
  592.              :
  593.              : "a"(term_xbra.next));
  594.     }
  595.     if(installed)
  596.         remove_handlers();
  597.     /* go on to the next guy */
  598.     __asm__ volatile("\
  599.              unlk    a6
  600.              jmp    %0@"
  601.              :
  602.              : "a"(term_xbra.next));
  603. }
  604.  
  605. /*
  606.  * install tick and terminate handlers at the head of the xbra chains
  607.  *    coding thanks to edgar roeder
  608.  */
  609. static void  install_handlers()
  610. {
  611.     long    *sysbase;
  612.  
  613.     sysbase = (long *) get_sysvar((void *) _sysbase);
  614.     switch(sysbase[6])
  615.     {
  616.       case 0x11201985L:
  617.       case 0x02061986L:
  618.       case 0x04241986L:
  619.     act_pd = (BASEPAGE **) 0x602CL;
  620.     break;
  621.       default:
  622.     act_pd = (BASEPAGE **) sysbase[10];
  623.     }
  624.  
  625. #ifdef _USE_TIMER_C_
  626.     tick_xbra.next = (xptr) Setexc(276>>2, _XBRA_VEC(tick_xbra));
  627. #else
  628.     tick_xbra.next = (xptr) Setexc(0x100, _XBRA_VEC(tick_xbra));
  629. #endif
  630.     term_xbra.next = (xptr) Setexc(0x102, _XBRA_VEC(term_xbra));
  631.     my_base = _base;
  632. }
  633.  
  634. /*
  635.  * unlink a handler in a xbra friendly manner from the exc chain
  636.  */
  637. static void unlink_handler(me, exc)
  638. xbra_struct *me;
  639. int exc;
  640. {
  641.     xbra_struct *this, *prev;
  642.     long save_ssp;
  643.     
  644.     this = (xbra_struct *)    /* get head of chain */
  645.     ((unsigned long)Setexc(exc, -1L) - offsetof(xbra_struct, jump));
  646.     if(this == me)
  647.     {    /* at the head, just unlink */
  648.     (void)Setexc(exc, me->next);
  649.     return;
  650.     }
  651.     /* otherwise find me in the chain and unlink */
  652.     save_ssp = Super(0L);
  653.     for(prev = this; this && (this != me); prev = this,
  654.         this = (xbra_struct *)((this->next)
  655.          ? (((char *)(this->next)) - offsetof(xbra_struct, jump)) : 0))
  656.     {
  657.     /* validate the xbra */
  658.     if(this->xbra_magic != _XBRA_MAGIC) 
  659.     {    /* shame on you */
  660.         Super(save_ssp);
  661.         (void)Setexc(exc, me->next); /* nuke it, otherwise it may call ME */
  662.         return;           /* after i am deinstalled */
  663.     }
  664.     }
  665.     
  666.     if(this == me)
  667.     {     /* unlink me from middle of the chain */
  668.     prev->next = this->next;
  669.     Super(save_ssp);
  670.     return;
  671.     }
  672.     /* we are screwed */
  673.     Super(save_ssp);
  674.     Cconws("\r\nwhat the fuck!\r\n\n");
  675. }
  676.  
  677.  
  678. static void remove_handlers()
  679. {
  680.     /* first validate pid */
  681.     if(_base == *act_pd)
  682.     {
  683.     if(_base != my_base)    /* in vfork()ed parallel addr space */
  684.         _base -= 2;
  685.     else
  686.     {
  687.         /* do i need to Super and raise IPL here ?? */
  688.  
  689. #ifdef _USE_TIMER_C_
  690.         unlink_handler(&tick_xbra, 276>>2);
  691. #else
  692.         unlink_handler(&tick_xbra, 0x100);
  693. #endif
  694.         unlink_handler(&term_xbra, 0x102);
  695.         installed = 0;
  696.     }
  697.     }
  698. }
  699.  
  700.