Bug 26276 - Need a mechanism to determine stack extent
authorbarraclough@apple.com <barraclough@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 20 Dec 2010 21:16:14 +0000 (21:16 +0000)
committerbarraclough@apple.com <barraclough@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Mon, 20 Dec 2010 21:16:14 +0000 (21:16 +0000)
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.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@74360 268f45cc-cd09-0410-ab3c-d52691b4dbfc

21 files changed:
JavaScriptCore/Android.mk
JavaScriptCore/Android.v8.wtf.mk
JavaScriptCore/ChangeLog
JavaScriptCore/GNUmakefile.am
JavaScriptCore/JavaScriptCore.gypi
JavaScriptCore/JavaScriptCore.vcproj/WTF/WTF.vcproj
JavaScriptCore/JavaScriptCore.xcodeproj/project.pbxproj
JavaScriptCore/parser/JSParser.cpp
JavaScriptCore/runtime/Collector.cpp
JavaScriptCore/runtime/JSGlobalData.cpp
JavaScriptCore/runtime/JSGlobalData.h
JavaScriptCore/wtf/CMakeLists.txt
JavaScriptCore/wtf/StackBounds.cpp [new file with mode: 0644]
JavaScriptCore/wtf/StackBounds.h [new file with mode: 0644]
JavaScriptCore/wtf/WTFThreadData.cpp
JavaScriptCore/wtf/WTFThreadData.h
JavaScriptCore/wtf/wtf.pri
JavaScriptGlue/ChangeLog
JavaScriptGlue/ForwardingHeaders/wtf/StackBounds.h [new file with mode: 0644]
WebCore/ChangeLog
WebCore/ForwardingHeaders/wtf/StackBounds.h [new file with mode: 0644]

index d2f4863..b87ff08 100644 (file)
@@ -165,6 +165,7 @@ LOCAL_SRC_FILES := \
        wtf/PageBlock.cpp\
        wtf/RandomNumber.cpp \
        wtf/RefCountedLeakCounter.cpp \
+    wtf/StackBounds.cpp \
        wtf/TCSystemAlloc.cpp \
        wtf/ThreadIdentifierDataPthreads.cpp \
        wtf/Threading.cpp \
index 17e2816..42cbfe8 100644 (file)
@@ -42,6 +42,7 @@ LOCAL_SRC_FILES := \
        wtf/MainThread.cpp \
        wtf/RandomNumber.cpp \
        wtf/RefCountedLeakCounter.cpp \
+    wtf/StackBounds.cpp \
        wtf/TCSystemAlloc.cpp \
        wtf/ThreadIdentifierDataPthreads.cpp \
        wtf/Threading.cpp \
index 29ce4c8..20be669 100644 (file)
@@ -1,3 +1,90 @@
+2010-12-18  Gavin Barraclough  <barraclough@apple.com>
+
+        Reviewed by Oliver Hunt.
+
+        Bug 26276 - Need a mechanism to determine stack extent
+
+        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.
+
 2010-12-17  Geoffrey Garen  <ggaren@apple.com>
 
         Reviewed by Sam Weinig.
index 05ee93b..e4b5f0b 100644 (file)
@@ -494,6 +494,8 @@ javascriptcore_sources += \
        JavaScriptCore/wtf/RefPtrHashMap.h \
        JavaScriptCore/wtf/RetainPtr.h \
        JavaScriptCore/wtf/SegmentedVector.h \
+    JavaScriptCore/wtf/StackBounds.cpp \
+    JavaScriptCore/wtf/StackBounds.h \
        JavaScriptCore/wtf/StaticConstructors.h \
        JavaScriptCore/wtf/StdLibExtras.h \
        JavaScriptCore/wtf/StringExtras.h \
index 0334ce7..2ea0af1 100644 (file)
             'wtf/RetainPtr.h',
             'wtf/SegmentedVector.h',
             'wtf/SizeLimits.cpp',
+            'wtf/StackBounds.cpp',
+            'wtf/StackBounds.h',
             'wtf/StaticConstructors.h',
             'wtf/StdLibExtras.h',
             'wtf/StringExtras.h',
index 4000c78..acd1c4e 100644 (file)
                        >
                </File>
                <File
+                       RelativePath="..\..\wtf\StackBounds.cpp"
+                       >
+               </File>
+               <File
+                       RelativePath="..\..\wtf\StackBounds.h"
+                       >
+               </File>
+               <File
                        RelativePath="..\..\wtf\StaticConstructors.h"
                        >
                </File>
index 0ef6fdd..76af919 100644 (file)
                86D3B2C610156BDE002865E7 /* MacroAssemblerARM.h in Headers */ = {isa = PBXBuildFile; fileRef = 86D3B2C210156BDE002865E7 /* MacroAssemblerARM.h */; };
                86D3B3C310159D7F002865E7 /* LinkBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 86D3B3C110159D7F002865E7 /* LinkBuffer.h */; };
                86D3B3C410159D7F002865E7 /* RepatchBuffer.h in Headers */ = {isa = PBXBuildFile; fileRef = 86D3B3C210159D7F002865E7 /* RepatchBuffer.h */; };
+               86D87DAE12BCA7D1008E73A1 /* StackBounds.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 86D87DA512BC4B14008E73A1 /* StackBounds.cpp */; };
+               86D87DDB12BCAF94008E73A1 /* StackBounds.h in Headers */ = {isa = PBXBuildFile; fileRef = 86D87DA612BC4B14008E73A1 /* StackBounds.h */; settings = {ATTRIBUTES = (Private, ); }; };
                86DB64640F95C6FC00D7D921 /* ExecutableAllocatorFixedVMPool.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 86DB64630F95C6FC00D7D921 /* ExecutableAllocatorFixedVMPool.cpp */; };
                86E116B10FE75AC800B512BC /* CodeLocation.h in Headers */ = {isa = PBXBuildFile; fileRef = 86E116B00FE75AC800B512BC /* CodeLocation.h */; };
                86E85539111B9968001AF51E /* JSStringBuilder.h in Headers */ = {isa = PBXBuildFile; fileRef = 86E85538111B9968001AF51E /* JSStringBuilder.h */; };
                86D3B2C210156BDE002865E7 /* MacroAssemblerARM.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MacroAssemblerARM.h; sourceTree = "<group>"; };
                86D3B3C110159D7F002865E7 /* LinkBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = LinkBuffer.h; sourceTree = "<group>"; };
                86D3B3C210159D7F002865E7 /* RepatchBuffer.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RepatchBuffer.h; sourceTree = "<group>"; };
+               86D87DA512BC4B14008E73A1 /* StackBounds.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = StackBounds.cpp; sourceTree = "<group>"; };
+               86D87DA612BC4B14008E73A1 /* StackBounds.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = StackBounds.h; sourceTree = "<group>"; };
                86DB64630F95C6FC00D7D921 /* ExecutableAllocatorFixedVMPool.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = ExecutableAllocatorFixedVMPool.cpp; sourceTree = "<group>"; };
                86E116B00FE75AC800B512BC /* CodeLocation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CodeLocation.h; sourceTree = "<group>"; };
                86E85538111B9968001AF51E /* JSStringBuilder.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = JSStringBuilder.h; sourceTree = "<group>"; };
                                51F648D60BB4E2CA0033D760 /* RetainPtr.h */,
                                969A07290ED1CE6900F1F681 /* SegmentedVector.h */,
                                0BF28A2811A33DC300638F84 /* SizeLimits.cpp */,
+                               86D87DA512BC4B14008E73A1 /* StackBounds.cpp */,
+                               86D87DA612BC4B14008E73A1 /* StackBounds.h */,
                                868BFA5F117D048200B908B1 /* StaticConstructors.h */,
                                FE1B44790ECCD73B004F4DD1 /* StdLibExtras.h */,
                                E11D51750B2E798D0056C188 /* StringExtras.h */,
                                BC18C4640E16F5CD00B34460 /* SourceCode.h in Headers */,
                                BC18C4630E16F5CD00B34460 /* SourceProvider.h in Headers */,
                                A7386554118697B400540279 /* SpecializedThunkJIT.h in Headers */,
+                               86D87DDB12BCAF94008E73A1 /* StackBounds.h in Headers */,
                                868BFA60117D048200B908B1 /* StaticConstructors.h in Headers */,
                                FE1B447A0ECCD73B004F4DD1 /* StdLibExtras.h in Headers */,
                                86B99AE3117E578100DF5A90 /* StringBuffer.h in Headers */,
                                14469DEA107EC7E700650446 /* ScopeChain.cpp in Sources */,
                                0BF28A2911A33DC300638F84 /* SizeLimits.cpp in Sources */,
                                9330402C0E6A764000786E6A /* SmallStrings.cpp in Sources */,
+                               86D87DAE12BCA7D1008E73A1 /* StackBounds.cpp in Sources */,
                                14469DEB107EC7E700650446 /* StringConstructor.cpp in Sources */,
                                868BFA0E117CEFD100B908B1 /* StringImpl.cpp in Sources */,
                                14469DEC107EC7E700650446 /* StringObject.cpp in Sources */,
index a0aa875..3ae05cc 100644 (file)
@@ -65,9 +65,6 @@ namespace JSC {
 
 COMPILE_ASSERT(LastUntaggedToken < 64, LessThan64UntaggedTokens);
 
-// This matches v8
-static const ptrdiff_t kMaxParserStackUsage = 128 * sizeof(void*) * 1024;
-
 class JSParser {
 public:
     JSParser(Lexer*, JSGlobalData*, FunctionParameters*, bool isStrictContext, bool isFunction, SourceProvider*);
@@ -193,9 +190,7 @@ private:
 
     bool canRecurse()
     {
-        char sample = 0;
-        ASSERT(m_endAddress);
-        return &sample > m_endAddress;
+        return m_stack.recursionCheck();
     }
     
     int lastTokenEnd() const
@@ -205,7 +200,7 @@ private:
 
     ParserArena m_arena;
     Lexer* m_lexer;
-    char* m_endAddress;
+    StackBounds m_stack;
     bool m_error;
     JSGlobalData* m_globalData;
     JSToken m_token;
@@ -491,7 +486,7 @@ int jsParse(JSGlobalObject* lexicalGlobalObject, FunctionParameters* parameters,
 
 JSParser::JSParser(Lexer* lexer, JSGlobalData* globalData, FunctionParameters* parameters, bool inStrictContext, bool isFunction, SourceProvider* provider)
     : m_lexer(lexer)
-    , m_endAddress(0)
+    , m_stack(globalData->stack())
     , m_error(false)
     , m_globalData(globalData)
     , m_allowsIn(true)
@@ -505,7 +500,6 @@ JSParser::JSParser(Lexer* lexer, JSGlobalData* globalData, FunctionParameters* p
     , m_nonTrivialExpressionCount(0)
     , m_lastIdentifier(0)
 {
-    m_endAddress = wtfThreadData().approximatedStackStart() - kMaxParserStackUsage;
     ScopeRef scope = pushScope();
     if (isFunction)
         scope->setIsFunction();
index f84f18c..c53cae5 100644 (file)
@@ -389,175 +389,6 @@ void Heap::shrinkBlocks(size_t neededBlocks)
         m_heap.collectorBlock(i)->marked.set(HeapConstants::cellsPerBlock - 1);
 }
 
-#if OS(WINCE)
-JS_EXPORTDATA void* g_stackBase = 0;
-
-inline bool isPageWritable(void* page)
-{
-    MEMORY_BASIC_INFORMATION memoryInformation;
-    DWORD result = VirtualQuery(page, &memoryInformation, sizeof(memoryInformation));
-
-    // return false on error, including ptr outside memory
-    if (result != sizeof(memoryInformation))
-        return false;
-
-    DWORD protect = memoryInformation.Protect & ~(PAGE_GUARD | PAGE_NOCACHE);
-    return protect == PAGE_READWRITE
-        || protect == PAGE_WRITECOPY
-        || protect == PAGE_EXECUTE_READWRITE
-        || protect == PAGE_EXECUTE_WRITECOPY;
-}
-
-static void* getStackBase(void* previousFrame)
-{
-    // find the address of this stack frame by taking the address of a local variable
-    bool isGrowingDownward;
-    void* thisFrame = (void*)(&isGrowingDownward);
-
-    isGrowingDownward = previousFrame < &thisFrame;
-    static DWORD pageSize = 0;
-    if (!pageSize) {
-        SYSTEM_INFO systemInfo;
-        GetSystemInfo(&systemInfo);
-        pageSize = systemInfo.dwPageSize;
-    }
-
-    // scan all of memory starting from this frame, and return the last writeable page found
-    register char* currentPage = (char*)((DWORD)thisFrame & ~(pageSize - 1));
-    if (isGrowingDownward) {
-        while (currentPage > 0) {
-            // check for underflow
-            if (currentPage >= (char*)pageSize)
-                currentPage -= pageSize;
-            else
-                currentPage = 0;
-            if (!isPageWritable(currentPage))
-                return currentPage + pageSize;
-        }
-        return 0;
-    } else {
-        while (true) {
-            // guaranteed to complete because isPageWritable returns false at end of memory
-            currentPage += pageSize;
-            if (!isPageWritable(currentPage))
-                return currentPage;
-        }
-    }
-}
-#endif
-
-#if OS(QNX)
-static inline void *currentThreadStackBaseQNX()
-{
-    static void* stackBase = 0;
-    static size_t stackSize = 0;
-    static pthread_t stackThread;
-    pthread_t thread = pthread_self();
-    if (stackBase == 0 || thread != stackThread) {
-        struct _debug_thread_info threadInfo;
-        memset(&threadInfo, 0, sizeof(threadInfo));
-        threadInfo.tid = pthread_self();
-        int fd = open("/proc/self", O_RDONLY);
-        if (fd == -1) {
-            LOG_ERROR("Unable to open /proc/self (errno: %d)", errno);
-            return 0;
-        }
-        devctl(fd, DCMD_PROC_TIDSTATUS, &threadInfo, sizeof(threadInfo), 0);
-        close(fd);
-        stackBase = reinterpret_cast<void*>(threadInfo.stkbase);
-        stackSize = threadInfo.stksize;
-        ASSERT(stackBase);
-        stackThread = thread;
-    }
-    return static_cast<char*>(stackBase) + stackSize;
-}
-#endif
-
-static inline void* currentThreadStackBase()
-{
-#if OS(DARWIN)
-    pthread_t thread = pthread_self();
-    return pthread_get_stackaddr_np(thread);
-#elif OS(WINDOWS) && CPU(X86) && COMPILER(MSVC)
-    // offset 0x18 from the FS segment register gives a pointer to
-    // the thread information block for the current thread
-    NT_TIB* pTib;
-    __asm {
-        MOV EAX, FS:[18h]
-        MOV pTib, EAX
-    }
-    return static_cast<void*>(pTib->StackBase);
-#elif OS(WINDOWS) && CPU(X86) && COMPILER(GCC)
-    // offset 0x18 from the FS segment register gives a pointer to
-    // the thread information block for the current thread
-    NT_TIB* pTib;
-    asm ( "movl %%fs:0x18, %0\n"
-          : "=r" (pTib)
-        );
-    return static_cast<void*>(pTib->StackBase);
-#elif OS(WINDOWS) && CPU(X86_64)
-    PNT_TIB64 pTib = reinterpret_cast<PNT_TIB64>(NtCurrentTeb());
-    return reinterpret_cast<void*>(pTib->StackBase);
-#elif OS(QNX)
-    AtomicallyInitializedStatic(Mutex&, mutex = *new Mutex);
-    MutexLocker locker(mutex);
-    return currentThreadStackBaseQNX();
-#elif OS(SOLARIS)
-    stack_t s;
-    thr_stksegment(&s);
-    return s.ss_sp;
-#elif OS(OPENBSD)
-    pthread_t thread = pthread_self();
-    stack_t stack;
-    pthread_stackseg_np(thread, &stack);
-    return stack.ss_sp;
-#elif OS(SYMBIAN)
-    TThreadStackInfo info;
-    RThread thread;
-    thread.StackInfo(info);
-    return (void*)info.iBase;
-#elif OS(HAIKU)
-    thread_info threadInfo;
-    get_thread_info(find_thread(NULL), &threadInfo);
-    return threadInfo.stack_end;
-#elif OS(UNIX)
-    AtomicallyInitializedStatic(Mutex&, mutex = *new Mutex);
-    MutexLocker locker(mutex);
-    static void* stackBase = 0;
-    static size_t stackSize = 0;
-    static pthread_t stackThread;
-    pthread_t thread = pthread_self();
-    if (stackBase == 0 || thread != stackThread) {
-        pthread_attr_t sattr;
-        pthread_attr_init(&sattr);
-#if HAVE(PTHREAD_NP_H) || OS(NETBSD)
-        // e.g. on FreeBSD 5.4, neundorf@kde.org
-        pthread_attr_get_np(thread, &sattr);
-#else
-        // FIXME: this function is non-portable; other POSIX systems may have different np alternatives
-        pthread_getattr_np(thread, &sattr);
-#endif
-        int rc = pthread_attr_getstack(&sattr, &stackBase, &stackSize);
-        (void)rc; // FIXME: Deal with error code somehow? Seems fatal.
-        ASSERT(stackBase);
-        pthread_attr_destroy(&sattr);
-        stackThread = thread;
-    }
-    return static_cast<char*>(stackBase) + stackSize;
-#elif OS(WINCE)
-    AtomicallyInitializedStatic(Mutex&, mutex = *new Mutex);
-    MutexLocker locker(mutex);
-    if (g_stackBase)
-        return g_stackBase;
-    else {
-        int dummy;
-        return getStackBase(&dummy);
-    }
-#else
-#error Need a way to get the stack base on this platform
-#endif
-}
-
 #if ENABLE(JSC_MULTIPLE_THREADS)
 
 static inline PlatformThread getCurrentPlatformThread()
@@ -587,7 +418,7 @@ void Heap::registerThread()
         return;
 
     pthread_setspecific(m_currentThreadRegistrar, this);
-    Heap::Thread* thread = new Heap::Thread(pthread_self(), getCurrentPlatformThread(), currentThreadStackBase());
+    Heap::Thread* thread = new Heap::Thread(pthread_self(), getCurrentPlatformThread(), m_globalData->stack().origin());
 
     MutexLocker lock(m_registeredThreadsMutex);
 
@@ -654,11 +485,15 @@ static inline bool isPossibleCell(void* p)
 
 void Heap::markConservatively(MarkStack& markStack, void* start, void* end)
 {
+#if OS(WINCE)
     if (start > end) {
         void* tmp = start;
         start = end;
         end = tmp;
     }
+#else
+    ASSERT(start <= end);
+#endif
 
     ASSERT((static_cast<char*>(end) - static_cast<char*>(start)) < 0x1000000);
     ASSERT(isPointerAligned(start));
@@ -692,10 +527,7 @@ void Heap::markConservatively(MarkStack& markStack, void* start, void* end)
 
 void NEVER_INLINE Heap::markCurrentThreadConservativelyInternal(MarkStack& markStack)
 {
-    void* dummy;
-    void* stackPointer = &dummy;
-    void* stackBase = currentThreadStackBase();
-    markConservatively(markStack, stackPointer, stackBase);
+    markConservatively(markStack, m_globalData->stack().current(), m_globalData->stack().origin());
     markStack.drain();
 }
 
index 9948877..6f1de3b 100644 (file)
@@ -153,6 +153,9 @@ JSGlobalData::JSGlobalData(GlobalDataType globalDataType, ThreadStackType thread
     , exclusiveThread(0)
 #endif
 {
+    if (globalDataType == Default)
+        m_stack = wtfThreadData().stack();
+
 #if PLATFORM(MAC)
     startProfilerServerIfNeeded();
 #endif
index 1819a0c..c6729c4 100644 (file)
@@ -46,6 +46,7 @@
 #include <wtf/HashMap.h>
 #include <wtf/RefCounted.h>
 #include <wtf/ThreadSpecific.h>
+#include <wtf/WTFThreadData.h>
 #if ENABLE(REGEXP_TRACING)
 #include <wtf/ListHashSet.h>
 #endif
@@ -178,6 +179,14 @@ namespace JSC {
 #else
         bool canUseJIT() { return m_canUseJIT; }
 #endif
+
+        const StackBounds& stack()
+        {
+            return (globalDataType == Default)
+                ? m_stack
+                : wtfThreadData().stack();
+        }
+
         Lexer* lexer;
         Parser* parser;
         Interpreter* interpreter;
@@ -250,6 +259,7 @@ namespace JSC {
 #if ENABLE(JIT) && ENABLE(INTERPRETER)
         bool m_canUseJIT;
 #endif
+        StackBounds m_stack;
     };
 
 } // namespace JSC
index 898d19b..98cd7e4 100644 (file)
@@ -9,6 +9,7 @@ SET(WTF_SOURCES
     MD5.cpp
     RandomNumber.cpp
     RefCountedLeakCounter.cpp
+    StackBounds.cpp
     StringExtras.cpp
     Threading.cpp
     TypeTraits.cpp
diff --git a/JavaScriptCore/wtf/StackBounds.cpp b/JavaScriptCore/wtf/StackBounds.cpp
new file mode 100644 (file)
index 0000000..2000f31
--- /dev/null
@@ -0,0 +1,272 @@
+/*
+ *  Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved.
+ *  Copyright (C) 2007 Eric Seidel <eric@webkit.org>
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License as published by the Free Software Foundation; either
+ *  version 2 of the License, or (at your option) any later version.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
+ *
+ */
+
+#include "config.h"
+#include "StackBounds.h"
+
+#if OS(DARWIN)
+
+#include <mach/task.h>
+#include <mach/thread_act.h>
+
+#elif OS(WINDOWS)
+
+#include <windows.h>
+
+#elif OS(HAIKU)
+
+#include <OS.h>
+
+#elif OS(SOLARIS)
+
+#include <thread.h>
+
+#elif OS(QNX)
+
+#include <fcntl.h>
+#include <sys/procfs.h>
+#include <stdio.h>
+#include <errno.h>
+
+#elif OS(UNIX)
+
+#include <pthread.h>
+#if HAVE(PTHREAD_NP_H)
+#include <pthread_np.h>
+#endif
+
+#endif
+
+namespace WTF {
+
+// Based on the current limit used by the JSC parser, guess the stack size.
+static const ptrdiff_t estimatedStackSize = 128 * sizeof(void*) * 1024;
+
+// This method assumes the stack is growing downwards.
+static void* estimateStackBound(void* origin)
+{
+    return static_cast<char*>(origin) - estimatedStackSize;
+}
+
+#if OS(DARWIN)
+
+void StackBounds::initialize()
+{
+    pthread_t thread = pthread_self();
+    m_origin = pthread_get_stackaddr_np(thread);
+    m_bound = estimateStackBound(m_origin);
+}
+
+#elif OS(WINDOWS)
+
+void StackBounds::initialize()
+{
+#if CPU(X86) && COMPILER(MSVC)
+    // offset 0x18 from the FS segment register gives a pointer to
+    // the thread information block for the current thread
+    NT_TIB* pTib;
+    __asm {
+        MOV EAX, FS:[18h]
+        MOV pTib, EAX
+    }
+    m_origin = static_cast<void*>(pTib->StackBase);
+#elif OS(WINDOWS) && CPU(X86) && COMPILER(GCC)
+    // offset 0x18 from the FS segment register gives a pointer to
+    // the thread information block for the current thread
+    NT_TIB* pTib;
+    asm ( "movl %%fs:0x18, %0\n"
+          : "=r" (pTib)
+        );
+    m_origin = static_cast<void*>(pTib->StackBase);
+#elif OS(WINDOWS) && CPU(X86_64)
+    PNT_TIB64 pTib = reinterpret_cast<PNT_TIB64>(NtCurrentTeb());
+    m_origin = reinterpret_cast<void*>(pTib->StackBase);
+#else
+#error Need a way to get the stack bounds on this platform (Windows)
+#endif
+    m_bound = estimateStackBound(m_origin);
+}
+
+#elif OS(QNX)
+
+void StackBounds::initialize()
+{
+    void* stackBase = 0;
+    size_t stackSize = 0;
+    pthread_t thread = pthread_self();
+
+    struct _debug_thread_info threadInfo;
+    memset(&threadInfo, 0, sizeof(threadInfo));
+    threadInfo.tid = pthread_self();
+    int fd = open("/proc/self", O_RDONLY);
+    if (fd == -1) {
+        LOG_ERROR("Unable to open /proc/self (errno: %d)", errno);
+        CRASH();
+    }
+    devctl(fd, DCMD_PROC_TIDSTATUS, &threadInfo, sizeof(threadInfo), 0);
+    close(fd);
+    stackBase = reinterpret_cast<void*>(threadInfo.stkbase);
+    stackSize = threadInfo.stksize;
+    ASSERT(stackBase);
+
+    m_origin = static_cast<char*>(stackBase) + stackSize;
+    m_bound = estimateStackBound(m_origin);
+}
+
+#elif OS(SOLARIS)
+
+void StackBounds::initialize()
+{
+    stack_t s;
+    thr_stksegment(&s);
+    m_origin = s.ss_sp;
+    m_bound = estimateStackBound(m_origin);
+}
+
+#elif OS(OPENBSD)
+
+void StackBounds::initialize()
+{
+    pthread_t thread = pthread_self();
+    stack_t stack;
+    pthread_stackseg_np(thread, &stack);
+    m_origin = stack.ss_sp;
+    m_bound = estimateStackBound(m_origin);
+}
+
+#elif OS(SYMBIAN)
+
+void StackBounds::initialize()
+{
+    TThreadStackInfo info;
+    RThread thread;
+    thread.StackInfo(info);
+    m_origin = (void*)info.iBase;
+    m_bound = estimateStackBound(m_origin);
+}
+
+#elif OS(HAIKU)
+
+void StackBounds::initialize()
+{
+    thread_info threadInfo;
+    get_thread_info(find_thread(NULL), &threadInfo);
+    m_origin = threadInfo.stack_end;
+    m_bound = estimateStackBound(m_origin);
+}
+
+#elif OS(UNIX)
+
+void StackBounds::initialize()
+{
+    void* stackBase = 0;
+    size_t stackSize = 0;
+
+    pthread_t thread = pthread_self();
+    pthread_attr_t sattr;
+    pthread_attr_init(&sattr);
+#if HAVE(PTHREAD_NP_H) || OS(NETBSD)
+    // e.g. on FreeBSD 5.4, neundorf@kde.org
+    pthread_attr_get_np(thread, &sattr);
+#else
+    // FIXME: this function is non-portable; other POSIX systems may have different np alternatives
+    pthread_getattr_np(thread, &sattr);
+#endif
+    int rc = pthread_attr_getstack(&sattr, &stackBase, &stackSize);
+    (void)rc; // FIXME: Deal with error code somehow? Seems fatal.
+    ASSERT(stackBase);
+    pthread_attr_destroy(&sattr);
+    m_origin = static_cast<char*>(stackBase) + stackSize;
+    m_bound = estimateStackBound(m_origin);
+}
+
+#elif OS(WINCE)
+
+} // namespace WTF
+// FIXME: this is not threadsafe, and should probably be removed.
+namespace JSC { JS_EXPORTDATA void* g_stackBase = 0; }
+namespace WTF {
+
+inline bool isPageWritable(void* page)
+{
+    MEMORY_BASIC_INFORMATION memoryInformation;
+    DWORD result = VirtualQuery(page, &memoryInformation, sizeof(memoryInformation));
+
+    // return false on error, including ptr outside memory
+    if (result != sizeof(memoryInformation))
+        return false;
+
+    DWORD protect = memoryInformation.Protect & ~(PAGE_GUARD | PAGE_NOCACHE);
+    return protect == PAGE_READWRITE
+        || protect == PAGE_WRITECOPY
+        || protect == PAGE_EXECUTE_READWRITE
+        || protect == PAGE_EXECUTE_WRITECOPY;
+}
+
+static void* getStackMax(void* previousFrame, bool& isGrowingDownward)
+{
+    // find the address of this stack frame by taking the address of a local variable
+    void* thisFrame = &thisFrame;
+    isGrowingDownward = previousFrame < &thisFrame;
+
+    if (JSC::g_stackBase)
+        return JSC::g_stackBase;
+
+    SYSTEM_INFO systemInfo;
+    GetSystemInfo(&systemInfo);
+    DWORD pageSize = systemInfo.dwPageSize;
+
+    // scan all of memory starting from this frame, and return the last writeable page found
+    register char* currentPage = (char*)((DWORD)thisFrame & ~(pageSize - 1));
+    if (isGrowingDownward) {
+        while (currentPage > 0) {
+            // check for underflow
+            if (currentPage >= (char*)pageSize)
+                currentPage -= pageSize;
+            else
+                currentPage = 0;
+            if (!isPageWritable(currentPage))
+                return currentPage + pageSize;
+        }
+        return 0;
+    } else {
+        while (true) {
+            // guaranteed to complete because isPageWritable returns false at end of memory
+            currentPage += pageSize;
+            if (!isPageWritable(currentPage))
+                return currentPage;
+        }
+    }
+}
+
+void StackBounds::initialize()
+{
+    bool isGrowingDownward
+    m_origin = getStackMax(isGrowingDownward);
+    m_bound = isGrowingDownward
+        ? static_cast<char*>(m_origin) - estimatedStackSize
+        : static_cast<char*>(m_origin) + estimatedStackSize;
+}
+
+#else
+#error Need a way to get the stack bounds on this platform
+#endif
+
+} // namespace WTF
diff --git a/JavaScriptCore/wtf/StackBounds.h b/JavaScriptCore/wtf/StackBounds.h
new file mode 100644 (file)
index 0000000..afce860
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2010 Apple Inc. All Rights Reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+ *
+ */
+
+#ifndef StackBounds_h
+#define StackBounds_h
+
+namespace WTF {
+
+class StackBounds {
+    // recursionCheck() / recursionLimit() tests (by default)
+    // that we are at least this far from the end of the stack.
+    const static size_t s_defaultAvailabilityDelta = 4096;
+
+public:
+    StackBounds()
+        : m_origin(0)
+        , m_bound(0)
+    {
+    }
+
+    static StackBounds currentThreadStackBounds()
+    {
+        StackBounds bounds;
+        bounds.initialize();
+        bounds.checkConsistency();
+        return bounds;
+    }
+
+    void* origin() const
+    {
+        ASSERT(m_origin);
+        return m_origin;
+    }
+
+    void* current() const
+    {
+        checkConsistency();
+        void* currentPosition = &currentPosition;
+        return currentPosition;
+    }
+
+    void* recursionLimit(size_t minAvailableDelta = s_defaultAvailabilityDelta) const
+    {
+        checkConsistency();
+        return isGrowingDownward()
+            ? static_cast<char*>(m_bound) + minAvailableDelta
+            : static_cast<char*>(m_bound) - minAvailableDelta;
+    }
+
+    bool recursionCheck(size_t minAvailableDelta = s_defaultAvailabilityDelta) const
+    {
+        checkConsistency();
+        return isGrowingDownward()
+            ? current() >= recursionLimit(minAvailableDelta)
+            : current() <= recursionLimit(minAvailableDelta);
+    }
+
+private:
+    void initialize();
+
+
+    bool isGrowingDownward() const
+    {
+        ASSERT(m_origin && m_bound);
+#if OS(WINCE)
+        return m_origin > m_bound;
+#else
+        return true;
+#endif
+    }
+
+    void checkConsistency() const
+    {
+#if !ASSERT_DISABLED
+        void* currentPosition = &currentPosition;
+        ASSERT(m_origin != m_bound);
+        ASSERT(isGrowingDownward()
+            ? (currentPosition < m_origin && currentPosition > m_bound)
+            : (currentPosition > m_origin && currentPosition < m_bound));
+#endif
+    }
+
+    void* m_origin;
+    void* m_bound;
+};
+
+} // namespace WTF
+
+using WTF::StackBounds;
+
+#endif
index 702baed..05be8d1 100644 (file)
@@ -42,9 +42,8 @@ WTFThreadData::WTFThreadData()
     , m_defaultIdentifierTable(new JSC::IdentifierTable())
     , m_currentIdentifierTable(m_defaultIdentifierTable)
 #endif
+    , m_stackBounds(StackBounds::currentThreadStackBounds())
 {
-    char sample = 0;
-    m_approximatedStackStart = &sample;
 }
 
 WTFThreadData::~WTFThreadData()
index 5019c33..da1b6eb 100644 (file)
@@ -30,6 +30,7 @@
 #include <wtf/HashMap.h>
 #include <wtf/HashSet.h>
 #include <wtf/Noncopyable.h>
+#include <wtf/StackBounds.h>
 #include <wtf/text/StringHash.h>
 
 // This was ENABLE(WORKERS) in WebCore, but this is not defined when compiling JSC.
@@ -113,9 +114,9 @@ public:
     }
 #endif
 
-    char* approximatedStackStart() const
+    const StackBounds& stack() const
     {
-        return m_approximatedStackStart;
+        return m_stackBounds;
     }
 
 private:
@@ -135,7 +136,7 @@ private:
     friend WTFThreadData& wtfThreadData();
     friend class AtomicStringTable;
 
-    char* m_approximatedStackStart;
+    StackBounds m_stackBounds;
 };
 
 inline WTFThreadData& wtfThreadData()
index a6af879..a823cbd 100644 (file)
@@ -19,6 +19,7 @@ SOURCES += \
     wtf/PageBlock.cpp \
     wtf/RandomNumber.cpp \
     wtf/RefCountedLeakCounter.cpp \
+    wtf/StackBounds.cpp \
     wtf/ThreadingNone.cpp \
     wtf/Threading.cpp \
     wtf/TypeTraits.cpp \
index 855f7c7..8485701 100644 (file)
@@ -1,3 +1,13 @@
+2010-12-18  Gavin Barraclough  <barraclough@apple.com>
+
+        Reviewed by Oliver Hunt.
+
+        Bug 26276 - Need a mechanism to determine stack extent
+
+        Add forwarding header for StackBounds.h.
+
+        * ForwardingHeaders/wtf/StackBounds.h: Added.
+
 2010-12-20  Geoffrey Garen  <ggaren@apple.com>
 
         Reviewed by Sam Weinig.
diff --git a/JavaScriptGlue/ForwardingHeaders/wtf/StackBounds.h b/JavaScriptGlue/ForwardingHeaders/wtf/StackBounds.h
new file mode 100644 (file)
index 0000000..dfec694
--- /dev/null
@@ -0,0 +1 @@
+#include <JavaScriptCore/StackBounds.h>
index fe3d24d..03b3548 100644 (file)
@@ -1,3 +1,13 @@
+2010-12-18  Gavin Barraclough  <barraclough@apple.com>
+
+        Reviewed by Oliver Hunt.
+
+        Bug 26276 - Need a mechanism to determine stack extent
+
+        Add forwarding header for StackBounds.h.
+
+        * ForwardingHeaders/wtf/StackBounds.h: Added.
+
 2010-12-20  James Robinson  <jamesr@chromium.org>
 
         Unreviewed, rolling out r74278.
diff --git a/WebCore/ForwardingHeaders/wtf/StackBounds.h b/WebCore/ForwardingHeaders/wtf/StackBounds.h
new file mode 100644 (file)
index 0000000..770265e
--- /dev/null
@@ -0,0 +1,4 @@
+#ifndef WebCore_FWD_StackBounds_h
+#define WebCore_FWD_StackBounds_h
+#include <JavaScriptCore/StackBounds.h>
+#endif