From 6e656444c6977649597b37dddc0c317da77dc275 Mon Sep 17 00:00:00 2001 From: Danny Robson Date: Wed, 27 Apr 2016 16:00:00 +1000 Subject: [PATCH] backtrace_stackwalk: add StackWalk64 backtrace Wine does not implement CaptureStackBackTrace, so we have to use StackWalk64 where possible. --- Makefile.am | 24 ++++++--- backtrace_stackwalk.cpp | 110 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 127 insertions(+), 7 deletions(-) create mode 100644 backtrace_stackwalk.cpp diff --git a/Makefile.am b/Makefile.am index e14dff64..efe7b54c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -258,7 +258,9 @@ UTIL_FILES = \ version.hpp \ view.cpp \ view.ipp \ - view.hpp + view.hpp \ + $(BACKTRACE_FILES) + if PLATFORM_LINUX UTIL_FILES += \ @@ -302,16 +304,24 @@ UTIL_FILES += \ endif -BACKTRACE_FILES = -if HAVE_CAPTURESTACKBACKTRACE -BACKTRACE_FILES += backtrace_win32.cpp -endif +# Select one of the available backtrace codepaths. We construct a list of +# candidates in their preferred order then used firstword to choose the best +# one. +# +# 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 -BACKTRACE_FILES += backtrace_execinfo.cpp +__BACKTRACE_FILES += backtrace_execinfo.cpp 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) ############################################################################### diff --git a/backtrace_stackwalk.cpp b/backtrace_stackwalk.cpp new file mode 100644 index 00000000..f271da44 --- /dev/null +++ b/backtrace_stackwalk.cpp @@ -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 + */ + +#include "./backtrace.hpp" + +#include "./debug.hpp" +#include "./except.hpp" +#include "./types.hpp" + +#include +#include + +#include + +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; +}