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: Yuly Novikov <ynovikov@chromium.org>
This commit is contained in:
Jamie Madill
2019-06-03 15:21:47 -04:00
committed by Commit Bot
parent 5993d899e3
commit 20d380fa5b
17 changed files with 2130 additions and 36 deletions

View File

@@ -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 += [

View File

@@ -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") {

View File

@@ -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}
}}

View File

@@ -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":

View File

@@ -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"));

View File

@@ -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_

View File

@@ -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");

View File

@@ -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))

View File

@@ -0,0 +1,167 @@
//
// 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

View File

@@ -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_

View File

@@ -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",
]

View File

@@ -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

View File

@@ -0,0 +1,29 @@
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.

View File

@@ -0,0 +1,12 @@
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.

View File

@@ -0,0 +1,3 @@
DisableFormat: true

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,255 @@
#ifndef __STACKWALKER_H__
#define __STACKWALKER_H__
#if defined(_MSC_VER)
/**********************************************************************
*
* StackWalker.h
*
*
*
* LICENSE (http://www.opensource.org/licenses/bsd-license.php)
*
* Copyright (c) 2005-2009, 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 Jochen Kalmbach 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 OWNER 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.
*
* **********************************************************************/
// #pragma once is supported starting with _MSC_VER 1000,
// so we need not to check the version (because we only support _MSC_VER >= 1100)!
#pragma once
#include <windows.h>
#if _MSC_VER >= 1900
#pragma warning(disable : 4091)
#endif
// special defines for VC5/6 (if no actual PSDK is installed):
#if _MSC_VER < 1300
typedef unsigned __int64 DWORD64, *PDWORD64;
#if defined(_WIN64)
typedef unsigned __int64 SIZE_T, *PSIZE_T;
#else
typedef unsigned long SIZE_T, *PSIZE_T;
#endif
#endif // _MSC_VER < 1300
class StackWalkerInternal; // forward
class StackWalker
{
public:
typedef enum StackWalkOptions
{
// No addition info will be retrieved
// (only the address is available)
RetrieveNone = 0,
// Try to get the symbol-name
RetrieveSymbol = 1,
// Try to get the line for this symbol
RetrieveLine = 2,
// Try to retrieve the module-infos
RetrieveModuleInfo = 4,
// Also retrieve the version for the DLL/EXE
RetrieveFileVersion = 8,
// Contains all the above
RetrieveVerbose = 0xF,
// Generate a "good" symbol-search-path
SymBuildPath = 0x10,
// Also use the public Microsoft-Symbol-Server
SymUseSymSrv = 0x20,
// Contains all the above "Sym"-options
SymAll = 0x30,
// Contains all options (default)
OptionsAll = 0x3F
} StackWalkOptions;
StackWalker(int options = OptionsAll, // 'int' is by design, to combine the enum-flags
LPCSTR szSymPath = NULL,
DWORD dwProcessId = GetCurrentProcessId(),
HANDLE hProcess = GetCurrentProcess());
StackWalker(DWORD dwProcessId, HANDLE hProcess);
virtual ~StackWalker();
typedef BOOL(__stdcall* PReadProcessMemoryRoutine)(
HANDLE hProcess,
DWORD64 qwBaseAddress,
PVOID lpBuffer,
DWORD nSize,
LPDWORD lpNumberOfBytesRead,
LPVOID pUserData // optional data, which was passed in "ShowCallstack"
);
BOOL LoadModules();
BOOL ShowCallstack(
HANDLE hThread = GetCurrentThread(),
const CONTEXT* context = NULL,
PReadProcessMemoryRoutine readMemoryFunction = NULL,
LPVOID pUserData = NULL // optional to identify some data in the 'readMemoryFunction'-callback
);
BOOL ShowObject(LPVOID pObject);
#if _MSC_VER >= 1300
// due to some reasons, the "STACKWALK_MAX_NAMELEN" must be declared as "public"
// in older compilers in order to use it... starting with VC7 we can declare it as "protected"
protected:
#endif
enum
{
STACKWALK_MAX_NAMELEN = 1024
}; // max name length for found symbols
protected:
// Entry for each Callstack-Entry
typedef struct CallstackEntry
{
DWORD64 offset; // if 0, we have no valid entry
CHAR name[STACKWALK_MAX_NAMELEN];
CHAR undName[STACKWALK_MAX_NAMELEN];
CHAR undFullName[STACKWALK_MAX_NAMELEN];
DWORD64 offsetFromSmybol;
DWORD offsetFromLine;
DWORD lineNumber;
CHAR lineFileName[STACKWALK_MAX_NAMELEN];
DWORD symType;
LPCSTR symTypeString;
CHAR moduleName[STACKWALK_MAX_NAMELEN];
DWORD64 baseOfImage;
CHAR loadedImageName[STACKWALK_MAX_NAMELEN];
} CallstackEntry;
typedef enum CallstackEntryType
{
firstEntry,
nextEntry,
lastEntry
} CallstackEntryType;
virtual void OnSymInit(LPCSTR szSearchPath, DWORD symOptions, LPCSTR szUserName);
virtual void OnLoadModule(LPCSTR img,
LPCSTR mod,
DWORD64 baseAddr,
DWORD size,
DWORD result,
LPCSTR symType,
LPCSTR pdbName,
ULONGLONG fileVersion);
virtual void OnCallstackEntry(CallstackEntryType eType, CallstackEntry& entry);
virtual void OnDbgHelpErr(LPCSTR szFuncName, DWORD gle, DWORD64 addr);
virtual void OnOutput(LPCSTR szText);
StackWalkerInternal* m_sw;
HANDLE m_hProcess;
DWORD m_dwProcessId;
BOOL m_modulesLoaded;
LPSTR m_szSymPath;
int m_options;
int m_MaxRecursionCount;
static BOOL __stdcall myReadProcMem(HANDLE hProcess,
DWORD64 qwBaseAddress,
PVOID lpBuffer,
DWORD nSize,
LPDWORD lpNumberOfBytesRead);
friend StackWalkerInternal;
}; // class StackWalker
// The "ugly" assembler-implementation is needed for systems before XP
// If you have a new PSDK and you only compile for XP and later, then you can use
// the "RtlCaptureContext"
// Currently there is no define which determines the PSDK-Version...
// So we just use the compiler-version (and assumes that the PSDK is
// the one which was installed by the VS-IDE)
// INFO: If you want, you can use the RtlCaptureContext if you only target XP and later...
// But I currently use it in x64/IA64 environments...
//#if defined(_M_IX86) && (_WIN32_WINNT <= 0x0500) && (_MSC_VER < 1400)
#if defined(_M_IX86)
#ifdef CURRENT_THREAD_VIA_EXCEPTION
// TODO: The following is not a "good" implementation,
// because the callstack is only valid in the "__except" block...
#define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \
do \
{ \
memset(&c, 0, sizeof(CONTEXT)); \
EXCEPTION_POINTERS* pExp = NULL; \
__try \
{ \
throw 0; \
} \
__except (((pExp = GetExceptionInformation()) ? EXCEPTION_EXECUTE_HANDLER \
: EXCEPTION_EXECUTE_HANDLER)) \
{ \
} \
if (pExp != NULL) \
memcpy(&c, pExp->ContextRecord, sizeof(CONTEXT)); \
c.ContextFlags = contextFlags; \
} while (0);
#else
// clang-format off
// The following should be enough for walking the callstack...
#define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \
do \
{ \
memset(&c, 0, sizeof(CONTEXT)); \
c.ContextFlags = contextFlags; \
__asm call x \
__asm x: pop eax \
__asm mov c.Eip, eax \
__asm mov c.Ebp, ebp \
__asm mov c.Esp, esp \
} while (0)
// clang-format on
#endif
#else
// The following is defined for x86 (XP and higher), x64 and IA64:
#define GET_CURRENT_CONTEXT_STACKWALKER_CODEPLEX(c, contextFlags) \
do \
{ \
memset(&c, 0, sizeof(CONTEXT)); \
c.ContextFlags = contextFlags; \
RtlCaptureContext(&c); \
} while (0);
#endif
#endif //defined(_MSC_VER)
#endif // __STACKWALKER_H__