Skip to content

Commit 7b7aa5a

Browse files
committed
[Runtime][Win32] Fix fatalError() backtraces.
We were calling `SymInitialize()` multiple times, which is wrong, which was making the `SymbolInfo::lookup()` call fail. Also, we weren't fetching the module names, so we should do that too. rdar://130992923
1 parent ce381e7 commit 7b7aa5a

File tree

4 files changed

+193
-36
lines changed

4 files changed

+193
-36
lines changed

stdlib/public/runtime/Errors.cpp

Lines changed: 36 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -100,25 +100,31 @@ static bool getSymbolNameAddr(llvm::StringRef libraryName,
100100
// providing failure status instead of just returning the original string like
101101
// swift demangle.
102102
#if defined(_WIN32)
103-
char szUndName[1024];
104-
DWORD dwResult;
105-
dwResult = _swift_win32_withDbgHelpLibrary([&] (HANDLE hProcess) -> DWORD {
106-
if (!hProcess) {
107-
return 0;
108-
}
109-
110-
DWORD dwFlags = UNDNAME_COMPLETE;
103+
const char *szSymbolName = syminfo.getSymbolName();
104+
105+
// UnDecorateSymbolName() will not fail for Swift symbols, so detect them
106+
// up-front and let Swift handle them.
107+
if (!Demangle::isMangledName(syminfo.getSymbolName())) {
108+
char szUndName[1024];
109+
DWORD dwResult;
110+
dwResult = _swift_win32_withDbgHelpLibrary([&] (HANDLE hProcess) -> DWORD {
111+
if (!hProcess) {
112+
return 0;
113+
}
114+
115+
DWORD dwFlags = UNDNAME_COMPLETE;
111116
#if !defined(_WIN64)
112-
dwFlags |= UNDNAME_32_BIT_DECODE;
117+
dwFlags |= UNDNAME_32_BIT_DECODE;
113118
#endif
114119

115-
return UnDecorateSymbolName(syminfo.getSymbolName(), szUndName,
116-
sizeof(szUndName), dwFlags);
117-
});
120+
return UnDecorateSymbolName(syminfo.getSymbolName(), szUndName,
121+
sizeof(szUndName), dwFlags);
122+
});
118123

119-
if (dwResult) {
120-
symbolName += szUndName;
121-
return true;
124+
if (dwResult) {
125+
symbolName += szUndName;
126+
return true;
127+
}
122128
}
123129
#else
124130
int status;
@@ -149,6 +155,9 @@ void swift::dumpStackTraceEntry(unsigned index, void *framePC,
149155
#if SWIFT_STDLIB_SUPPORTS_BACKTRACE_REPORTING && SWIFT_STDLIB_HAS_DLADDR
150156
auto syminfo = SymbolInfo::lookup(framePC);
151157
if (!syminfo.has_value()) {
158+
constexpr const char *format = "%-4u %-34s 0x%0.16tx\n";
159+
fprintf(stderr, format, index, "<unknown>",
160+
reinterpret_cast<uintptr_t>(framePC));
152161
return;
153162
}
154163

@@ -157,7 +166,12 @@ void swift::dumpStackTraceEntry(unsigned index, void *framePC,
157166
// is not provided in the header so that it requires linking with
158167
// libSupport.a.
159168
llvm::StringRef libraryName{syminfo->getFilename()};
169+
170+
#ifdef _WIN32
171+
libraryName = libraryName.substr(libraryName.rfind('\\')).substr(1);
172+
#else
160173
libraryName = libraryName.substr(libraryName.rfind('/')).substr(1);
174+
#endif
161175

162176
// Next we get the symbol name that we are going to use in our backtrace.
163177
std::string symbolName;
@@ -177,6 +191,11 @@ void swift::dumpStackTraceEntry(unsigned index, void *framePC,
177191
symbolName = "<unavailable>";
178192
}
179193

194+
const char *libraryNameStr = libraryName.data();
195+
196+
if (!libraryNameStr)
197+
libraryNameStr = "<unknown>";
198+
180199
// We do not use %p here for our pointers since the format is implementation
181200
// defined. This makes it logically impossible to check the output. Forcing
182201
// hexadecimal solves this issue.
@@ -185,11 +204,11 @@ void swift::dumpStackTraceEntry(unsigned index, void *framePC,
185204
// This gives enough info to reconstruct identical debugging target after
186205
// this process terminates.
187206
if (shortOutput) {
188-
fprintf(stderr, "%s`%s + %td", libraryName.data(), symbolName.c_str(),
207+
fprintf(stderr, "%s`%s + %td", libraryNameStr, symbolName.c_str(),
189208
offset);
190209
} else {
191210
constexpr const char *format = "%-4u %-34s 0x%0.16" PRIxPTR " %s + %td\n";
192-
fprintf(stderr, format, index, libraryName.data(), symbolAddr,
211+
fprintf(stderr, format, index, libraryNameStr, symbolAddr,
193212
symbolName.c_str(), offset);
194213
}
195214
#else

stdlib/public/runtime/ImageInspectionCOFF.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ using namespace swift;
3131
#if defined(_WIN32)
3232
static LazyMutex mutex;
3333
static HANDLE dbgHelpHandle = nullptr;
34+
static bool isDbgHelpInitialized = false;
3435

3536
void _swift_win32_withDbgHelpLibrary(
3637
void (* body)(HANDLE hProcess, void *context), void *context) {
@@ -54,8 +55,7 @@ void _swift_win32_withDbgHelpLibrary(
5455
}
5556

5657
// If we have not previously initialized the Debug Help library, do so now.
57-
bool isDbgHelpInitialized = false;
58-
if (dbgHelpHandle) {
58+
if (dbgHelpHandle && !isDbgHelpInitialized) {
5959
isDbgHelpInitialized = SymInitialize(dbgHelpHandle, nullptr, true);
6060
}
6161

stdlib/public/runtime/SymbolInfo.cpp

Lines changed: 94 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#define NOMINMAX
1818
#include <Windows.h>
1919
#include <DbgHelp.h>
20+
#include <psapi.h>
2021
#elif SWIFT_STDLIB_HAS_DLADDR
2122
#include <dlfcn.h>
2223
#endif
@@ -29,7 +30,7 @@ using namespace swift;
2930

3031
const char *SymbolInfo::getFilename() const {
3132
#if defined(_WIN32) && !defined(__CYGWIN__)
32-
return nullptr;
33+
return _moduleFileName;
3334
#elif SWIFT_STDLIB_HAS_DLADDR
3435
return _info.dli_fname;
3536
#else
@@ -39,7 +40,7 @@ const char *SymbolInfo::getFilename() const {
3940

4041
const void *SymbolInfo::getBaseAddress() const {
4142
#if defined(_WIN32) && !defined(__CYGWIN__)
42-
return reinterpret_cast<const void *>(_package.si.ModBase);
43+
return _moduleBaseAddress;
4344
#elif SWIFT_STDLIB_HAS_DLADDR
4445
return _info.dli_fbase;
4546
#else
@@ -49,7 +50,7 @@ const void *SymbolInfo::getBaseAddress() const {
4950

5051
const char *SymbolInfo::getSymbolName() const {
5152
#if defined(_WIN32) && !defined(__CYGWIN__)
52-
return _package.si.Name;
53+
return _symbolName;
5354
#elif SWIFT_STDLIB_HAS_DLADDR
5455
return _info.dli_sname;
5556
#else
@@ -59,42 +60,119 @@ const char *SymbolInfo::getSymbolName() const {
5960

6061
const void *SymbolInfo::getSymbolAddress() const {
6162
#if defined(_WIN32) && !defined(__CYGWIN__)
62-
return reinterpret_cast<const void *>(_package.si.Address);
63+
return _symbolAddress;
6364
#elif SWIFT_STDLIB_HAS_DLADDR
6465
return _info.dli_saddr;
6566
#else
6667
return nullptr;
6768
#endif
6869
}
6970

70-
std::optional<SymbolInfo> SymbolInfo::lookup(const void *address) {
71-
std::optional<SymbolInfo> result;
71+
#if defined(_WIN32) && !defined(__CYGWIN__)
72+
struct Win32ModuleInfo {
73+
const char *name;
74+
const void *base;
75+
76+
Win32ModuleInfo(const char *n, const void *b) : name(n), base(b) {}
77+
};
78+
79+
// Get the filename and base of the module that contains the specified
80+
// address. N.B. This returns a malloc()ed copy of the filename in the
81+
// Win32ModuleInfo struct; you are responsible for freeing that.
82+
static Win32ModuleInfo moduleInfoFromAddress(const void *address) {
83+
HMODULE hModule;
84+
if (GetModuleHandleEx(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS
85+
| GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT,
86+
(LPCTSTR)address,
87+
&hModule)) {
88+
MODULEINFO mi;
89+
90+
if (!GetModuleInformation(GetCurrentProcess(), hModule,
91+
&mi, sizeof(mi))) {
92+
::memset(&mi, 0, sizeof(mi));
93+
}
94+
95+
WCHAR wszBuffer[256];
96+
LPWSTR pwszFileName = wszBuffer;
97+
DWORD dwCapacity = sizeof(wszBuffer) / sizeof(WCHAR);
98+
DWORD dwRet = GetModuleFileNameW(hModule, pwszFileName, dwCapacity);
99+
100+
if (dwRet == dwCapacity) {
101+
dwCapacity = 512;
102+
103+
pwszFileName = (LPWSTR)::malloc(sizeof(WCHAR) * dwCapacity);
104+
while (true) {
105+
dwRet = GetModuleFileNameW(hModule, pwszFileName, dwCapacity);
106+
if (dwRet != dwCapacity)
107+
break;
108+
109+
dwCapacity *= 2;
110+
111+
pwszFileName = (LPWSTR)::realloc(pwszFileName,
112+
sizeof(WCHAR) * dwCapacity);
113+
}
114+
}
115+
116+
if (dwRet == 0) {
117+
if (pwszFileName != wszBuffer)
118+
::free(pwszFileName);
72119

120+
return Win32ModuleInfo(::strdup("<unknown>"), mi.lpBaseOfDll);
121+
}
122+
123+
const char *result = _swift_win32_copyUTF8FromWide(pwszFileName);
124+
125+
if (pwszFileName != wszBuffer)
126+
::free((void *)pwszFileName);
127+
128+
return Win32ModuleInfo(result, mi.lpBaseOfDll);
129+
} else {
130+
return Win32ModuleInfo(::strdup("<unknown>"), nullptr);
131+
}
132+
}
133+
#endif
134+
135+
std::optional<SymbolInfo> SymbolInfo::lookup(const void *address) {
73136
#if defined(__wasm__)
74137
// Currently, Wasm doesn't have a standard stable ABI for exporting address <->
75138
// symbol table, it's work in progress. Also, there is no API to access such
76139
// information from Wasm binary side. It's accessible only from host VM.
77140
// See https://github.com/WebAssembly/tool-conventions/blob/main/DynamicLinking.md
78141
#elif defined(_WIN32) && !defined(__CYGWIN__)
142+
Win32ModuleInfo moduleInfo = moduleInfoFromAddress(address);
143+
SYMBOL_INFO_PACKAGE package;
144+
BOOL bRet;
145+
146+
package.si.SizeOfStruct = sizeof(SYMBOL_INFO);
147+
package.si.MaxNameLen = MAX_SYM_NAME;
148+
79149
_swift_win32_withDbgHelpLibrary([&] (HANDLE hProcess) {
80150
if (!hProcess) {
81-
return;
151+
bRet = false;
152+
return;
82153
}
83154

84-
SymbolInfo info;
85-
info._package.si.SizeOfStruct = sizeof(SYMBOL_INFO);
86-
info._package.si.MaxNameLen = MAX_SYM_NAME;
87-
if (SymFromAddr(hProcess, reinterpret_cast<const DWORD64>(address),
88-
nullptr, &info._package.si)) {
89-
result = info;
90-
}
155+
bRet = SymFromAddr(hProcess, reinterpret_cast<const DWORD64>(address),
156+
nullptr, &package.si);
91157
});
158+
159+
if (bRet) {
160+
return SymbolInfo((const void *)package.si.Address,
161+
::strdup(package.si.Name),
162+
moduleInfo.name,
163+
moduleInfo.base);
164+
} else {
165+
return SymbolInfo(address,
166+
nullptr,
167+
moduleInfo.name,
168+
moduleInfo.base);
169+
}
92170
#elif SWIFT_STDLIB_HAS_DLADDR
93171
SymbolInfo info;
94172
if (dladdr(address, &info._info)) {
95-
result = info;
173+
return info;
96174
}
97175
#endif
98176

99-
return result;
177+
return {};
100178
}

stdlib/public/runtime/SymbolInfo.h

Lines changed: 61 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,14 +43,74 @@ namespace swift {
4343
struct SymbolInfo {
4444
private:
4545
#if defined(_WIN32) && !defined(__CYGWIN__)
46-
SYMBOL_INFO_PACKAGE _package;
46+
const void *_symbolAddress;
47+
const char *_symbolName;
48+
const char *_moduleFileName;
49+
const void *_moduleBaseAddress;
4750
#elif SWIFT_STDLIB_HAS_DLADDR
4851
Dl_info _info;
4952
#endif
5053

54+
#if defined(_WIN32) && !defined(__CYGWIN__)
55+
SymbolInfo(const void *symbolAddress,
56+
const char *symbolName,
57+
const char *moduleFileName,
58+
const void *moduleBaseAddress)
59+
: _symbolAddress(symbolAddress),
60+
_symbolName(symbolName),
61+
_moduleFileName(moduleFileName),
62+
_moduleBaseAddress(moduleBaseAddress)
63+
{}
64+
#endif
65+
5166
public:
5267
SymbolInfo() {}
5368

69+
#if defined(_WIN32) && !defined(__CYGWIN__)
70+
SymbolInfo(const SymbolInfo &other) {
71+
_symbolName = nullptr;
72+
_moduleFileName = nullptr;
73+
*this = other;
74+
}
75+
SymbolInfo(SymbolInfo &&other) {
76+
*this = std::move(other);
77+
}
78+
~SymbolInfo() {
79+
if (_moduleFileName)
80+
::free((void *)_moduleFileName);
81+
if (_symbolName)
82+
::free((void *)_symbolName);
83+
}
84+
85+
SymbolInfo &operator=(const SymbolInfo &other) {
86+
if (this != &other) {
87+
if (_moduleFileName)
88+
::free((void *)_moduleFileName);
89+
if (_symbolName)
90+
::free((void *)_symbolName);
91+
92+
_symbolAddress = other._symbolAddress;
93+
_symbolName = ::strdup(other._symbolName);
94+
_moduleFileName = ::strdup(other._moduleFileName);
95+
_moduleBaseAddress = other._moduleBaseAddress;
96+
}
97+
98+
return *this;
99+
}
100+
SymbolInfo &operator=(SymbolInfo &&other) {
101+
if (this != &other) {
102+
_symbolAddress = other._symbolAddress;
103+
_symbolName = other._symbolName;
104+
other._symbolName = nullptr;
105+
_moduleFileName = other._moduleFileName;
106+
other._moduleFileName = nullptr;
107+
_moduleBaseAddress = other._moduleBaseAddress;
108+
}
109+
110+
return *this;
111+
}
112+
#endif
113+
54114
/// Get the file name of the image where the symbol was found.
55115
///
56116
/// The resulting C string is only valid for the lifetime of \c this.

0 commit comments

Comments
 (0)