[Gc] GC crash on OSX 10.3.1 (7C103)

Boehm, Hans hans_boehm@hp.com
Wed, 12 Nov 2003 09:39:23 -0800


That looks much more promising than I had thought.

At least initially, I would favor a "look for and scan unregistered threads" option,
which doesn't replace the existing code.  It may make sense to eventually
make that the default.  But on other platforms it has generally been useful
to keep as many options as possible.  And this is something that I don't know
how to do on a more generic pthreads platform.  Hence it may not be something
that you want to turn on while developing portable code.

The existing code of course needs to deal with the case that it's not
initialized from the main thread.  Depending on whether there are any risks
involved in that version of the code (is the sort of stack unwinding described
below always reliable, even with assembly frames on the stack?), it may make
sense to only turn that on with the new option as well.

If it looks for extra threads, the GC_stop_world code probably has to iterate
until no more new threads are found.  Threads may be created while others are
being stopped.

It would probably be useful (but clearly not essential)
to make this a runtime option that can be turned
on (but probably not off, for security reasons) with an environment variable.
That would make it easier to look at any performance issues.  And it may be
a useful debugging option.  (If it fixes your problem, you're either in Andy's
situation, or you forgot to include gc.h in threads code.)

Hans

> -----Original Message-----
> From: Andrew Begel [mailto:abegel@eecs.berkeley.edu]
> Sent: Tuesday, November 11, 2003 7:05 PM
> To: Boehm, Hans
> Cc: 'gc@linux.hpl.hp.com'
> Subject: Re: [Gc] GC crash on OSX 10.3.1 (7C103)
> 
> 
> On Nov 11, 2003, at 4:17 PM, Boehm, Hans wrote:
> 
> > I hadn't fully appreciated what you're trying to do.  You're loading
> > a library using our GC into a Java application running on a 
> Sun-based
> > VM?
> 
> Well, the Apple supplied VM, yes.
> 
> > In general this is pretty hairy, and I would avoid it if there are
> > other feasible solutions.  (I assume gcj won't work?  A separate 
> > process?  When it comes
> > to garbage collectors, two is a crowd.)
> 
> Nope, it has to be this way. We aren't set up for process-based 
> interfaces.
> 
> > 1) Depending on the platform and whether you explicitly 
> tell it to or 
> > not,
> > our collector may want to trace the Java heap.  This may be 
> essential 
> > to
> > avoid dropping objects.  Or it may be bad for performance.  Or both.
> 
> No, we're being very careful to not put random references to GC owned 
> items on the Java heap. When we do need to do this, we maintain a 
> hashtable of GC pointers in C++ code that alerts the GC that 
> we've got 
> an extent pointer around. We augment the Java finalization for each 
> object type to remove that object's ref from the hashtable so the C++ 
> side can get GCd properly.
> 
> > 2) If you do allow references from the Java heap, cycles 
> crossing both 
> > GC heaps
> > can't really be reclaimed.
> 
> Understood. We're pretty careful about this too.
> 
> > 3) I haven't thought about deadlock issues if both 
> collectors try to 
> > stop
> > the world to GC at the same time.
> 
> Yes, this could be a problem if Java threads are GCing and BW 
> GC tries 
> to scan them. However, given that we don't have the GC "detect" 
> GC-owned memory on the Java stack/heap, it should work out ok for us.
> 
> > As far as thread stacks are concerned, as Brian pointed out, our 
> > collector
> > somehow needs to know about all thread stacks that may 
> reference the 
> > collected heap.
> > On win32 there are the beginnings of an API for "attaching" threads 
> > after the fact.
> > It would be relatively easy to do the same on Posix platforms.  (Up 
> > until now,
> > it was less necessary there.)  But each relevant
> > thread would need to make an explicit call, and would need to get a 
> > pointer to
> > the "base" of the stack.  (This doesn't need to be the real 
> base, so 
> > long as
> > there are no pointers to the GC heap below it.)
> 
> Yes, this presents a problem. Also, there are two thread 
> APIs, pthreads 
> and NSThreads. The pthread source code is open-source, but 
> the NSThread 
> code is not. I've written code to get the base of the stack 
> in Darwin. 
> All threads must obey the OSX ABI which mandates the stack layout, 
> whether you're an NSThread or a pthread.
> 
> typedef struct StackFrame {
>    unsigned int	savedSP;
>    unsigned int	savedCR;
>    unsigned int	savedLR;
> } StackFrame;
> 
> unsigned int FindTopOfStack(unsigned int stack_start) {
>    StackFrame	*frame;
> 
> if (stack_start == 0) {
>      __asm__ volatile("lwz	%0,0(r1)" : "=r" (frame));
>    } else {
>      frame = (StackFrame *)stack_start;
>    }
>    while((StackFrame *)frame->savedSP != NULL) {
>      if (((frame->savedLR & ~3) == 0) || ((~(frame->savedLR) 
> & ~3) == 0))
>        break;
>      frame = (StackFrame*)frame->savedSP;
>    }
>    return (unsigned int)frame;
> }	
> 
> This function walks the stack. If you pass a known thread's 
> register 1 
> as argument 0, you can also walk its stack to the top too (On Darwin, 
> stacks grow down).
> 
> This function works for the main process thread as well as spawned 
> pthreads and should work for spawned NSThreads.
> 
> -----
> 
> So we're good if there was only a way to register all threads every 
> time a GC occurs (or when the thread is created, but that's much much 
> harder on OSX. But after a few hours, I figured it out.
> 
> I used task_threads() (from /usr/include/mach/ppc/task.h) to 
> get a list 
> of the current threads, and then used thread_get_state() to get the 
> thread's register info, and FindTopOfStack() to crawl the stack and 
> find its stack base.
> 
> #include <mach/mach.h>
> void print_threads() {
>    thread_act_array_t act_list;
>    int i;
>    mach_msg_type_number_t listcount;
>    int ret = task_threads(current_task(), &act_list, &listcount);
>    printf("ret = %d, count = %d\n", ret, listcount);
>    for(i = 0; i < listcount; i++) {
>      thread_act_t thread = act_list[i];
>      printf("thread is %d\n", (int)thread);
>      ppc_thread_state_t info;
>      mach_msg_type_number_t outCount = THREAD_STATE_MAX;
>      thread_get_state(thread, MACHINE_THREAD_STATE, 
> (natural_t *)&info, 
> &outCount);
>      printf("thread %d, stack pointer %p\n", i, 
> FindTopOfStack(info.r1));
>    }
> }
> 
> This should be enough to get the stack limits for each thread in the 
> process.
> 
> 
> Brian, can you put this together in some coherent form into the 
> darwin_stop_threads.{h.c} code?
> 
> Hans, how do you feel about checking for new threads on every GC, on 
> Darwin?
> 
> 
> Andy
>