Fix rare crashes in release builds on Windows with exceptions
authorSimon Hausmann <simon.hausmann@digia.com>
Tue, 9 Jul 2013 11:42:09 +0000 (13:42 +0200)
committerThe Qt Project <gerrit-noreply@qt-project.org>
Wed, 10 Jul 2013 11:02:01 +0000 (13:02 +0200)
When throwing exceptions we have a tendency to save a backtrace, using
StackWalk64 on Windows. Before we can do that stack walk, we have to
capture the current processor register state, in particular the frame pointer,
for which we call RtlCaptureContext. Almost naturally that function requires
the caller to have set up a stack frame, which may not have happened when
compiling with release flags.

As a remedy, this patch moves the code that calls RtlCaptureContext into
a separate file, which will be compiled with frame pointer optimizations
disabled.

Change-Id: I3ce53b47c4a421efeaa3e575429d85327540ca97
Reviewed-by: Erik Verbruggen <erik.verbruggen@digia.com>
src/qml/qml/v4/qv4engine.cpp
src/qml/qml/v4/qv4stacktrace.cpp [new file with mode: 0644]
src/qml/qml/v4/qv4stacktrace_p.h [new file with mode: 0644]
src/qml/qml/v4/v4.pri

index 343e24f..2f40442 100644 (file)
 #include "qv4sequenceobject_p.h"
 #include "qv4qobjectwrapper_p.h"
 #include "qv4qmlextensions_p.h"
-
-#if (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) || defined(Q_OS_MAC)
-#define HAVE_GNU_BACKTRACE
-#include <execinfo.h>
-#endif
-
-#if defined(Q_OS_WIN)
-#include <DbgHelp.h>
-#endif
+#include "qv4stacktrace_p.h"
 
 #ifdef V4_ENABLE_JIT
 #  include "qv4isel_masm_p.h"
@@ -589,106 +581,9 @@ Object *ExecutionEngine::qmlContextObject() const
 }
 
 namespace {
-    struct NativeFrame {
-        Function *function;
-        int line;
-    };
-
-    struct NativeStackTrace
-    {
-        void *trace[100];
-        int nativeFrameCount;
-        int currentNativeFrame;
-        ExecutionEngine *engine;
-
-        NativeStackTrace(ExecutionContext *context)
-        {
-            engine = context->engine;
-            currentNativeFrame = 0;
-
-#if defined(HAVE_GNU_BACKTRACE)
-            UnwindHelper::prepareForUnwind(context);
-
-            nativeFrameCount = backtrace(&trace[0], sizeof(trace) / sizeof(trace[0]));
-#elif defined(Q_OS_WIN)
-
-            int machineType = 0;
-
-            CONTEXT winContext;
-            memset(&winContext, 0, sizeof(winContext));
-            winContext.ContextFlags = CONTEXT_FULL;
-            RtlCaptureContext(&winContext);
-
-            STACKFRAME64 sf64;
-            memset(&sf64, 0, sizeof(sf64));
-
-#if defined(Q_PROCESSOR_X86_32)
-            machineType = IMAGE_FILE_MACHINE_I386;
-
-            sf64.AddrFrame.Offset = winContext.Ebp;
-            sf64.AddrFrame.Mode = AddrModeFlat;
-            sf64.AddrPC.Offset = winContext.Eip;
-            sf64.AddrPC.Mode = AddrModeFlat;
-            sf64.AddrStack.Offset = winContext.Esp;
-            sf64.AddrStack.Mode = AddrModeFlat;
-
-#elif defined(Q_PROCESSOR_X86_64)
-            machineType = IMAGE_FILE_MACHINE_AMD64;
-
-            sf64.AddrFrame.Offset = winContext.Rbp;
-            sf64.AddrFrame.Mode = AddrModeFlat;
-            sf64.AddrPC.Offset = winContext.Rip;
-            sf64.AddrPC.Mode = AddrModeFlat;
-            sf64.AddrStack.Offset = winContext.Rsp;
-            sf64.AddrStack.Mode = AddrModeFlat;
-
-#else
-#error "Platform unsupported!"
-#endif
-
-            nativeFrameCount = 0;
-
-            while (StackWalk64(machineType, GetCurrentProcess(), GetCurrentThread(), &sf64, &winContext, 0, SymFunctionTableAccess64, SymGetModuleBase64, 0)) {
-
-                if (sf64.AddrReturn.Offset == 0)
-                    break;
-
-                trace[nativeFrameCount] = reinterpret_cast<void*>(sf64.AddrReturn.Offset);
-                nativeFrameCount++;
-                if (nativeFrameCount >= sizeof(trace) / sizeof(trace[0]))
-                    break;
-            }
-
-#else
-            nativeFrameCount = 0;
-#endif
-        }
-
-        NativeFrame nextFrame() {
-            NativeFrame frame;
-            frame.function = 0;
-            frame.line = -1;
-
-            for (; currentNativeFrame < nativeFrameCount && !frame.function; ++currentNativeFrame) {
-                quintptr pc = reinterpret_cast<quintptr>(trace[currentNativeFrame]);
-                // The pointers from the back trace point to the return address, but we are interested in
-                // the caller site.
-                pc = pc - 1;
-
-                Function *f = engine->functionForProgramCounter(pc);
-                if (!f)
-                    continue;
-
-                frame.function = f;
-                frame.line = f->lineNumberForProgramCounter(pc - reinterpret_cast<quintptr>(f->code));
-            }
-
-            return frame;
-        }
-    };
     struct LineNumberResolver {
         const ExecutionEngine* engine;
-        QScopedPointer<NativeStackTrace> nativeTrace;
+        QScopedPointer<QV4::NativeStackTrace> nativeTrace;
 
         LineNumberResolver(const ExecutionEngine *engine)
             : engine(engine)
@@ -702,7 +597,7 @@ namespace {
                 frame->line = function->lineNumberForProgramCounter(offset);
             } else {
                 if (!nativeTrace)
-                    nativeTrace.reset(new NativeStackTrace(engine->current));
+                    nativeTrace.reset(new QV4::NativeStackTrace(engine->current));
 
                 NativeFrame nativeFrame = nativeTrace->nextFrame();
                 if (nativeFrame.function == function)
diff --git a/src/qml/qml/v4/qv4stacktrace.cpp b/src/qml/qml/v4/qv4stacktrace.cpp
new file mode 100644 (file)
index 0000000..a96824e
--- /dev/null
@@ -0,0 +1,159 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#if defined(_WIN32)
+#include <windows.h>
+#include <DbgHelp.h>
+#endif
+
+#include "qv4stacktrace_p.h"
+#include "qv4function_p.h"
+#include "qv4engine_p.h"
+#include "qv4unwindhelper_p.h"
+
+#if (defined(Q_OS_LINUX) && !defined(Q_OS_ANDROID)) || defined(Q_OS_MAC)
+#define HAVE_GNU_BACKTRACE
+#include <execinfo.h>
+#endif
+
+#if defined(Q_OS_WIN)
+// We call RtlCaptureContext and that will crash if the compiler did not
+// create a stack frame for the function that calls RtlCaptureContext.
+#if defined(Q_CC_MSVC)
+#pragma optimize( "y", off )
+#elif defined(Q_CC_GNU)
+#pragma GCC optimize ("O0")
+#else
+#error "Don't know how to disable frame pointer optimizations with this compiler on Windows!"
+#endif
+
+#endif
+
+QT_BEGIN_NAMESPACE
+
+using namespace QV4;
+
+NativeStackTrace::NativeStackTrace(ExecutionContext *context)
+{
+    engine = context->engine;
+    currentNativeFrame = 0;
+
+#if defined(HAVE_GNU_BACKTRACE)
+    UnwindHelper::prepareForUnwind(context);
+
+    nativeFrameCount = backtrace(&trace[0], sizeof(trace) / sizeof(trace[0]));
+#elif defined(Q_OS_WIN)
+
+    int machineType = 0;
+
+    CONTEXT winContext;
+    memset(&winContext, 0, sizeof(winContext));
+    winContext.ContextFlags = CONTEXT_FULL;
+    RtlCaptureContext(&winContext);
+
+    STACKFRAME64 sf64;
+    memset(&sf64, 0, sizeof(sf64));
+
+#if defined(Q_PROCESSOR_X86_32)
+    machineType = IMAGE_FILE_MACHINE_I386;
+
+    sf64.AddrFrame.Offset = winContext.Ebp;
+    sf64.AddrFrame.Mode = AddrModeFlat;
+    sf64.AddrPC.Offset = winContext.Eip;
+    sf64.AddrPC.Mode = AddrModeFlat;
+    sf64.AddrStack.Offset = winContext.Esp;
+    sf64.AddrStack.Mode = AddrModeFlat;
+
+#elif defined(Q_PROCESSOR_X86_64)
+    machineType = IMAGE_FILE_MACHINE_AMD64;
+
+    sf64.AddrFrame.Offset = winContext.Rbp;
+    sf64.AddrFrame.Mode = AddrModeFlat;
+    sf64.AddrPC.Offset = winContext.Rip;
+    sf64.AddrPC.Mode = AddrModeFlat;
+    sf64.AddrStack.Offset = winContext.Rsp;
+    sf64.AddrStack.Mode = AddrModeFlat;
+
+#else
+#error "Platform unsupported!"
+#endif
+
+    nativeFrameCount = 0;
+
+    while (StackWalk64(machineType, GetCurrentProcess(), GetCurrentThread(), &sf64, &winContext, 0, SymFunctionTableAccess64, SymGetModuleBase64, 0)) {
+
+        if (sf64.AddrReturn.Offset == 0)
+            break;
+
+        trace[nativeFrameCount] = reinterpret_cast<void*>(sf64.AddrReturn.Offset);
+        nativeFrameCount++;
+        if (nativeFrameCount >= sizeof(trace) / sizeof(trace[0]))
+            break;
+    }
+
+#else
+    nativeFrameCount = 0;
+#endif
+    }
+
+NativeFrame NativeStackTrace::nextFrame() {
+    NativeFrame frame;
+    frame.function = 0;
+    frame.line = -1;
+
+    for (; currentNativeFrame < nativeFrameCount && !frame.function; ++currentNativeFrame) {
+        quintptr pc = reinterpret_cast<quintptr>(trace[currentNativeFrame]);
+        // The pointers from the back trace point to the return address, but we are interested in
+        // the caller site.
+        pc = pc - 1;
+
+        Function *f = engine->functionForProgramCounter(pc);
+        if (!f)
+            continue;
+
+        frame.function = f;
+        frame.line = f->lineNumberForProgramCounter(pc - reinterpret_cast<quintptr>(f->code));
+    }
+
+    return frame;
+}
+
+QT_END_NAMESPACE
diff --git a/src/qml/qml/v4/qv4stacktrace_p.h b/src/qml/qml/v4/qv4stacktrace_p.h
new file mode 100644 (file)
index 0000000..79cb4d1
--- /dev/null
@@ -0,0 +1,75 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia.  For licensing terms and
+** conditions see http://qt.digia.com/licensing.  For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights.  These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3.0 as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL included in the
+** packaging of this file.  Please review the following information to
+** ensure the GNU General Public License version 3.0 requirements will be
+** met: http://www.gnu.org/copyleft/gpl.html.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QV4STACKTRACE_P_H
+#define QV4STACKTRACE_P_H
+
+#include <qglobal.h>
+
+QT_BEGIN_NAMESPACE
+
+namespace QV4 {
+
+struct Function;
+struct ExecutionEngine;
+struct ExecutionContext;
+
+struct NativeFrame {
+    Function *function;
+    int line;
+};
+
+struct NativeStackTrace
+{
+    void *trace[100];
+    int nativeFrameCount;
+    int currentNativeFrame;
+    ExecutionEngine *engine;
+
+    NativeStackTrace(ExecutionContext *context);
+
+    NativeFrame nextFrame();
+};
+
+} // namespace QV4
+
+QT_END_NAMESPACE
+
+#endif // QV4STACKTRACE_P_H
index 46b4770..48d9e82 100644 (file)
@@ -56,7 +56,8 @@ SOURCES += \
     $$PWD/qv4sequenceobject.cpp \
     $$PWD/qv4include.cpp \
     $$PWD/qv4qobjectwrapper.cpp \
-    $$PWD/qv4qmlextensions.cpp
+    $$PWD/qv4qmlextensions.cpp \
+    $$PWD/qv4stacktrace.cpp
 
 HEADERS += \
     $$PWD/qv4global_p.h \
@@ -111,7 +112,8 @@ HEADERS += \
     $$PWD/qv4sequenceobject_p.h \
     $$PWD/qv4include_p.h \
     $$PWD/qv4qobjectwrapper_p.h \
-    $$PWD/qv4qmlextensions_p.h
+    $$PWD/qv4qmlextensions_p.h \
+    $$PWD/qv4stacktrace_p.h
 
 llvm-libs {