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

Andrew Begel abegel@eecs.berkeley.edu
Tue, 11 Nov 2003 19:05:19 -0800


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