Ignore:
Timestamp:
Dec 20, 2010, 1:16:14 PM (14 years ago)
Author:
[email protected]
Message:

Bug 26276 - Need a mechanism to determine stack extent

Reviewed by Oliver Hunt.

JavaScriptCore:

This patch adds a class 'StackBounds', to hold information about the machine stack.
The implementation of this class broadly adheres to the current implmentation of
stack limit checking, and as such does not solve the problem of determining stack
extent, but gives us a common place to do so.

Currently two mechanism are provided to determine the stack origin (the point the
stack is growing away from). currentThreadStackBase() in Collector provides a
more accurate determination of the stack origin, so use this to calculate
StackBounds::m_origin; WTFThreadData::approximatedStackStart is less accurate, and
as such can be removed. Cache the StackBounds on WTFThreadData such that they
need only be determined once per thread, and for non-API contexts cache this
information in JSGlobalData, to save a thread-specific access.

For the time being retain the estimate of stack size used by JSC's parser
(128 * sizeof(void*) * 1024), with a view to replacing this with something more
accurate in the near future.

  • parser/JSParser.cpp:

(JSC::JSParser::canRecurse):
(JSC::JSParser::JSParser):

Change to use StackBounds.

  • runtime/Collector.cpp:

(JSC::Heap::registerThread):
(JSC::Heap::markCurrentThreadConservativelyInternal):

Change to use StackBounds, cached on JSGlobalData.

  • runtime/JSGlobalData.cpp:

(JSC::JSGlobalData::JSGlobalData):

  • runtime/JSGlobalData.h:

(JSC::JSGlobalData::stack):

Add a cached copy of StackBounds.

  • wtf/StackBounds.cpp: Copied from JavaScriptCore/runtime/Collector.cpp.

(WTF::estimateStackBound):
(WTF::StackBounds::initialize):
(WTF::getStackMax):

Copy code from Collector.cpp to determine stack origin.

  • wtf/StackBounds.h: Added.

(WTF::StackBounds::StackBounds):

No argument constructor; returns a null StackBounds.

(WTF::StackBounds::currentThreadStackBounds):

Returns a StackBounds object representing the stack limits
of the current thread.

(WTF::StackBounds::origin):

Returns to stack origin (the point the stack is growing away
from; the highest extent of the stack on machines where the
stack grows downwards.

(WTF::StackBounds::recursionLimit):

Returns a limit value that is 'a comfortable distance from
the end of the stack'. Our concept of this is currently 1 page
away from the end, however the default value may be tuned in
the future, and clients may override passing a larger delta;
should only be called on StackBounds object representing the
stack of the thread this method is called on (checked by
checkConsistency).

(WTF::StackBounds::recursionCheck):

Checks whether we are currently 'a comfortable distance from
the end of the stack'. Our concept of this is currently 1 page
away from the end, however the default value may be tuned in
the future, and clients may override passing a larger delta
to apply when checking, if they wish to do so. This method
should only be called on StackBounds object representing the
stack of the thread this method is called on (checked by
checkConsistency).

(WTF::StackBounds::current):

Approximate current stack position. On machines where the stack
is growing downwards this is the lowest address that might need
conservative collection.

(WTF::StackBounds::isGrowingDownward):

True for all platforms other than WINCE, which has to check.

(WTF::StackBounds::checkConsistency):

This is called in methods that shoulds only be operating on a
valid set of bounds; as such we expect m_origin != m_bounds
(i.e. stack size != zero) - we're really testing that this
object is not null (the constructor initializes both fields
to zero). Also checks that current() is within the stack's
bounds.

  • wtf/WTFThreadData.cpp:

(WTF::WTFThreadData::WTFThreadData):

  • wtf/WTFThreadData.h:

(WTF::WTFThreadData::stack):

Add the StackBounds member variable.

JavaScriptGlue:

Add forwarding header for StackBounds.h.

  • ForwardingHeaders/wtf/StackBounds.h: Added.

WebCore:

Add forwarding header for StackBounds.h.

  • ForwardingHeaders/wtf/StackBounds.h: Added.
File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/JavaScriptCore/runtime/Collector.cpp

    r73623 r74360  
    390390}
    391391
    392 #if OS(WINCE)
    393 JS_EXPORTDATA void* g_stackBase = 0;
    394 
    395 inline bool isPageWritable(void* page)
    396 {
    397     MEMORY_BASIC_INFORMATION memoryInformation;
    398     DWORD result = VirtualQuery(page, &memoryInformation, sizeof(memoryInformation));
    399 
    400     // return false on error, including ptr outside memory
    401     if (result != sizeof(memoryInformation))
    402         return false;
    403 
    404     DWORD protect = memoryInformation.Protect & ~(PAGE_GUARD | PAGE_NOCACHE);
    405     return protect == PAGE_READWRITE
    406         || protect == PAGE_WRITECOPY
    407         || protect == PAGE_EXECUTE_READWRITE
    408         || protect == PAGE_EXECUTE_WRITECOPY;
    409 }
    410 
    411 static void* getStackBase(void* previousFrame)
    412 {
    413     // find the address of this stack frame by taking the address of a local variable
    414     bool isGrowingDownward;
    415     void* thisFrame = (void*)(&isGrowingDownward);
    416 
    417     isGrowingDownward = previousFrame < &thisFrame;
    418     static DWORD pageSize = 0;
    419     if (!pageSize) {
    420         SYSTEM_INFO systemInfo;
    421         GetSystemInfo(&systemInfo);
    422         pageSize = systemInfo.dwPageSize;
    423     }
    424 
    425     // scan all of memory starting from this frame, and return the last writeable page found
    426     register char* currentPage = (char*)((DWORD)thisFrame & ~(pageSize - 1));
    427     if (isGrowingDownward) {
    428         while (currentPage > 0) {
    429             // check for underflow
    430             if (currentPage >= (char*)pageSize)
    431                 currentPage -= pageSize;
    432             else
    433                 currentPage = 0;
    434             if (!isPageWritable(currentPage))
    435                 return currentPage + pageSize;
    436         }
    437         return 0;
    438     } else {
    439         while (true) {
    440             // guaranteed to complete because isPageWritable returns false at end of memory
    441             currentPage += pageSize;
    442             if (!isPageWritable(currentPage))
    443                 return currentPage;
    444         }
    445     }
    446 }
    447 #endif
    448 
    449 #if OS(QNX)
    450 static inline void *currentThreadStackBaseQNX()
    451 {
    452     static void* stackBase = 0;
    453     static size_t stackSize = 0;
    454     static pthread_t stackThread;
    455     pthread_t thread = pthread_self();
    456     if (stackBase == 0 || thread != stackThread) {
    457         struct _debug_thread_info threadInfo;
    458         memset(&threadInfo, 0, sizeof(threadInfo));
    459         threadInfo.tid = pthread_self();
    460         int fd = open("/proc/self", O_RDONLY);
    461         if (fd == -1) {
    462             LOG_ERROR("Unable to open /proc/self (errno: %d)", errno);
    463             return 0;
    464         }
    465         devctl(fd, DCMD_PROC_TIDSTATUS, &threadInfo, sizeof(threadInfo), 0);
    466         close(fd);
    467         stackBase = reinterpret_cast<void*>(threadInfo.stkbase);
    468         stackSize = threadInfo.stksize;
    469         ASSERT(stackBase);
    470         stackThread = thread;
    471     }
    472     return static_cast<char*>(stackBase) + stackSize;
    473 }
    474 #endif
    475 
    476 static inline void* currentThreadStackBase()
    477 {
    478 #if OS(DARWIN)
    479     pthread_t thread = pthread_self();
    480     return pthread_get_stackaddr_np(thread);
    481 #elif OS(WINDOWS) && CPU(X86) && COMPILER(MSVC)
    482     // offset 0x18 from the FS segment register gives a pointer to
    483     // the thread information block for the current thread
    484     NT_TIB* pTib;
    485     __asm {
    486         MOV EAX, FS:[18h]
    487         MOV pTib, EAX
    488     }
    489     return static_cast<void*>(pTib->StackBase);
    490 #elif OS(WINDOWS) && CPU(X86) && COMPILER(GCC)
    491     // offset 0x18 from the FS segment register gives a pointer to
    492     // the thread information block for the current thread
    493     NT_TIB* pTib;
    494     asm ( "movl %%fs:0x18, %0\n"
    495           : "=r" (pTib)
    496         );
    497     return static_cast<void*>(pTib->StackBase);
    498 #elif OS(WINDOWS) && CPU(X86_64)
    499     PNT_TIB64 pTib = reinterpret_cast<PNT_TIB64>(NtCurrentTeb());
    500     return reinterpret_cast<void*>(pTib->StackBase);
    501 #elif OS(QNX)
    502     AtomicallyInitializedStatic(Mutex&, mutex = *new Mutex);
    503     MutexLocker locker(mutex);
    504     return currentThreadStackBaseQNX();
    505 #elif OS(SOLARIS)
    506     stack_t s;
    507     thr_stksegment(&s);
    508     return s.ss_sp;
    509 #elif OS(OPENBSD)
    510     pthread_t thread = pthread_self();
    511     stack_t stack;
    512     pthread_stackseg_np(thread, &stack);
    513     return stack.ss_sp;
    514 #elif OS(SYMBIAN)
    515     TThreadStackInfo info;
    516     RThread thread;
    517     thread.StackInfo(info);
    518     return (void*)info.iBase;
    519 #elif OS(HAIKU)
    520     thread_info threadInfo;
    521     get_thread_info(find_thread(NULL), &threadInfo);
    522     return threadInfo.stack_end;
    523 #elif OS(UNIX)
    524     AtomicallyInitializedStatic(Mutex&, mutex = *new Mutex);
    525     MutexLocker locker(mutex);
    526     static void* stackBase = 0;
    527     static size_t stackSize = 0;
    528     static pthread_t stackThread;
    529     pthread_t thread = pthread_self();
    530     if (stackBase == 0 || thread != stackThread) {
    531         pthread_attr_t sattr;
    532         pthread_attr_init(&sattr);
    533 #if HAVE(PTHREAD_NP_H) || OS(NETBSD)
    534         // e.g. on FreeBSD 5.4, [email protected]
    535         pthread_attr_get_np(thread, &sattr);
    536 #else
    537         // FIXME: this function is non-portable; other POSIX systems may have different np alternatives
    538         pthread_getattr_np(thread, &sattr);
    539 #endif
    540         int rc = pthread_attr_getstack(&sattr, &stackBase, &stackSize);
    541         (void)rc; // FIXME: Deal with error code somehow? Seems fatal.
    542         ASSERT(stackBase);
    543         pthread_attr_destroy(&sattr);
    544         stackThread = thread;
    545     }
    546     return static_cast<char*>(stackBase) + stackSize;
    547 #elif OS(WINCE)
    548     AtomicallyInitializedStatic(Mutex&, mutex = *new Mutex);
    549     MutexLocker locker(mutex);
    550     if (g_stackBase)
    551         return g_stackBase;
    552     else {
    553         int dummy;
    554         return getStackBase(&dummy);
    555     }
    556 #else
    557 #error Need a way to get the stack base on this platform
    558 #endif
    559 }
    560 
    561392#if ENABLE(JSC_MULTIPLE_THREADS)
    562393
     
    588419
    589420    pthread_setspecific(m_currentThreadRegistrar, this);
    590     Heap::Thread* thread = new Heap::Thread(pthread_self(), getCurrentPlatformThread(), currentThreadStackBase());
     421    Heap::Thread* thread = new Heap::Thread(pthread_self(), getCurrentPlatformThread(), m_globalData->stack().origin());
    591422
    592423    MutexLocker lock(m_registeredThreadsMutex);
     
    655486void Heap::markConservatively(MarkStack& markStack, void* start, void* end)
    656487{
     488#if OS(WINCE)
    657489    if (start > end) {
    658490        void* tmp = start;
     
    660492        end = tmp;
    661493    }
     494#else
     495    ASSERT(start <= end);
     496#endif
    662497
    663498    ASSERT((static_cast<char*>(end) - static_cast<char*>(start)) < 0x1000000);
     
    693528void NEVER_INLINE Heap::markCurrentThreadConservativelyInternal(MarkStack& markStack)
    694529{
    695     void* dummy;
    696     void* stackPointer = &dummy;
    697     void* stackBase = currentThreadStackBase();
    698     markConservatively(markStack, stackPointer, stackBase);
     530    markConservatively(markStack, m_globalData->stack().current(), m_globalData->stack().origin());
    699531    markStack.drain();
    700532}
Note: See TracChangeset for help on using the changeset viewer.