backtrace_stackwalk: add StackWalk64 backtrace

Wine does not implement CaptureStackBackTrace, so we have to use
StackWalk64 where possible.
This commit is contained in:
Danny Robson 2016-04-27 16:00:00 +10:00
parent 2434424488
commit 6e656444c6
2 changed files with 127 additions and 7 deletions

View File

@ -258,7 +258,9 @@ UTIL_FILES = \
version.hpp \ version.hpp \
view.cpp \ view.cpp \
view.ipp \ view.ipp \
view.hpp view.hpp \
$(BACKTRACE_FILES)
if PLATFORM_LINUX if PLATFORM_LINUX
UTIL_FILES += \ UTIL_FILES += \
@ -302,16 +304,24 @@ UTIL_FILES += \
endif endif
BACKTRACE_FILES = # Select one of the available backtrace codepaths. We construct a list of
if HAVE_CAPTURESTACKBACKTRACE # candidates in their preferred order then used firstword to choose the best
BACKTRACE_FILES += backtrace_win32.cpp # one.
endif #
# We need to use this two variable approach otherwise the final variable
# doesn't seem to be expanded at the appropriate time. Using ?= apparently
# suffers a similar problem.
__BACKTRACE_FILES=
if HAVE_EXECINFO if HAVE_EXECINFO
BACKTRACE_FILES += backtrace_execinfo.cpp __BACKTRACE_FILES += backtrace_execinfo.cpp
endif endif
UTIL_FILES += $(BACKTRACE_FILES) if HAVE_CAPTURESTACKBACKTRACE
__BACKTRACE_FILES += backtrace_stackwalk.cpp backtrace_win32.cpp
endif
BACKTRACE_FILES=$(firstword $(__BACKTRACE_FILES) backtrace_null.cpp)
############################################################################### ###############################################################################

110
backtrace_stackwalk.cpp Normal file
View File

@ -0,0 +1,110 @@
/*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Copyright 2016 Danny Robson <danny@nerdcruft.net>
*/
#include "./backtrace.hpp"
#include "./debug.hpp"
#include "./except.hpp"
#include "./types.hpp"
#include <windows.h>
#include <dbghelp.h>
#include <iostream>
using debug::backtrace;
///////////////////////////////////////////////////////////////////////////////
backtrace::backtrace ()
{
static auto ready = SymInitialize (GetCurrentProcess (), nullptr, TRUE);
CHECK (ready);
CONTEXT ctx;
memset (&ctx, 0, sizeof (ctx));
ctx.ContextFlags = CONTEXT_ALL;
RtlCaptureContext (&ctx);
auto image_type = IMAGE_FILE_MACHINE_AMD64;
STACKFRAME64 frame;
memset (&frame, 0, sizeof (frame));
frame.AddrPC.Offset = ctx.Rip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = ctx.Rsp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Offset = ctx.Rsp;
frame.AddrStack.Mode = AddrModeFlat;
auto process = GetCurrentProcess ();
auto thread = GetCurrentThread ();
SetLastError (0);
while (true) {
if (!StackWalk64 (image_type,
process,
thread,
&frame,
&ctx,
nullptr,
SymFunctionTableAccess64,
SymGetModuleBase64,
nullptr))
{
util::win32_error::throw_code ();
}
// we've read the bottom of the stack
if (frame.AddrReturn.Offset == 0)
break;
// we've reached the tail of an (apparently) infinite stack. this is a common occurence.
if (frame.AddrPC.Offset == frame.AddrReturn.Offset)
break;
m_frames.push_back ((void*)frame.AddrPC.Offset);
}
}
///////////////////////////////////////////////////////////////////////////////
std::ostream&
debug::operator<< (std::ostream &os, const debug::backtrace &b)
{
const auto self = GetCurrentProcess ();
struct [[gnu::packed]] {
IMAGEHLP_SYMBOL64 info;
char name[255];
} sym;
sym.info.SizeOfStruct = sizeof (sym.info);
sym.info.MaxNameLength = elems (sym.name);
for (auto frame: b.frames ()) {
// Find the symbol name
sym.info.Name[0] = '\0';
memset (sym.name, 0, sizeof (sym.name));
SymGetSymFromAddr64 (self, (uintptr_t)frame, nullptr, &sym.info);
*std::rbegin (sym.name) = '\0';
os << self << '\t' << frame << '\t' << sym.info.Name << '\n';
}
return os;
}