15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2009 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// A smaller wrapper around the dbghelp symbol resolution routines.
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// For example:
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//   SymResolver resolver("ntdll.dll");
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)//   resolver.Resolve("ntdll!NtBlahBlah");
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#ifndef TRACELINE_SYM_RESOLVER_H_
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#define TRACELINE_SYM_RESOLVER_H_
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <windows.h>
145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <dbghelp.h>
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <vector>
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <string>
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include <map>
195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)static BOOL CALLBACK SymEnumer(PCSTR name, DWORD64 base, PVOID context) {
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  reinterpret_cast<std::vector<DWORD64>*>(context)->push_back(base);
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return TRUE;
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class SymResolver {
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Constructor to load a single DLL.
295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  SymResolver(const char* dllname, HANDLE proc = ::GetCurrentProcess())
305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      : proc_(proc) {
315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // TODO(deanm): Would be nice to get this from WinDBG, but it's buried
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // in the workspace data blob... _NT_SYMBOL_PATH is not usually set...
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    static char* kSymbolPath =
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        "C:\\Program Files\\Debugging Tools for Windows (x86)\\sym;"
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        "C:\\Program Files\\Debugging Tools for Windows\\sym";
375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // If we want to load a specific DLL, or we want to load all.
395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (::SymInitialize(proc_, kSymbolPath, dllname ? FALSE : TRUE) != TRUE) {
405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED("SymInitialize failed: %d", GetLastError());
415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    base_ = 0;
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (dllname) {
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      base_ = ::SymLoadModuleEx(proc_,
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                NULL,
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                const_cast<char*>(dllname),
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                NULL,
505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                reinterpret_cast<DWORD64>(
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    GetModuleHandleA(dllname)),
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                0,
535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                NULL,
545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                0);
555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (base_ == 0) {
565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        NOTREACHED("SymLoadModuleEx(%s) failed: %d", dllname, GetLastError());
575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::vector<DWORD64> bases;
615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // The name returned from SymEnumerateModules64 doesn't include the ext,
625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // so we can't differentiate between a dll and exe of the same name. So
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // collect all of the base addresses and query for more info.
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // The prototype changed from PSTR to PCSTR, so in order to support older
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // SDKs we have to cast SymEnumer.
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    PSYM_ENUMMODULES_CALLBACK64 enumer =
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        reinterpret_cast<PSYM_ENUMMODULES_CALLBACK64>(&SymEnumer);
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (SymEnumerateModules64(proc_, enumer, &bases) != TRUE) {
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED("SymEnumerateModules64 failed: %d\n", GetLastError());
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    for (size_t i = 0; i < bases.size(); ++i) {
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // This was failing, turns out I was just using the system32
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // dbghelp.dll which is old, use the one from windbg :(
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      IMAGEHLP_MODULE64 info;
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      info.SizeOfStruct = sizeof(info);
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (SymGetModuleInfo64(proc_, bases[i], &info) != TRUE) {
775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        NOTREACHED("SymGetModuleInfo64 failed: %d\n", GetLastError());
785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::string filename(info.ImageName);
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      size_t last_slash = filename.find_last_of('\\');
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (last_slash != std::string::npos)
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        filename = filename.substr(filename.find_last_of('\\') + 1);
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Map the base address to the image name...
855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      dlls_[static_cast<int>(bases[i])] = filename;
865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // TODO(deanm): check the symbols are rad and stuff...
895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  char* Resolve(const char* name) {
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // The API writes to the space after SYMBOL_INFO...
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    struct {
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SYMBOL_INFO info;
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      char buf[128];
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } info = {0};
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    info.info.SizeOfStruct = sizeof(info.info);
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    info.info.ModBase = base_;
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    info.info.MaxNameLen = 127;
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (SymFromName(proc_, const_cast<char*>(name), &info.info) != TRUE) {
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED("SymFromName(%s) failed: %d", name, GetLastError());
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return reinterpret_cast<char*>(info.info.Address);
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string Unresolve(int ptr) {
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // The API writes to the space after SYMBOL_INFO...
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    struct {
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      SYMBOL_INFO info;
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      char buf[128];
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } info = {0};
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    info.info.SizeOfStruct = sizeof(info.info);
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    info.info.ModBase = base_;
1185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    info.info.MaxNameLen = 127;
1195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!::SymFromAddr(proc_, static_cast<DWORD64>(ptr), NULL, &info.info)) {
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      return std::string("failed");
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string name;
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int addr = static_cast<int>(info.info.Address);
1255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int base = static_cast<int>(info.info.ModBase);
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (dlls_.count(base) == 1) {
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      name.append(dlls_[base]);
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      name.append("unknown_mod");
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    name.push_back('!');
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    name.append(info.info.Name);
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    char buf[32];
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    _itoa_s(ptr - addr, buf, sizeof(buf), 16);
1375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    name.append("+0x");
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    name.append(buf);
1395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DWORD disp;
1415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    IMAGEHLP_LINE64 line;
1425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (::SymGetLineFromAddr64(
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)            proc_, static_cast<DWORD64>(ptr), &disp, &line)) {
1445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      name.append(" [ ");
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      name.append(line.FileName);
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      name.append(":");
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      _itoa_s(line.LineNumber, buf, sizeof(buf), 10);
1485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      name.append(buf);
1495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      name.append(" ]");
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return name;
1535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ~SymResolver() {
1565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (::SymCleanup(proc_) != TRUE) {
1575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED("SymCleanup failed: %d", GetLastError());
1585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
1605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
1625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HANDLE proc_;
1635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ULONG64 base_;
1645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::map<int, std::string> dlls_;
1655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#endif  // TRACELINE_SYM_RESOLVER_H_
168