[comp.sys.mac] turning off the stack sniffer

per@philabs.Philips.Com (Paul Rutter) (06/24/87)

When playing with "multi-tasking", it is useful to be able to move the stack
pointer to one of several stack areas.  The "stack sniffer" routine checks
whether the stack overlaps the heap during every tick, and bombs if it does.

How do I disable the stack sniffer?  As far as I can tell "IM" Vols 1-4 only
tell what the stack sniffer does, not how to control it.  Someone mentioned
that there is a global flag that enables/disables; if so, what is it's
address (I can't find it in my compiler's include files).

							thanx,
							   paul

james@cantuar.UUCP (J. Collier) (07/02/87)

Paul Rutter (per@philabs.Philips.Com) writes:
>When playing with "multi-tasking", it is useful to be able to move the stack
>pointer to one of several stack areas.  The "stack sniffer" routine checks
>whether the stack overlaps the heap during every tick, and bombs if it does.
>
>How do I disable the stack sniffer? 

Steve Munson (sbm@purdue.edu) suggested an alternative solution to this 
problem some weeks ago. I used a similar method in an experiment earlier this
year - and found it effective to the extent that I tested it (not much). Hope
I'm not deluding myself in thinking other people might be interested...

    NB - the suggestions which follow are most unlikely to work on anything
newer than a 128k ROM, if at all. APPLE THOUGHT POLICE PLEASE HIT 'j' NOW...:-)

    Unfortunately, the 60Hz stack sniffer is not the only ROM routine which
checks the stack. Disabling the stack sniffer (by setting StkLowPt to 0, if I 
remember correctly) also has some messy side-effects.  Besides which, it's
quite nice to have the sniffer look after the behaviour of ones own
heap-resident stacks. 
    The sniffing routines work by comparing the value of the stack pointer to
the global HeapEnd. Somewhat surprisingly, changing the value of this global
to the new stack limit at each context switch appears to achieve the desired
effect. 
    Of course, various other ROM routines will be using HeapEnd. However, after
several cross-eyed hours searching a 128k ROM, I found only the following
references to HeapEnd:

                4008fc  :       Stack Sniffer          (read)
                409172  :       GetFInfo - stack check (read)
                40c218  :       MapRgn   - stack check (read)
                40fed6  :       SetApplBase            (set)
                4100b4  :       SetApplLimit           (set)
                4108xx  :       MoreMasters            (read & set)
                4135a6  :       InitResources (can't remember, but who cares?)

    So it seems that HeapEnd is normally used only to mark the limit of the
stack area. SetApplBase, SetApplLimit and InitResources shouldn't be called
after the application is under way. MoreMasters is trickier; I just allocated
a large block of masters during initialisation and hoped for the best, but 
you will probably want to patch the trap.

    BTW, how are you initiating context switches? Doing it on the 60Hz interrupt
I came across several problems:
   1. I found I had to patch the interrupt vector and do the switch *after* the
normal VBL tasks.
   2. I had to disable switching during ROM routines (non-reentrant code :-().
This involved (don't barf) hooking the trap dispatcher. Tricky. Scott Knaster
mentions (in "How to Write Macintosh Software") a particularly revolting system
patch which relies on the value of the return address being from a certain ROM
routine. The dispatcher therefore has to replace the stack frame if the routine
was called from user code, but not if called from another ROM routine. 
   Oh, what the ***, the bored will have stopped reading by now, so here goes..
It breaks every rule in the book: self modification, RTE, etc. etc. Do you
want to run this on a 68020? %-Q
-----------------------------------------------------------------------
             /* set A-trap vector to "mytrdsp" */
#define CANTXFER    APPLSCRATCH     /*  inhibit context switch if set */
#define XFERPEND    APPLSCRATCH+1   /*  c-s blocked during ROM call? */
                                    /*  (refer VBLtrap.c) */
#define SAVERA      APPLSCRATCH+2   /*  save return address here */

                                    /*  <oldTOS> <4EA> <2SR> <-SP */
mytrdsp:    tst.b   CANTXFER        /*  set if called from ROM */
            bne.s   jtdisp          /*  if set, we can't mess with the stack */    
            addq.b  #1, CANTXFER    /*  disable hook in ROM-ROM calls */
            movem.l A0/A1, -(A7)    /*  <oldTOS> <4EA> <2SR> <8SV>*/
            movea.l 10(A7), A0      /*  EA -> A0 */
            lea     trpcpy, A1      /*  &(word after jmp) -> A1 */
            move.w  (A0), (A1)      /*  Axxx -> (word after jmp) */
            addq.l  #2, A0          /*  skip trap word on rte */ 
            move.l  A0, SAVERA      /*  RA -> global */
            move.l  A1, 10(A7)      /*  <oldTOS><4MYEA><2SR><8SV> */
            movem.l  (A7)+, A0/A1   /*  <oldTOS><4MYEA><2SR> */
jtdisp:     jmp     <trap dispatch> /*  initialise on load */
trpcpy:     dc.w    0xA000          /*  dummy trap copied from code */
            move.l  SAVERA, -(A7)   /*  restore saved return address */
            move.l  SR, -(A7)       /*  push SR - cc's not needed */
                                    /*  assert : CANTXFER == 1 */
            clr.b   CANTXFER        /*  returning to user code - clear flag */
            tst.b   XFERPEND        /*  set if process xfer was blocked */
            beq.s   immrtn          /*  if not, return now */
            jsr     <cont switcher> /*  context switching routine */
immrtn:     rte                     /*  return & restore saved SR */
------------------------------------------------------------------------------
Grubbiness rules, OK? Have fun.
-------------------------
James Collier  (james@cantuar.uucp / {watmath,munnari,mcvax}!cantuar!james
Computer Science Dept., University of Canterbury, Christchurch, New Zealand.
My supervisor doesn't know that I'm doing this.

jwhitnel@csib.UUCP (Jerry Whitnell) (07/07/87)

In article <135@cantuar.UUCP> james@cantuar.UUCP (J. Collier) writes:
>Paul Rutter (per@philabs.Philips.Com) writes:
>>When playing with "multi-tasking", it is useful to be able to move the stack
>>pointer to one of several stack areas.  The "stack sniffer" routine checks
>>whether the stack overlaps the heap during every tick, and bombs if it does.
>>
>>How do I disable the stack sniffer? 

I've not tried this but I've heard that if you store a 0 in StackLowPt (sp?
my Inside Mac is at home), it disables the stack sniffer.  I think Scott
Knaster's book mentions it in his discription of launching a program

Jerry Whitnell
Communication Solutions, Inc.

olson@mmm.UUCP (Al Olson) (07/07/87)

I have also been playing around with "multi-tasking" on the Mac and
ran into the some of the same problems.  I discovered that placing a
process stack in a heap block causes major-league problems. (The
MoreMasters trap also looks for heap zones on the stack in its
search.  Blammo!)  This approach gave me so much grief that I did
the only logical thing -- I gave up.

Well, not completely.  The Mac firmly believes that the stack is the
stack and the heap is the heap, so why fool it?  I tried another
approach which works faily well - link the process stack on to the
scheduler stack a context switch.  Where does it come from?

When a process is prempted (either synchronously or asynchronously),
copy the process portion of the stack (including registers, etc) into
a newly allocated heap block.  Then save the handle in the process
descriptor.  Now deallocate the process stack and restore the scheduler.
This is nice because when a process is not executing its saved stack
relocates in memory.  When resumeing a process, do the reverse: create
space on the scheduler stack, copy the proc stack in and throw the block
away. (of course you have to lock the handle during transfer).

This method has the advantage of keeping the stack and the heap seperate
and allowing the stack sniffer to work *for* you.  The disadvantage is
the increased time to perform a context switch.  (the BlockMove trap
is pretty damn fast though).

Process premption and toolbox reentrancy is another issue, however.
I approached this in a slightly different, but equally illegal way.
After a few sessions with Tmon, I discovered the format of the stack
during the execution of a VBL task.  Way down deep, but in a fixed
location, lie the SR and PC value pushed during the exception by the
68000.  Now what?  Prior to entering the scheduler, I located and
locked the resource map for the application (scheduler + processes)
ank kept a pointer to the CODE resource list.  During my VBL task
I compare the interrupt PC value with all loaded code segments.  If
the PC lies in a loaded CODE segment (not the scheduler's, however)
then I *replace* the interrupt PC and SR with my own premption routine.
(after saving them, of course).  The result: asynchronous premption.

The only real advantage of this method is that I have not patched
*any* trap routine.  (although blasting the stack is certainly more
severe)  I guess there is no easy way to initiate asynchronous premption
without resorting to subterfuge...

Any way, these are just some thoughts I had on the subject.  Maybe
I should wait for a Mac II and Unix...
-- 

Alan Olson	ihnp4!mmm!olson