StackWalk64不适用于发布版本

时间:2012-09-05 11:31:39

标签: c++ windows stack-trace

以下是我的代码,仅限开源。为什么StackWalk64不适用于release builddebug 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

1 个答案:

答案 0 :(得分:8)

您不能指望在优化代码上可靠地遍历堆栈。消除堆栈帧位于代码优化器的命中列表的顶部。 “忽略帧指针”优化是一个重要的优点,它释放了一个额外的寄存器(EBP),对x86代码来说一直很重要。默认情况下它通常是关闭的,但代码生成器无论如何都可以在内联函数时应用它。

最强的一个是“内联函数扩展”优化,它用目标函数体中的代码替换函数调用。这对您的测试代码有一个很好的数字,它完全消除了所有的功能。换句话说,func1()中的代码被移动到main()。

禁用这些优化是不明智的,它会极大地影响代码的效率。 C和C ++语言设计得很快,可调试性不是主要考虑因素。这就是调试和发布配置首先存在的原因。请记住,您的测试代码过于人为,无法成为实际代码中发生情况的可靠指标。您将从堆栈跟踪中获取某些,而不是调试代码时习惯的内容。