aboutsummaryrefslogtreecommitdiff
path: root/lib/sanitizer_common/sanitizer_symbolizer_win.cc
blob: ed96a3a895a83794ea2d26505fa39af0deb1df54 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
//===-- sanitizer_symbolizer_win.cc ---------------------------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This file is shared between AddressSanitizer and ThreadSanitizer
// run-time libraries.
// Windows-specific implementation of symbolizer parts.
//===----------------------------------------------------------------------===//

#include "sanitizer_platform.h"
#if SANITIZER_WINDOWS
#include <windows.h>
#include <dbghelp.h>
#pragma comment(lib, "dbghelp.lib")

#include "sanitizer_symbolizer.h"

namespace __sanitizer {

class WinSymbolizer : public Symbolizer {
 public:
  WinSymbolizer() : initialized_(false) {}

  SymbolizedStack *SymbolizePC(uptr addr) override {
    SymbolizedStack *frame = SymbolizedStack::New(addr);

    BlockingMutexLock l(&dbghelp_mu_);
    InitializeIfNeeded();

    // See http://msdn.microsoft.com/en-us/library/ms680578(VS.85).aspx
    char buffer[sizeof(SYMBOL_INFO) + MAX_SYM_NAME * sizeof(CHAR)];
    PSYMBOL_INFO symbol = (PSYMBOL_INFO)buffer;
    symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
    symbol->MaxNameLen = MAX_SYM_NAME;
    DWORD64 offset = 0;
    BOOL got_objname = SymFromAddr(GetCurrentProcess(),
                                   (DWORD64)addr, &offset, symbol);
    if (!got_objname)
      return frame;

    DWORD unused;
    IMAGEHLP_LINE64 line_info;
    line_info.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
    BOOL got_fileline = SymGetLineFromAddr64(GetCurrentProcess(), (DWORD64)addr,
                                             &unused, &line_info);
    frame->info.function = internal_strdup(symbol->Name);
    frame->info.function_offset = (uptr)offset;
    if (got_fileline) {
      frame->info.file = internal_strdup(line_info.FileName);
      frame->info.line = line_info.LineNumber;
    }

    IMAGEHLP_MODULE64 mod_info;
    internal_memset(&mod_info, 0, sizeof(mod_info));
    mod_info.SizeOfStruct = sizeof(mod_info);
    if (SymGetModuleInfo64(GetCurrentProcess(), addr, &mod_info))
      frame->info.FillAddressAndModuleInfo(addr, mod_info.ImageName,
                                           addr - (uptr)mod_info.BaseOfImage);
    return frame;
  }

  bool CanReturnFileLineInfo() override {
    return true;
  }

  const char *Demangle(const char *name) override {
    CHECK(initialized_);
    static char demangle_buffer[1000];
    if (name[0] == '\01' &&
        UnDecorateSymbolName(name + 1, demangle_buffer, sizeof(demangle_buffer),
                             UNDNAME_NAME_ONLY))
      return demangle_buffer;
    else
      return name;
  }

  // FIXME: Implement GetModuleNameAndOffsetForPC().

 private:
  void InitializeIfNeeded() {
    if (initialized_)
      return;
    if (!TrySymInitialize()) {
      // OK, maybe the client app has called SymInitialize already.
      // That's a bit unfortunate for us as all the DbgHelp functions are
      // single-threaded and we can't coordinate with the app.
      // FIXME: Can we stop the other threads at this point?
      // Anyways, we have to reconfigure stuff to make sure that SymInitialize
      // has all the appropriate options set.
      // Cross our fingers and reinitialize DbgHelp.
      Report("*** WARNING: Failed to initialize DbgHelp!              ***\n");
      Report("*** Most likely this means that the app is already      ***\n");
      Report("*** using DbgHelp, possibly with incompatible flags.    ***\n");
      Report("*** Due to technical reasons, symbolization might crash ***\n");
      Report("*** or produce wrong results.                           ***\n");
      SymCleanup(GetCurrentProcess());
      TrySymInitialize();
    }
    initialized_ = true;

    // When an executable is run from a location different from the one where it
    // was originally built, we may not see the nearby PDB files.
    // To work around this, let's append the directory of the main module
    // to the symbol search path.  All the failures below are not fatal.
    const size_t kSymPathSize = 2048;
    static wchar_t path_buffer[kSymPathSize + 1 + MAX_PATH];
    if (!SymGetSearchPathW(GetCurrentProcess(), path_buffer, kSymPathSize)) {
      Report("*** WARNING: Failed to SymGetSearchPathW ***\n");
      return;
    }
    size_t sz = wcslen(path_buffer);
    if (sz) {
      CHECK_EQ(0, wcscat_s(path_buffer, L";"));
      sz++;
    }
    DWORD res = GetModuleFileNameW(NULL, path_buffer + sz, MAX_PATH);
    if (res == 0 || res == MAX_PATH) {
      Report("*** WARNING: Failed to getting the EXE directory ***\n");
      return;
    }
    // Write the zero character in place of the last backslash to get the
    // directory of the main module at the end of path_buffer.
    wchar_t *last_bslash = wcsrchr(path_buffer + sz, L'\\');
    CHECK_NE(last_bslash, 0);
    *last_bslash = L'\0';
    if (!SymSetSearchPathW(GetCurrentProcess(), path_buffer)) {
      Report("*** WARNING: Failed to SymSetSearchPathW\n");
      return;
    }
  }

  bool TrySymInitialize() {
    SymSetOptions(SYMOPT_DEFERRED_LOADS | SYMOPT_UNDNAME | SYMOPT_LOAD_LINES);
    return SymInitialize(GetCurrentProcess(), 0, TRUE);
    // FIXME: We don't call SymCleanup() on exit yet - should we?
  }

  // All DbgHelp functions are single threaded, so we should use a mutex to
  // serialize accesses.
  BlockingMutex dbghelp_mu_;
  bool initialized_;
};

Symbolizer *Symbolizer::PlatformInit() {
  static bool called_once = false;
  CHECK(!called_once && "Shouldn't create more than one symbolizer");
  called_once = true;
  return new(symbolizer_allocator_) WinSymbolizer();
}

}  // namespace __sanitizer

#endif  // _WIN32