以下是我的代码,仅限开源。为什么StackWalk64不适用于release build
和debug build without pdbs
HEADER FILE
#ifndef STACK_TRACE_H_
#define STACK_TRACE_H_
#include <string>
#include <deque>
struct StackItem {
std::string m_name;
std::string m_file;
int m_line;
StackItem() : m_line(0) {}
};
class StackTraceImpl;
class StackTrace {
public:
typedef std::deque<StackItem> ItemContainer;
typedef ItemContainer::const_iterator ItemIterator;
ItemContainer m_items;
StackTraceImpl *m_impl;
public:
StackTrace();
virtual ~StackTrace();
void print() const;
void popFront() {
m_items.pop_front();
}
ItemIterator begin() const {
return m_items.begin();
}
ItemIterator end() const {
return m_items.end();
}
};
#endif
CPP文件
#include <windows.h>
#include <DbgHelp.h>
#include <tlhelp32.h>
#include <vector>
#include <iostream>
#include "StackTrace.h"
std::size_t const SYMBOL_NAME_MAXLEN = 1024;
struct SymStartup {
HANDLE process;
SymStartup(HANDLE process) : process(process) {
char current[MAX_PATH];
std::string path;
if (GetCurrentDirectoryA(MAX_PATH, current) > 0) {
path += current;
path += ";";
}
if (GetModuleFileNameA(NULL, current, MAX_PATH) > 0) {
std::string filePath = current;
std::string::size_type pos = filePath.find_last_of('\\');
if (pos != std::string::npos)
filePath.erase(pos);
path += filePath;
path += ";";
}
if (GetEnvironmentVariableA("_NT_SYMBOL_PATH", current, MAX_PATH) > 0) {
path += current;
path += ";";
}
if (GetEnvironmentVariableA("_NT_ALTERNATE_SYMBOL_PATH", current, MAX_PATH) > 0) {
path += current;
path += ";";
}
if (GetEnvironmentVariableA("SYSTEMROOT", current, MAX_PATH) > 0) {
path += current;
path += ";";
path += current;
path += "\\system32";
path += ";";
}
if (!SymInitialize(process, path.c_str(), FALSE))
throw 1;
DWORD options = SymGetOptions();
options |= SYMOPT_LOAD_LINES;
options |= SYMOPT_FAIL_CRITICAL_ERRORS;
options = SymSetOptions(options);
}
~SymStartup() {
if (process)
SymCleanup(process);
}
};
//inline std::string wstr2str(std::wstring const& ws)
//{
// using namespace std;
// std::string mbs;
// ctype<wchar_t> const& conv(use_facet<ctype<wchar_t> >(locale()));
//
// mbs.reserve(ws.size());
// for (wstring::const_iterator it = ws.begin(); it != ws.end(); ++it)
// mbs.push_back(conv.narrow(*it, '?'));
//
// return mbs;
//}
std::string wstr2str(const std::wstring &wstr) {
std::string strTo;
char *szTo = new char[wstr.length() + 1];
szTo[wstr.size()] = '\0';
WideCharToMultiByte(CP_ACP, 0, wstr.c_str(), -1, szTo, (int)wstr.length(), NULL, NULL);
strTo = szTo;
delete[] szTo;
return strTo;
}
class StackTraceImpl {
private:
void load_modules(HANDLE process, DWORD processID) {
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPMODULE, processID);
if (snap == INVALID_HANDLE_VALUE)
return;
MODULEENTRY32 entry;
entry.dwSize = sizeof(entry);
if (Module32First(snap, &entry)) {
do {
std::string fileName = wstr2str(entry.szExePath);
std::string moduleName = wstr2str(entry.szModule);
SymLoadModule64(process, NULL, fileName.c_str(), moduleName.c_str(), (DWORD64) entry.modBaseAddr, entry.modBaseSize);
} while (Module32Next(snap, &entry));
}
CloseHandle(snap);
}
void retrieve_context(CONTEXT& context) {
std::memset(&context, 0, sizeof(context));
context.ContextFlags = CONTEXT_FULL;
RtlCaptureContext(&context);
}
void retrieve_frame(CONTEXT& context, STACKFRAME64& frame, DWORD& imageType) {
std::memset(&frame, 0, sizeof(frame));
#ifdef _M_IX86
imageType = IMAGE_FILE_MACHINE_I386;
frame.AddrPC.Offset = context.Eip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.Ebp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.Esp;
frame.AddrStack.Mode = AddrModeFlat;
#elif _M_X64
imageType = IMAGE_FILE_MACHINE_AMD64;
frame.AddrPC.Offset = context.Rip;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.Rsp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.Rsp;
frame.AddrStack.Mode = AddrModeFlat;
#elif _M_IA64
imageType = IMAGE_FILE_MACHINE_IA64;
frame.AddrPC.Offset = context.StIIP;
frame.AddrPC.Mode = AddrModeFlat;
frame.AddrFrame.Offset = context.IntSp;
frame.AddrFrame.Mode = AddrModeFlat;
frame.AddrBStore.Offset = context.RsBSP;
frame.AddrBStore.Mode = AddrModeFlat;
frame.AddrStack.Offset = context.IntSp;
frame.AddrStack.Mode = AddrModeFlat;
#else
#error "Platform not supported!"
#endif
}
public:
void retrieve(StackTrace::ItemContainer& items) {
HANDLE process = 0;
try {
items.clear();
process = GetCurrentProcess();
SymStartup startup(process);
load_modules(process, GetCurrentProcessId());
HANDLE thread = GetCurrentThread();
CONTEXT context;
retrieve_context(context);
DWORD imageType = 0;
STACKFRAME64 frame;
retrieve_frame(context, frame, imageType);
std::vector<char> symbolData(sizeof(IMAGEHLP_SYMBOL64) + SYMBOL_NAME_MAXLEN, 0);
IMAGEHLP_SYMBOL64* symbol = reinterpret_cast<IMAGEHLP_SYMBOL64*>(&symbolData[0]);
symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
symbol->MaxNameLength = SYMBOL_NAME_MAXLEN;
IMAGEHLP_LINE64 m_line;
std::memset(&m_line, 0, sizeof(IMAGEHLP_LINE64));
m_line.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
for (int frameNum = 0; true; ++frameNum) {
if (!StackWalk64(imageType, process, thread, &frame, &context, NULL, &SymFunctionTableAccess64, &SymGetModuleBase64, NULL))
break;
if (frame.AddrPC.Offset == frame.AddrReturn.Offset)
break;
if (frame.AddrPC.Offset != 0) {
StackItem item;
DWORD64 displacement64 = 0;
if (SymGetSymFromAddr64(process, frame.AddrPC.Offset, &displacement64, symbol)) {
char symbolName[SYMBOL_NAME_MAXLEN];
std::strncpy(symbolName, symbol->Name, SYMBOL_NAME_MAXLEN);
UnDecorateSymbolName(symbol->Name, symbolName, SYMBOL_NAME_MAXLEN, UNDNAME_COMPLETE);
item.m_name.assign(symbolName, symbolName + std::strlen(symbolName));
}
DWORD displacement = 0;
if (SymGetLineFromAddr64(process, frame.AddrPC.Offset, &displacement, &m_line)) {
item.m_line = m_line.LineNumber;
item.m_file.assign(m_line.FileName, m_line.FileName + std::strlen(m_line.FileName));
}
items.push_back(item);
}
if (frame.AddrReturn.Offset == 0)
break;
}
} catch (...) {
}
}
};
StackTrace::StackTrace() : m_impl(new StackTraceImpl) {
m_impl->retrieve(m_items);
if (m_items.size() > 1)
m_items.erase(m_items.begin(), m_items.begin() + 2);
}
StackTrace::~StackTrace() {
}
void StackTrace::print() const {
for (StackTrace::ItemIterator it = m_items.begin(), end = m_items.end(); it != end; ++it)
std::cout << it->m_file << "(" << it->m_line << ") : " << it->m_name << std::endl;
}
主要文件
#include "StackTrace.h"
void func1() {
StackTrace st;
st.print();
}
void func2() {
func1();
}
void func3() {
func2();
}
void func4() {
func3();
}
void func5() {
func4();
}
void func6() {
func5();
}
int main ( int argc, char **argv ) {
func5();
return 0;
}
DEBUG BUILD OUTPUT
E:\Avinash\my_work\StackWalk64>Debug\StackWalk64.exe
e:\avinash\my_work\stackwalk64\stackwalk64\stackwalk64main.cpp(3) : func1
e:\avinash\my_work\stackwalk64\stackwalk64\stackwalk64main.cpp(8) : func2
e:\avinash\my_work\stackwalk64\stackwalk64\stackwalk64main.cpp(11) : func3
e:\avinash\my_work\stackwalk64\stackwalk64\stackwalk64main.cpp(14) : func4
e:\avinash\my_work\stackwalk64\stackwalk64\stackwalk64main.cpp(17) : func5
e:\avinash\my_work\stackwalk64\stackwalk64\stackwalk64main.cpp(23) : main
f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c(555) : __tmainCRTStartup
f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c(371) : mainCRTStartup
(0) : BaseThreadInitThunk
(0) : RtlInitializeExceptionChain
(0) : RtlInitializeExceptionChain
释放构建输出
E:\Avinash\my_work\StackWalk64>Release\StackWalk64.exe
f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c(555) : __tmainCRTStartup
(0) : BaseThreadInitThunk
(0) : RtlInitializeExceptionChain
(0) : RtlInitializeExceptionChain
删除PDB文件后
E:\Avinash\my_work\StackWalk64\Debug>StackWalk64.exe
(0) :
(0) :
(0) :
(0) :
(0) :
(0) :
(0) :
(0) :
(0) : BaseThreadInitThunk
(0) : RtlInitializeExceptionChain
(0) : RtlInitializeExceptionChain
答案 0 :(得分:8)
您不能指望在优化代码上可靠地遍历堆栈。消除堆栈帧位于代码优化器的命中列表的顶部。 “忽略帧指针”优化是一个重要的优点,它释放了一个额外的寄存器(EBP),对x86代码来说一直很重要。默认情况下它通常是关闭的,但代码生成器无论如何都可以在内联函数时应用它。
最强的一个是“内联函数扩展”优化,它用目标函数体中的代码替换函数调用。这对您的测试代码有一个很好的数字,它完全消除了所有的功能。换句话说,func1()中的代码被移动到main()。
禁用这些优化是不明智的,它会极大地影响代码的效率。 C和C ++语言设计得很快,可调试性不是主要考虑因素。这就是调试和发布配置首先存在的原因。请记住,您的测试代码过于人为,无法成为实际代码中发生情况的可靠指标。您将从堆栈跟踪中获取某些,而不是调试代码时习惯的内容。