libcruft-util/backtrace_stackwalk.cpp

122 lines
3.3 KiB
C++

/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* Copyright 2016 Danny Robson <danny@nerdcruft.net>
*/
#include "backtrace.hpp"
#include "types.hpp"
#include "win32/except.hpp"
#include "win32/windows.hpp"
// windows.h needs to be included here so that it defines some symbols that
// dbghelp.h requires.
#include "win32/windows.hpp"
#include <dbghelp.h>
#include <fmt/ostream.h>
using cruft::backtrace;
///////////////////////////////////////////////////////////////////////////////
backtrace::backtrace ()
{
if (!SymInitialize (GetCurrentProcess (), nullptr, TRUE))
cruft::win32::error::throw_code ();
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))
{
cruft::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 (reinterpret_cast<void*> (frame.AddrPC.Offset));
}
}
///////////////////////////////////////////////////////////////////////////////
std::ostream&
cruft::operator<< (std::ostream &os, backtrace const &obj)
{
fmt::print (os, "{}", obj);
return os;
}
///////////////////////////////////////////////////////////////////////////////
fmt::format_context::iterator
fmt::formatter<cruft::backtrace>::format (
cruft::backtrace const &obj,
fmt::format_context &ctx
) {
const auto process = GetCurrentProcess ();
struct {
SYMBOL_INFO info;
char name[255];
char null = '\0';
} sym {};
sym.info.SizeOfStruct = sizeof (sym.info);
sym.info.MaxNameLen = std::size (sym.name);
fmt::format_to (ctx.out (), "[ ");
for (auto const frame: obj.frames ()) {
fmt::format_to (ctx.out (), "{{ addr: {}, name: ", frame);
// Find the symbol name
sym.info.Name[0] = '\0';
memset (sym.name, 0, sizeof (sym.name));
if (FALSE == SymFromAddr (process, reinterpret_cast<uintptr_t> (frame), nullptr, &sym.info)) {
fmt::format_to (ctx.out (), "null }}, ");
} else {
fmt::format_to (ctx.out (), "'{}' }}, ", sym.info.Name);
}
}
return fmt::format_to (ctx.out (), " ]");
}