Linux current_stack_region()

Gary Benson gbenson at redhat.com
Mon Mar 10 08:06:35 PDT 2008


Hi all,

Recently I've been investigating some stack-related failures on ppc,
and trying to figure out how to make the stack region code work on
ia64.

The first thing I discovered is that the current linux code is wrong
when there are guard pages.  The comment above current_stack_region
in os_linux_{i486,amd64,x86}.cpp puts the guard page outside the
region reported by pthread_attr_getstack(), which is not the case.
It needs to use pthread_attr_getguardsize() and trim that many bytes
from the bottom of the region reported by pthread_attr_getstack().

I started modifying current_stack_region to do just that, but its
comments contain warnings that pthread_getattr_np() returns bogus
values for initial threads.  os::Linux::capture_initial_stack()
has more such warnings, though neither mentions exactly _what_ was
bogus.  Does anyone know?  Without a working pthread_getattr_np()
you can't use pthread_attr_getguardsize(), and without that it's
not possible to implement current_stack_region() in the form it's
currently defined.

I spoke with our glibc maintainer and he assured me that
pthread_getattr_np() returns good values for all threads, albeit more
slowly for the initial thread.  I rewrote current_stack_region() to
use it.

I attached what I wrote.  Does it look ok?

Cheers,
Gary

--
http://gbenson.net/
-------------- next part --------------
static void current_stack_region(address *bottom, size_t *size)
{
  pthread_attr_t attr;
  int res = pthread_getattr_np(pthread_self(), &attr);
  if (res != 0) {
    if (res == ENOMEM) {
      vm_exit_out_of_memory(0, "pthread_getattr_np");
    }
    else {
      fatal1("pthread_getattr_np failed with errno = %d", res);
    }
  }

  address stack_bottom;
  size_t stack_bytes;
  res = pthread_attr_getstack(&attr, (void **) &stack_bottom, &stack_bytes);
  if (res != 0) {
    fatal1("pthread_attr_getstack failed with errno = %d", res);
  }
  address stack_top = stack_bottom + stack_bytes;

  // The block of memory returned by pthread_attr_getstack() includes
  // guard pages where present.  We need to trim these off.
  size_t page_bytes = os::Linux::page_size();
  assert(((intptr_t) stack_bottom & (page_bytes - 1)) == 0, "unaligned stack");

  size_t guard_bytes;
  res = pthread_attr_getguardsize(&attr, &guard_bytes);
  if (res != 0) {
    fatal1("pthread_attr_getguardsize failed with errno = %d", res);
  }
  int guard_pages = align_size_up(guard_bytes, page_bytes) / page_bytes;
  assert(guard_bytes == guard_pages * page_bytes, "unaligned guard");

#ifdef IA64
  // IA64 has two stacks sharing the same area of memory, a normal
  // stack growing downwards and a register stack growing upwards.
  // Guard pages, if present, are in the centre.  This code splits
  // the stack in two even without guard pages, though in theory
  // there's nothing to stop us allocating more to the normal stack
  // or more to the register stack if one or the other were found
  // to grow faster.
  int total_pages = align_size_down(stack_bytes, page_bytes) / page_bytes;
  stack_bottom += (total_pages - guard_pages) / 2 * page_bytes;
#endif // IA64

  stack_bottom += guard_bytes;

  pthread_attr_destroy(&attr);

  // The initial thread has a growable stack, and the size reported
  // by pthread_attr_getstack is the maximum size it could possibly
  // be given what currently mapped.  This can be huge, so we cap it.
  if (os::Linux::is_initial_thread()) {
    stack_bytes = stack_top - stack_bottom;

    if (stack_bytes > JavaThread::stack_size_at_create())
      stack_bytes = JavaThread::stack_size_at_create();

    stack_bottom = stack_top - stack_bytes;
  }

  assert(os::current_stack_pointer() >= stack_bottom, "should do");
  assert(os::current_stack_pointer() < stack_top, "should do");

  *bottom = stack_bottom;
  *size = stack_top - stack_bottom;
}


More information about the hotspot-dev mailing list