-
Notifications
You must be signed in to change notification settings - Fork 45
Fix stacktrace issues on Windows #122
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for looking into this and getting to the bottom of the problem!
Perhaps the one thing I'm somewhat iffy about is the dependency on windows
here. I think this and windows-sys
are outsized dependencies for the tracy-client-sys
crate. Given the limited use of the APIs, could this perhaps be changed to use windows-targets
instead? Some of my thoughts on the windows{,-sys}
dependency are here. You can also find some inspiration for the changes here at that same link.
It is fine if you don't have the time or capacity for such a change -- I'll just get around to it myself at some point in the future before I cut the next release.
tracy-client-sys/src/dbghelp.rs
Outdated
// Initialize the `dbghelp.dll` symbol helper by capturing and resolving a backtrace using the standard library. | ||
// Since symbol resolution is lazy, the backtrace is written to `sink`, which forces symbol resolution. | ||
// Refer to the module documentation on why the standard library should do the initialization instead of Tracy. | ||
write!(sink(), "{:?}", std::backtrace::Backtrace::force_capture()).unwrap(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we avoid the unwrap
here? Although it is very likely that writing a backtrace to a sink()
cannot fail at all, ever, just in case there is some perverse corner case, I would rather users deal with broken stack traces than the program failing completely in that scenario.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The result is ignored now. This is only called to trigger a symbol resolve in the standard library, so the result doesn't matter.
tracy-client-sys/src/dbghelp.rs
Outdated
write!(sink(), "{:?}", std::backtrace::Backtrace::force_capture()).unwrap(); | ||
|
||
// The name is the same one that the standard library and `backtrace-rs` use | ||
let mut name = [0; 33]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let mut name = [0; 33]; | |
let mut name: [u8; 33] = *b"Local\\RustBacktraceMutexFFFFFFFF\0"; |
makes it a little more obvious that 33 is indeed sufficient (and exact) size required.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It uses format!
directly now. I initially adapted this from the implementation in backtrace-rs
which seems to avoid allocations, but that is not necessary here.
tracy-client-sys/src/dbghelp.rs
Outdated
// Initialization of the `dbghelp.dll` symbol helper should have already happened | ||
// through the standard library backtrace above. | ||
// Therefore, the shared named mutex should already have existed. | ||
assert_eq!(GetLastError(), ERROR_ALREADY_EXISTS); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd debug_assert_eq
this for the similar reasons as above (in case libstd
stops creating this Mutex, if it just so happened that the dbghelp no longer needed any locks to work.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I changed the error handling, and it will no longer panic should the standard library change its symbol resolution logic.
Only a small subset of the windows APIs is necessary. So we define that subset ourselves and avoid a heavy dependency.
Sorry for the delay! Will release 0.24.2 with this shortly. |
There are 2 issues with capturing stack traces on Windows.
Specifically with the resolving of the symbols of the stack traces.
Note that this affects both the stack traces captured by Rust and Tracy.
Both Rust and Tracy use the
dbghelp.dll
symbol helper to resolve symbols.The first issue is that
dbghelp.dll
is single threaded and all its functions need to be externally synchronized.Rust uses a named Windows mutex to synchronize access to
dbghelp.dll
between the standard library and thebacktrace-rs
crate when both are used.The first commit in this PR makes Tracy use the same named Mutex.
Not properly synchronizing can lead to partially corrupted stacktraces.
Example corrupted stacktrace
Note the
<unknown>
at the top probably caused by some race conditionThe second issue is that Rust and Tracy initialize the symbol helper in different ways.
Since Rust 1.78.0 the compiler no longer includes absolute paths to .pdb files in the binary (rust-lang/rust#121297). That can make the symbol resolution fail because by default only the current working directory is searched for .pdb files.
There was also a corresponding PR to
backtrace-rs
to include the executable location in the search path (rust-lang/backtrace-rs#584), but when Tracy initializesdbghelp.dll
first, the modules get loaded before the search path gets modified.The second commit in this PR fixes that by capturing and resolving a backtrace using the standard library before Tracy does the initialization.
This fixes #101.