From: Simon Hausmann Date: Sat, 8 Dec 2012 17:22:25 +0000 (+0100) Subject: Add initial version of stack walker X-Git-Tag: upstream/5.2.1~669^2~659^2~729 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=da3c085626d965acd5ff52c304497759be86740a;p=platform%2Fupstream%2Fqtdeclarative.git Add initial version of stack walker Not used yet Change-Id: Id096f7efd5582d2a20c3b921180be0ebf48996a5 Reviewed-by: Lars Knoll --- diff --git a/masm/masm.pri b/masm/masm.pri index 90f30b9..642926d 100644 --- a/masm/masm.pri +++ b/masm/masm.pri @@ -6,6 +6,9 @@ SOURCES += $$PWD/assembler/MacroAssemblerARM.cpp SOURCES += $$PWD/assembler/MacroAssemblerSH4.cpp SOURCES += $$PWD/assembler/LinkBuffer.cpp +SOURCES += $$PWD/wtf/StackBounds.cpp +HEADERS += $$PWD/wtf/StackBounds.h + SOURCES += $$PWD/stubs/WTFStubs.cpp HEADERS += $$PWD/stubs/WTFStubs.h diff --git a/masm/wtf/StackBounds.cpp b/masm/wtf/StackBounds.cpp new file mode 100644 index 0000000..a272ce3 --- /dev/null +++ b/masm/wtf/StackBounds.cpp @@ -0,0 +1,261 @@ +/* + * Copyright (C) 2003, 2004, 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. + * Copyright (C) 2007 Eric Seidel + * + * 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 +#include +#include + +#elif OS(WINDOWS) + +#include + +#elif OS(SOLARIS) + +#include + +#elif OS(QNX) + +#include +#include +#include +#include +#include +#include + +#elif OS(UNIX) + +#include +#if HAVE(PTHREAD_NP_H) +#include +#endif + +#endif + +namespace WTF { + +// Bug 26276 - Need a mechanism to determine stack extent +// +// These platforms should now be working correctly: +// DARWIN, QNX, UNIX +// These platforms are not: +// WINDOWS, SOLARIS, OPENBSD, WINCE +// +// FIXME: remove this! - this code unsafely guesses at stack sizes! +#if OS(WINDOWS) || OS(SOLARIS) || OS(OPENBSD) +// 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(origin) - estimatedStackSize; +} +#endif + +#if OS(DARWIN) + +void StackBounds::initialize() +{ + pthread_t thread = pthread_self(); + m_origin = pthread_get_stackaddr_np(thread); + m_bound = static_cast(m_origin) - pthread_get_stacksize_np(thread); +} + +#elif OS(QNX) + +void StackBounds::initialize() +{ + void* stackBase = 0; + size_t stackSize = 0; + + 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(threadInfo.stkbase); + stackSize = threadInfo.stksize; + ASSERT(stackBase); + + m_bound = static_cast(stackBase) + 0x1000; // 4kb guard page + m_origin = static_cast(stackBase) + stackSize; +} + +#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(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_bound = stackBase; + m_origin = static_cast(stackBase) + stackSize; +} + +#elif OS(WINCE) + +static bool detectGrowingDownward(void* previousFrame) +{ + // Find the address of this stack frame by taking the address of a local variable. + int thisFrame; + return previousFrame > &thisFrame; +} + +static 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 inline void* getLowerStackBound(char* currentPage, DWORD pageSize) +{ + while (currentPage > 0) { + // check for underflow + if (currentPage >= reinterpret_cast(pageSize)) + currentPage -= pageSize; + else + currentPage = 0; + + if (!isPageWritable(currentPage)) + return currentPage + pageSize; + } + + return 0; +} + +static inline void* getUpperStackBound(char* currentPage, DWORD pageSize) +{ + do { + // guaranteed to complete because isPageWritable returns false at end of memory + currentPage += pageSize; + } while (isPageWritable(currentPage)); + + return currentPage - pageSize; +} + +void StackBounds::initialize() +{ + // find the address of this stack frame by taking the address of a local variable + void* thisFrame = &thisFrame; + bool isGrowingDownward = detectGrowingDownward(thisFrame); + + SYSTEM_INFO systemInfo; + GetSystemInfo(&systemInfo); + DWORD pageSize = systemInfo.dwPageSize; + + // scan all of memory starting from this frame, and return the last writeable page found + char* currentPage = reinterpret_cast(reinterpret_cast(thisFrame) & ~(pageSize - 1)); + void* lowerStackBound = getLowerStackBound(currentPage, pageSize); + void* upperStackBound = getUpperStackBound(currentPage, pageSize); + + m_origin = isGrowingDownward ? upperStackBound : lowerStackBound; + m_bound = isGrowingDownward ? lowerStackBound : upperStackBound; +} + +#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(pTib->StackBase); +#elif 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(pTib->StackBase); +#elif CPU(X86_64) + PNT_TIB64 pTib = reinterpret_cast(NtCurrentTeb()); + m_origin = reinterpret_cast(pTib->StackBase); +#else +#error Need a way to get the stack bounds on this platform (Windows) +#endif + // Looks like we should be able to get pTib->StackLimit + m_bound = estimateStackBound(m_origin); +} + +#else +#error Need a way to get the stack bounds on this platform +#endif + +} // namespace WTF diff --git a/masm/wtf/StackBounds.h b/masm/wtf/StackBounds.h new file mode 100644 index 0000000..185afec --- /dev/null +++ b/masm/wtf/StackBounds.h @@ -0,0 +1,129 @@ +/* + * 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 { + // isSafeToRecurse() / recursionLimit() tests (by default) + // that we are at least this far from the end of the stack. + // + // This 64k number was picked because a sampling of stack usage differences + // between consecutive entries into one of the Interpreter::execute...() + // functions was seen to be as high as 27k. Hence, 64k is chosen as a + // conservative availability value that is not too large but comfortably + // exceeds 27k with some buffer for error. + const static size_t s_defaultAvailabilityDelta = 64 * 1024; + +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 = ¤tPosition; + return currentPosition; + } + + size_t size() const + { + return isGrowingDownward() + ? static_cast(m_origin) - static_cast(m_bound) + : static_cast(m_bound) - static_cast(m_origin); + } + + void* recursionLimit(size_t minAvailableDelta = s_defaultAvailabilityDelta) const + { + checkConsistency(); + return isGrowingDownward() + ? static_cast(m_bound) + minAvailableDelta + : static_cast(m_bound) - minAvailableDelta; + } + + bool isSafeToRecurse(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 = ¤tPosition; + 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; + + friend class StackStats; +}; + +} // namespace WTF + +using WTF::StackBounds; + +#endif diff --git a/qv4mm.cpp b/qv4mm.cpp index fe72551..05fce09 100644 --- a/qv4mm.cpp +++ b/qv4mm.cpp @@ -31,6 +31,7 @@ #include "qmljs_objects.h" #include "qv4ecmaobjects_p.h" #include "qv4mm.h" +#include "StackBounds.h" #include #include @@ -375,3 +376,20 @@ void MemoryManagerWithoutGC::collectRootsOnStack(QVector &roots) c { Q_UNUSED(roots); } + +MemoryManagerWithNativeStack::~MemoryManagerWithNativeStack() +{ +} + +void MemoryManagerWithNativeStack::collectRootsOnStack(QVector &roots) const +{ + StackBounds bounds = StackBounds::currentThreadStackBounds(); + Value* top = reinterpret_cast(bounds.origin()); + Value* current = reinterpret_cast(bounds.current()); + qDebug("Collecting on stack. top %p current %p\n", top, current); + for (; current < top; ++current) { + if (current->asObject()) + qDebug("found object %p on stack", (void*)current); + add(roots, *current); + } +} diff --git a/qv4mm.h b/qv4mm.h index 85395b9..5bf0077 100644 --- a/qv4mm.h +++ b/qv4mm.h @@ -175,6 +175,19 @@ protected: virtual void collectRootsOnStack(QVector &roots) const; }; +class MemoryManagerWithNativeStack: public MemoryManager +{ +public: + MemoryManagerWithNativeStack() + { setEnableGC(true); } + + virtual ~MemoryManagerWithNativeStack(); + +protected: + virtual void collectRootsOnStack(QVector &roots) const; +}; + + } // namespace VM } // namespace QQmlJS