/* * 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 */ #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 #include 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 (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::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 (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 (), " ]"); }