Home | History | Annotate | Download | only in traceline
      1 // Copyright (c) 2009 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 // A smaller wrapper around the dbghelp symbol resolution routines.
      6 // For example:
      7 //   SymResolver resolver("ntdll.dll");
      8 //   resolver.Resolve("ntdll!NtBlahBlah");
      9 
     10 #ifndef TRACELINE_SYM_RESOLVER_H_
     11 #define TRACELINE_SYM_RESOLVER_H_
     12 
     13 #include <windows.h>
     14 #include <dbghelp.h>
     15 
     16 #include <vector>
     17 #include <string>
     18 #include <map>
     19 
     20 static BOOL CALLBACK SymEnumer(PCSTR name, DWORD64 base, PVOID context) {
     21   reinterpret_cast<std::vector<DWORD64>*>(context)->push_back(base);
     22   return TRUE;
     23 }
     24 
     25 class SymResolver {
     26  public:
     27 
     28   // Constructor to load a single DLL.
     29   SymResolver(const char* dllname, HANDLE proc = ::GetCurrentProcess())
     30       : proc_(proc) {
     31 
     32     // TODO(deanm): Would be nice to get this from WinDBG, but it's buried
     33     // in the workspace data blob... _NT_SYMBOL_PATH is not usually set...
     34     static char* kSymbolPath =
     35         "C:\\Program Files\\Debugging Tools for Windows (x86)\\sym;"
     36         "C:\\Program Files\\Debugging Tools for Windows\\sym";
     37 
     38     // If we want to load a specific DLL, or we want to load all.
     39     if (::SymInitialize(proc_, kSymbolPath, dllname ? FALSE : TRUE) != TRUE) {
     40       NOTREACHED("SymInitialize failed: %d", GetLastError());
     41     }
     42 
     43     base_ = 0;
     44 
     45     if (dllname) {
     46       base_ = ::SymLoadModuleEx(proc_,
     47                                 NULL,
     48                                 const_cast<char*>(dllname),
     49                                 NULL,
     50                                 reinterpret_cast<DWORD64>(
     51                                     GetModuleHandleA(dllname)),
     52                                 0,
     53                                 NULL,
     54                                 0);
     55       if (base_ == 0) {
     56         NOTREACHED("SymLoadModuleEx(%s) failed: %d", dllname, GetLastError());
     57       }
     58     }
     59 
     60     std::vector<DWORD64> bases;
     61     // The name returned from SymEnumerateModules64 doesn't include the ext,
     62     // so we can't differentiate between a dll and exe of the same name. So
     63     // collect all of the base addresses and query for more info.
     64     // The prototype changed from PSTR to PCSTR, so in order to support older
     65     // SDKs we have to cast SymEnumer.
     66     PSYM_ENUMMODULES_CALLBACK64 enumer =
     67         reinterpret_cast<PSYM_ENUMMODULES_CALLBACK64>(&SymEnumer);
     68     if (SymEnumerateModules64(proc_, enumer, &bases) != TRUE) {
     69       NOTREACHED("SymEnumerateModules64 failed: %d\n", GetLastError());
     70     }
     71     for (size_t i = 0; i < bases.size(); ++i) {
     72       // This was failing, turns out I was just using the system32
     73       // dbghelp.dll which is old, use the one from windbg :(
     74       IMAGEHLP_MODULE64 info;
     75       info.SizeOfStruct = sizeof(info);
     76       if (SymGetModuleInfo64(proc_, bases[i], &info) != TRUE) {
     77         NOTREACHED("SymGetModuleInfo64 failed: %d\n", GetLastError());
     78       }
     79       std::string filename(info.ImageName);
     80       size_t last_slash = filename.find_last_of('\\');
     81       if (last_slash != std::string::npos)
     82         filename = filename.substr(filename.find_last_of('\\') + 1);
     83 
     84       // Map the base address to the image name...
     85       dlls_[static_cast<int>(bases[i])] = filename;
     86     }
     87 
     88     // TODO(deanm): check the symbols are rad and stuff...
     89   }
     90 
     91   char* Resolve(const char* name) {
     92     // The API writes to the space after SYMBOL_INFO...
     93     struct {
     94       SYMBOL_INFO info;
     95       char buf[128];
     96     } info = {0};
     97 
     98     info.info.SizeOfStruct = sizeof(info.info);
     99     info.info.ModBase = base_;
    100     info.info.MaxNameLen = 127;
    101 
    102     if (SymFromName(proc_, const_cast<char*>(name), &info.info) != TRUE) {
    103       NOTREACHED("SymFromName(%s) failed: %d", name, GetLastError());
    104     }
    105 
    106     return reinterpret_cast<char*>(info.info.Address);
    107   }
    108 
    109   std::string Unresolve(int ptr) {
    110     // The API writes to the space after SYMBOL_INFO...
    111     struct {
    112       SYMBOL_INFO info;
    113       char buf[128];
    114     } info = {0};
    115 
    116     info.info.SizeOfStruct = sizeof(info.info);
    117     info.info.ModBase = base_;
    118     info.info.MaxNameLen = 127;
    119     if (!::SymFromAddr(proc_, static_cast<DWORD64>(ptr), NULL, &info.info)) {
    120       return std::string("failed");
    121     }
    122 
    123     std::string name;
    124     int addr = static_cast<int>(info.info.Address);
    125     int base = static_cast<int>(info.info.ModBase);
    126 
    127     if (dlls_.count(base) == 1) {
    128       name.append(dlls_[base]);
    129     } else {
    130       name.append("unknown_mod");
    131     }
    132     name.push_back('!');
    133     name.append(info.info.Name);
    134 
    135     char buf[32];
    136     _itoa_s(ptr - addr, buf, sizeof(buf), 16);
    137     name.append("+0x");
    138     name.append(buf);
    139 
    140     DWORD disp;
    141     IMAGEHLP_LINE64 line;
    142     if (::SymGetLineFromAddr64(
    143             proc_, static_cast<DWORD64>(ptr), &disp, &line)) {
    144       name.append(" [ ");
    145       name.append(line.FileName);
    146       name.append(":");
    147       _itoa_s(line.LineNumber, buf, sizeof(buf), 10);
    148       name.append(buf);
    149       name.append(" ]");
    150     }
    151 
    152     return name;
    153   }
    154 
    155   ~SymResolver() {
    156     if (::SymCleanup(proc_) != TRUE) {
    157       NOTREACHED("SymCleanup failed: %d", GetLastError());
    158     }
    159   }
    160 
    161  private:
    162   HANDLE proc_;
    163   ULONG64 base_;
    164   std::map<int, std::string> dlls_;
    165 };
    166 
    167 #endif  // TRACELINE_SYM_RESOLVER_H_
    168