Commit 20d380fa by Jamie Madill Committed by Commit Bot

Print stack backtrace on critical failure.

We reuse code from Skia to walk the stack on Posix platforms. See: https://github.com/google/skia/blob/master/tools/CrashHandler.cpp On Windows we use a BSD-licensed tool called StackWalker. See: https://github.com/JochenKalmbach/StackWalker This allows us to get high quality stack traces on Win/Linux/Mac. Bug: angleproject:3162 Change-Id: I9c50ede2c6a41ed0ee85a0507372df42a487bcef Reviewed-on: https://chromium-review.googlesource.com/c/angle/angle/+/1632950 Commit-Queue: Jamie Madill <jmadill@chromium.org> Reviewed-by: 's avatarYuly Novikov <ynovikov@chromium.org>
parent 5993d899
......@@ -3,7 +3,6 @@
# found in the LICENSE file.
# import the use_x11 variable
import("//build/config/dcheck_always_on.gni")
import("//build/config/linux/pkg_config.gni")
import("//build/config/ui.gni")
import("//testing/libfuzzer/fuzzer_test.gni")
......@@ -145,6 +144,13 @@ config("build_id_config") {
ldflags = [ "-Wl,--build-id" ]
}
# Useful for more informative stack traces.
config("better_linux_stack_traces") {
if (angle_better_stack_traces) {
ldflags = [ "-Wl,--export-dynamic" ]
}
}
# Windows ARM64 is available since 10.0.16299 so no need to copy
# d3dcompiler_47.dll because this file is available as inbox.
if (is_win && target_cpu != "arm64") {
......@@ -210,6 +216,25 @@ config("angle_common_config") {
}
}
if (is_win) {
angle_source_set("angle_stack_walker") {
sources = [
"util/windows/third_party/StackWalker/src/StackWalker.cpp",
"util/windows/third_party/StackWalker/src/StackWalker.h",
]
if (is_clang) {
cflags_cc = [
"-Wno-c++98-compat-extra-semi",
"-Wno-missing-declarations",
"-Wno-switch",
]
} else {
cflags_cc = [ "/wd4740" ]
}
}
}
angle_source_set("angle_system_utils") {
sources = angle_system_utils_sources
}
......@@ -921,12 +946,18 @@ foreach(is_shared_library,
target(library_type, library_name) {
sources = util_sources
deps = [
":angle_common",
":angle_util_loader_headers",
]
public_deps = []
libs = []
if (is_win) {
sources += util_win_sources
deps += [ ":angle_stack_walker" ]
}
libs = []
if (is_linux) {
sources += util_linux_sources
libs += [
......@@ -950,7 +981,6 @@ foreach(is_shared_library,
if (is_android) {
# To prevent linux sources filtering on android
set_sources_assignment_filter([])
sources += util_linux_sources
sources += util_android_sources
libs += [
"android",
......@@ -962,12 +992,6 @@ foreach(is_shared_library,
public_configs += [ ":angle_util_config" ]
deps = [
":angle_common",
":angle_util_loader_headers",
]
public_deps = []
if (is_fuchsia) {
sources += util_fuchsia_sources
public_deps += [
......
......@@ -2,6 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import("//build/config/dcheck_always_on.gni")
import("//build/config/sanitizers/sanitizers.gni")
import("//build/config/ui.gni") # import the use_x11 variable
import("//build_overrides/angle.gni")
......@@ -95,14 +96,21 @@ if (is_win) {
}
angle_common_configs = [
angle_root + ":better_linux_stack_traces",
angle_root + ":extra_warnings",
angle_root + ":internal_config",
]
angle_remove_configs = [ "//build/config/compiler:default_include_dirs" ]
angle_better_stack_traces = (is_debug || dcheck_always_on) && is_linux
if (is_clang) {
angle_remove_configs += [ "//build/config/clang:find_bad_constructs" ]
# Disabled to enable better stack traces.
if (angle_better_stack_traces) {
angle_remove_configs += [ "//build/config/gcc:symbol_visibility_hidden" ]
}
}
set_defaults("angle_executable") {
......
......@@ -43,7 +43,8 @@ def write_header(data_source_name,
api_lower=api,
preamble=preamble,
export=export,
lib=lib.upper())
lib=lib.upper(),
load_fn_name="Load%s%s" % (prefix if prefix else "", api.upper()))
out.write(loader_header)
out.close()
......@@ -71,7 +72,8 @@ def write_source(data_source_name, all_cmds, api, path, ns="", prefix=None, expo
function_pointers="\n".join(var_defs),
set_pointers="\n".join(setters),
api_upper=api.upper(),
api_lower=api)
api_lower=api,
load_fn_name="Load%s%s" % (prefix if prefix else "", api.upper()))
out.write(loader_source)
out.close()
......@@ -266,9 +268,9 @@ namespace angle
{{
using GenericProc = void (*)();
using LoadProc = GenericProc (KHRONOS_APIENTRY *)(const char *);
{export}void Load{api_upper}(LoadProc loadProc);
{export}void {load_fn_name}(LoadProc loadProc);
}} // namespace angle
#endif // {lib}_{api_upper}_LOADER_AUTOGEN_H_
"""
......@@ -288,7 +290,7 @@ template_loader_cpp = """// GENERATED FILE - DO NOT EDIT.
namespace angle
{{
void Load{api_upper}(LoadProc loadProc)
void {load_fn_name}(LoadProc loadProc)
{{
{set_pointers}
}}
......
......@@ -144,15 +144,15 @@
"GL/EGL/WGL loader:scripts/egl_angle_ext.xml":
"cc91aa6b14979dc7b3a0b7fee024e59e",
"GL/EGL/WGL loader:scripts/generate_loader.py":
"b8c0dc876c8122bdc2447de982bcfad6",
"5a7cd014230fe04664d9613e65399d42",
"GL/EGL/WGL loader:scripts/registry_xml.py":
"79d48343cb33f2f534720f84dcba1b4f",
"GL/EGL/WGL loader:scripts/wgl.xml":
"aa96419c582af2f6673430e2847693f4",
"GL/EGL/WGL loader:src/libEGL/egl_loader_autogen.cpp":
"07e4ac072bd111f32d0798131e5e2926",
"494b0076622c9d7f70b74eeb00fd348c",
"GL/EGL/WGL loader:src/libEGL/egl_loader_autogen.h":
"5faf638e5f4b23e268193e2cf4a03aae",
"8fd78216d48373e776f2f579a58f84c8",
"GL/EGL/WGL loader:util/egl_loader_autogen.cpp":
"310b388513c03d205c33e84454851ed7",
"GL/EGL/WGL loader:util/egl_loader_autogen.h":
......
......@@ -102,7 +102,7 @@ PFNEGLSTREAMCONSUMERGLTEXTUREEXTERNALATTRIBSNVPROC EGL_StreamConsumerGLTextureEx
namespace angle
{
void LoadEGL(LoadProc loadProc)
void LoadEGL_EGL(LoadProc loadProc)
{
EGL_ChooseConfig = reinterpret_cast<PFNEGLCHOOSECONFIGPROC>(loadProc("EGL_ChooseConfig"));
EGL_CopyBuffers = reinterpret_cast<PFNEGLCOPYBUFFERSPROC>(loadProc("EGL_CopyBuffers"));
......
......@@ -109,7 +109,7 @@ namespace angle
{
using GenericProc = void (*)();
using LoadProc = GenericProc(KHRONOS_APIENTRY *)(const char *);
void LoadEGL(LoadProc loadProc);
void LoadEGL_EGL(LoadProc loadProc);
} // namespace angle
#endif // LIBEGL_EGL_LOADER_AUTOGEN_H_
......@@ -34,7 +34,7 @@ void EnsureEGLLoaded()
return;
gEntryPointsLib.reset(angle::OpenSharedLibrary(ANGLE_GLESV2_LIBRARY_NAME));
angle::LoadEGL(GlobalLoad);
angle::LoadEGL_EGL(GlobalLoad);
if (!EGL_GetPlatformDisplay)
{
fprintf(stderr, "Error loading EGL entry points.\n");
......
......@@ -55,7 +55,11 @@ void TestPlatform_logError(PlatformMethods *platform, const char *errorMessage)
if (testPlatformContext->ignoreMessages)
return;
FAIL() << errorMessage;
GTEST_NONFATAL_FAILURE_(errorMessage);
// Print the stack and stop any crash handling to prevent duplicate reports.
PrintStackBacktrace();
TerminateCrashHandler();
}
void TestPlatform_logWarning(PlatformMethods *platform, const char *warningMessage)
......@@ -491,6 +495,8 @@ void ANGLETestBase::ANGLETestSetUp()
{
mSetUpCalled = true;
InitCrashHandler();
gDefaultPlatformMethods.overrideWorkaroundsD3D = TestPlatform_overrideWorkaroundsD3D;
gDefaultPlatformMethods.overrideFeaturesVk = TestPlatform_overrideFeaturesVk;
gDefaultPlatformMethods.logError = TestPlatform_logError;
......@@ -616,6 +622,8 @@ void ANGLETestBase::ANGLETestTearDown()
mFixture->eglWindow->destroySurface();
}
TerminateCrashHandler();
// Check for quit message
Event myEvent;
while (mFixture->osWindow->popEvent(&myEvent))
......
//
// Copyright 2019 The ANGLE Project Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// system_utils_crash_handler:
// ANGLE's crash handling and stack walking code. Modified from Skia's:
// https://github.com/google/skia/blob/master/tools/CrashHandler.cpp
//
#include "util/system_utils.h"
#include "common/angleutils.h"
#include <stdio.h>
#include <stdlib.h>
#if !defined(ANGLE_PLATFORM_ANDROID) && !defined(ANGLE_PLATFORM_FUCHSIA)
# if defined(ANGLE_PLATFORM_APPLE)
// We only use local unwinding, so we can define this to select a faster implementation.
# define UNW_LOCAL_ONLY
# include <cxxabi.h>
# include <libunwind.h>
# include <signal.h>
# elif defined(ANGLE_PLATFORM_POSIX)
// We'd use libunwind here too, but it's a pain to get installed for
// both 32 and 64 bit on bots. Doesn't matter much: catchsegv is best anyway.
# include <cxxabi.h>
# include <dlfcn.h>
# include <execinfo.h>
# include <signal.h>
# include <string.h>
# endif // defined(ANGLE_PLATFORM_APPLE)
#endif // !defined(ANGLE_PLATFORM_ANDROID) && !defined(ANGLE_PLATFORM_FUCHSIA)
namespace angle
{
#if defined(ANGLE_PLATFORM_ANDROID) || defined(ANGLE_PLATFORM_FUCHSIA)
void PrintStackBacktrace()
{
// No implementations yet.
}
void InitCrashHandler()
{
// No implementations yet.
}
void TerminateCrashHandler()
{
// No implementations yet.
}
#else
# if defined(ANGLE_PLATFORM_APPLE)
void PrintStackBacktrace()
{
printf("Backtrace:\n");
unw_context_t context;
unw_getcontext(&context);
unw_cursor_t cursor;
unw_init_local(&cursor, &context);
while (unw_step(&cursor) > 0)
{
static const size_t kMax = 256;
char mangled[kMax], demangled[kMax];
unw_word_t offset;
unw_get_proc_name(&cursor, mangled, kMax, &offset);
int ok;
size_t len = kMax;
abi::__cxa_demangle(mangled, demangled, &len, &ok);
printf(" %s (+0x%zx)\n", ok == 0 ? demangled : mangled, (size_t)offset);
}
printf("\n");
}
static void Handler(int sig)
{
printf("\nSignal %d:\n", sig);
PrintStackBacktrace();
// Exit NOW. Don't notify other threads, don't call anything registered with atexit().
_Exit(sig);
}
# elif defined(ANGLE_PLATFORM_POSIX)
void PrintStackBacktrace()
{
printf("Backtrace:\n");
void *stack[64];
const int count = backtrace(stack, ArraySize(stack));
char **symbols = backtrace_symbols(stack, count);
for (int i = 0; i < count; i++)
{
Dl_info info;
if (dladdr(stack[i], &info) && info.dli_sname)
{
char demangled[256];
size_t len = ArraySize(demangled);
int ok;
abi::__cxa_demangle(info.dli_sname, demangled, &len, &ok);
if (ok == 0)
{
printf(" %s\n", demangled);
continue;
}
}
printf(" %s\n", symbols[i]);
}
}
static void Handler(int sig)
{
printf("\nSignal %d [%s]:\n", sig, strsignal(sig));
PrintStackBacktrace();
// Exit NOW. Don't notify other threads, don't call anything registered with atexit().
_Exit(sig);
}
# endif // defined(ANGLE_PLATFORM_APPLE)
static constexpr int kSignals[] = {
SIGABRT, SIGBUS, SIGFPE, SIGILL, SIGSEGV, SIGTRAP,
};
void InitCrashHandler()
{
for (int sig : kSignals)
{
// Register our signal handler unless something's already done so (e.g. catchsegv).
void (*prev)(int) = signal(sig, Handler);
if (prev != SIG_DFL)
{
signal(sig, prev);
}
}
}
void TerminateCrashHandler()
{
for (int sig : kSignals)
{
void (*prev)(int) = signal(sig, SIG_DFL);
if (prev != Handler && prev != SIG_DFL)
{
signal(sig, prev);
}
}
}
#endif // defined(ANGLE_PLATFORM_ANDROID) || defined(ANGLE_PLATFORM_FUCHSIA)
} // namespace angle
......@@ -26,6 +26,13 @@ ANGLE_UTIL_EXPORT void WriteDebugMessage(const char *format, ...);
// Set thread affinity and priority.
ANGLE_UTIL_EXPORT bool StabilizeCPUForBenchmarking();
// Set a crash handler to print stack traces.
ANGLE_UTIL_EXPORT void InitCrashHandler();
ANGLE_UTIL_EXPORT void TerminateCrashHandler();
// Print a stack back trace.
ANGLE_UTIL_EXPORT void PrintStackBacktrace();
} // namespace angle
#endif // UTIL_SYSTEM_UTILS_H_
......@@ -50,12 +50,15 @@ util_winrt_sources = [
"util/windows/WindowsTimer.h",
]
util_linux_sources = [
util_posix_sources = [
"util/posix/PosixTimer.cpp",
"util/posix/PosixTimer.h",
"util/posix/Posix_crash_handler.cpp",
"util/posix/Posix_system_utils.cpp",
]
util_linux_sources = util_posix_sources
util_x11_sources = [
"util/x11/X11Pixmap.cpp",
"util/x11/X11Pixmap.h",
......@@ -63,13 +66,10 @@ util_x11_sources = [
"util/x11/X11Window.h",
]
util_fuchsia_sources = [
"util/posix/PosixTimer.cpp",
"util/posix/PosixTimer.h",
"util/posix/Posix_system_utils.cpp",
"util/fuchsia/ScenicWindow.cpp",
"util/fuchsia/ScenicWindow.h",
]
util_fuchsia_sources = util_posix_sources + [
"util/fuchsia/ScenicWindow.cpp",
"util/fuchsia/ScenicWindow.h",
]
util_ozone_sources = [
"util/ozone/OzonePixmap.cpp",
......@@ -84,13 +84,14 @@ util_osx_sources = [
"util/osx/OSXPixmap.h",
"util/osx/OSXWindow.mm",
"util/osx/OSXWindow.h",
"util/posix/Posix_crash_handler.cpp",
"util/posix/Posix_system_utils.cpp",
]
util_android_sources = [
"util/android/AndroidPixmap.cpp",
"util/android/AndroidWindow.cpp",
"util/android/AndroidWindow.h",
"util/android/third_party/android_native_app_glue.c",
"util/android/third_party/android_native_app_glue.h",
]
util_android_sources = util_posix_sources + [
"util/android/AndroidPixmap.cpp",
"util/android/AndroidWindow.cpp",
"util/android/AndroidWindow.h",
"util/android/third_party/android_native_app_glue.c",
"util/android/third_party/android_native_app_glue.h",
]
......@@ -13,8 +13,99 @@
#include <array>
#include <vector>
#include "util/windows/third_party/StackWalker/src/StackWalker.h"
namespace angle
{
namespace
{
static const struct
{
const char *name;
const DWORD code;
} kExceptions[] = {
#define _(E) \
{ \
# E, E \
}
_(EXCEPTION_ACCESS_VIOLATION),
_(EXCEPTION_BREAKPOINT),
_(EXCEPTION_INT_DIVIDE_BY_ZERO),
_(EXCEPTION_STACK_OVERFLOW),
#undef _
};
class CustomStackWalker : public StackWalker
{
public:
CustomStackWalker() {}
~CustomStackWalker() {}
void OnCallstackEntry(CallstackEntryType eType, CallstackEntry &entry) override
{
char buffer[STACKWALK_MAX_NAMELEN];
size_t maxLen = _TRUNCATE;
if ((eType != lastEntry) && (entry.offset != 0))
{
if (entry.name[0] == 0)
strncpy_s(entry.name, STACKWALK_MAX_NAMELEN, "(function-name not available)",
_TRUNCATE);
if (entry.undName[0] != 0)
strncpy_s(entry.name, STACKWALK_MAX_NAMELEN, entry.undName, _TRUNCATE);
if (entry.undFullName[0] != 0)
strncpy_s(entry.name, STACKWALK_MAX_NAMELEN, entry.undFullName, _TRUNCATE);
if (entry.lineFileName[0] == 0)
{
strncpy_s(entry.lineFileName, STACKWALK_MAX_NAMELEN, "(filename not available)",
_TRUNCATE);
if (entry.moduleName[0] == 0)
strncpy_s(entry.moduleName, STACKWALK_MAX_NAMELEN,
"(module-name not available)", _TRUNCATE);
_snprintf_s(buffer, maxLen, " %s - %p (%s): %s\n", entry.name,
reinterpret_cast<void *>(entry.offset), entry.moduleName,
entry.lineFileName);
}
else
_snprintf_s(buffer, maxLen, " %s (%s:%d)\n", entry.name, entry.lineFileName,
entry.lineNumber);
buffer[STACKWALK_MAX_NAMELEN - 1] = 0;
printf("%s", buffer);
OutputDebugStringA(buffer);
}
}
};
void PrintBacktrace(CONTEXT *c)
{
printf("Backtrace:\n");
OutputDebugStringA("Backtrace:\n");
CustomStackWalker sw;
sw.ShowCallstack(GetCurrentThread(), c);
}
LONG WINAPI StackTraceCrashHandler(EXCEPTION_POINTERS *e)
{
const DWORD code = e->ExceptionRecord->ExceptionCode;
printf("\nCaught exception %lu", code);
for (size_t i = 0; i < ArraySize(kExceptions); i++)
{
if (kExceptions[i].code == code)
{
printf(" %s", kExceptions[i].name);
}
}
printf("\n");
PrintBacktrace(e->ContextRecord);
// Exit NOW. Don't notify other threads, don't call anything registered with atexit().
_exit(1);
// The compiler wants us to return something. This is what we'd do if we didn't _exit().
return EXCEPTION_EXECUTE_HANDLER;
}
} // anonymous namespace
void Sleep(unsigned int milliseconds)
{
......@@ -36,4 +127,22 @@ void WriteDebugMessage(const char *format, ...)
OutputDebugStringA(buffer.data());
}
void InitCrashHandler()
{
SetUnhandledExceptionFilter(StackTraceCrashHandler);
}
void TerminateCrashHandler()
{
SetUnhandledExceptionFilter(nullptr);
}
void PrintStackBacktrace()
{
CONTEXT context;
ZeroMemory(&context, sizeof(CONTEXT));
RtlCaptureContext(&context);
PrintBacktrace(&context);
}
} // namespace angle
BSD 3-Clause License
Copyright (c) 2005 - 2017, Jochen Kalmbach
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
* 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.
* Neither the name of the copyright holder nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "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 THE COPYRIGHT HOLDER 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.
Name: StackWalker
URL: https://github.com/JochenKalmbach/StackWalker
License: BSD
License File: LICENSE
Security Critical: no
Description:
Walking the callstack in windows applications
See github page for more info. StackWalker is only used on Windows for
walking crash stacks in tests for better logging. It can also be used
for local debugging.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment