Skip to content

Commit 489e413

Browse files
authored
Merge pull request #231 from rust-lang/test-init-once-windows
Ignore return of `SymInitializeW` on Windows
2 parents 31a002a + e6ac926 commit 489e413

File tree

3 files changed

+26
-104
lines changed

3 files changed

+26
-104
lines changed

src/capture.rs

Lines changed: 0 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,6 @@ impl Backtrace {
124124
/// enabled, and the `std` feature is enabled by default.
125125
#[inline(never)] // want to make sure there's a frame here to remove
126126
pub fn new() -> Backtrace {
127-
let _guard = lock_and_platform_init();
128127
let mut bt = Self::create(Self::new as usize);
129128
bt.resolve();
130129
bt
@@ -155,7 +154,6 @@ impl Backtrace {
155154
/// enabled, and the `std` feature is enabled by default.
156155
#[inline(never)] // want to make sure there's a frame here to remove
157156
pub fn new_unresolved() -> Backtrace {
158-
let _guard = lock_and_platform_init();
159157
Self::create(Self::new_unresolved as usize)
160158
}
161159

@@ -205,7 +203,6 @@ impl Backtrace {
205203
/// This function requires the `std` feature of the `backtrace` crate to be
206204
/// enabled, and the `std` feature is enabled by default.
207205
pub fn resolve(&mut self) {
208-
let _guard = lock_and_platform_init();
209206
for frame in self.frames.iter_mut().filter(|f| f.symbols.is_none()) {
210207
let mut symbols = Vec::new();
211208
{
@@ -418,51 +415,6 @@ impl fmt::Debug for BacktraceSymbol {
418415
}
419416
}
420417

421-
// When using `dbghelp` on Windows this is a performance optimization. If
422-
// we don't do this then `SymInitializeW` is called once per trace and once per
423-
// frame during resolution. That function, however, takes quite some time! To
424-
// help speed it up this function can amortize the calls necessary by ensuring
425-
// that the scope this is called in only initializes when this is called and
426-
// doesn't reinitialize for the rest of the scope.
427-
#[cfg(all(windows, feature = "dbghelp"))]
428-
fn lock_and_platform_init() -> impl Drop {
429-
use std::mem::ManuallyDrop;
430-
431-
struct Cleanup {
432-
_lock: crate::lock::LockGuard,
433-
434-
// Need to make sure this is cleaned up before `_lock`
435-
dbghelp_cleanup: Option<ManuallyDrop<crate::dbghelp::Cleanup>>,
436-
}
437-
438-
impl Drop for Cleanup {
439-
fn drop(&mut self) {
440-
if let Some(cleanup) = self.dbghelp_cleanup.as_mut() {
441-
// Unsafety here should be ok since we're only dropping this in
442-
// `Drop` to ensure it's dropped before the lock, and `Drop`
443-
// should only be called once.
444-
unsafe {
445-
ManuallyDrop::drop(cleanup);
446-
}
447-
}
448-
}
449-
}
450-
451-
// Unsafety here should be ok because we only acquire the `dbghelp`
452-
// initialization (the unsafe part) after acquiring the global lock for this
453-
// crate. Note that we're also careful to drop it before the lock is
454-
// dropped.
455-
unsafe {
456-
Cleanup {
457-
_lock: crate::lock::lock(),
458-
dbghelp_cleanup: crate::dbghelp::init().ok().map(ManuallyDrop::new),
459-
}
460-
}
461-
}
462-
463-
#[cfg(not(all(windows, feature = "dbghelp")))]
464-
fn lock_and_platform_init() {}
465-
466418
#[cfg(feature = "serialize-rustc")]
467419
mod rustc_serialize_impls {
468420
use super::*;

src/dbghelp.rs

Lines changed: 24 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ macro_rules! dbghelp {
154154
// Convenience proxy to use the cleanup locks to reference dbghelp
155155
// functions.
156156
#[allow(dead_code)]
157-
impl Cleanup {
157+
impl Init {
158158
$(pub fn $name(&self) -> $name {
159159
unsafe {
160160
DBGHELP.$name().unwrap()
@@ -244,75 +244,45 @@ dbghelp! {
244244
}
245245
}
246246

247-
pub struct Cleanup;
248-
249-
// Number of times `init` has been called on this thread. This is externally
250-
// synchronized and doesn't use internal synchronization on our behalf.
251-
static mut COUNT: usize = 0;
252-
253-
// Used to restore `SymSetOptions` and `SymGetOptions` values.
254-
static mut OPTS_ORIG: DWORD = 0;
247+
pub struct Init;
255248

256249
/// Unsafe because this requires external synchronization, must be done
257250
/// inside of the same lock as all other backtrace operations.
258251
///
259252
/// Note that the `Dbghelp` returned must also be dropped within the same
260253
/// lock.
261254
#[cfg(all(windows, feature = "dbghelp"))]
262-
pub unsafe fn init() -> Result<Cleanup, ()> {
263-
// Initializing symbols has significant overhead, but initializing only
264-
// once without cleanup causes problems for external sources. For
265-
// example, the standard library checks the result of SymInitializeW
266-
// (which returns an error if attempting to initialize twice) and in
267-
// the event of an error, will not print a backtrace on panic.
268-
// Presumably, external debuggers may have similar issues.
269-
//
270-
// As a compromise, we'll keep track of the number of internal
271-
// initialization requests within a single API call in order to
272-
// minimize the number of init/cleanup cycles.
273-
if COUNT > 0 {
274-
COUNT += 1;
275-
return Ok(Cleanup);
255+
pub unsafe fn init() -> Result<Init, ()> {
256+
// Calling `SymInitializeW` is quite expensive, so we only do so once per
257+
// process.
258+
static mut INITIALIZED: bool = false;
259+
if INITIALIZED {
260+
return Ok(Init);
276261
}
277262

278263
// Actually load `dbghelp.dll` into the process here, returning an error if
279264
// that fails.
280265
DBGHELP.ensure_open()?;
281266

282-
OPTS_ORIG = DBGHELP.SymGetOptions().unwrap()();
267+
let orig = DBGHELP.SymGetOptions().unwrap()();
283268

284269
// Ensure that the `SYMOPT_DEFERRED_LOADS` flag is set, because
285270
// according to MSVC's own docs about this: "This is the fastest, most
286271
// efficient way to use the symbol handler.", so let's do that!
287-
DBGHELP.SymSetOptions().unwrap()(OPTS_ORIG | SYMOPT_DEFERRED_LOADS);
288-
289-
let ret = DBGHELP.SymInitializeW().unwrap()(GetCurrentProcess(), ptr::null_mut(), TRUE);
290-
if ret != TRUE {
291-
// Symbols may have been initialized by another library or an
292-
// external debugger
293-
DBGHELP.SymSetOptions().unwrap()(OPTS_ORIG);
294-
Err(())
295-
} else {
296-
COUNT += 1;
297-
Ok(Cleanup)
298-
}
299-
}
272+
DBGHELP.SymSetOptions().unwrap()(orig | SYMOPT_DEFERRED_LOADS);
300273

301-
impl Drop for Cleanup {
302-
fn drop(&mut self) {
303-
unsafe {
304-
COUNT -= 1;
305-
if COUNT != 0 {
306-
return;
307-
}
308-
309-
// Clean up after ourselves by cleaning up symbols and restoring the
310-
// symbol options to their original value. This is currently
311-
// required to cooperate with libstd as libstd's backtracing will
312-
// assert symbol initialization succeeds and will clean up after the
313-
// backtrace is finished.
314-
DBGHELP.SymCleanup().unwrap()(GetCurrentProcess());
315-
DBGHELP.SymSetOptions().unwrap()(OPTS_ORIG);
316-
}
317-
}
274+
// Actually initialize symbols with MSVC. Note that this can fail, but we
275+
// ignore it. There's not a ton of prior art for this per se, but LLVM
276+
// internally seems to ignore the return value here and one of the
277+
// sanitizer libraries in LLVM prints a scary warning if this fails but
278+
// basically ignores it in the long run.
279+
//
280+
// One case this comes up a lot for Rust is that the standard library and
281+
// this crate on crates.io both want to compete for `SymInitializeW`. The
282+
// standard library historically wanted to initialize then cleanup most of
283+
// the time, but now that it's using this crate it means that someone will
284+
// get to initialization first and the other will pick up that
285+
// initialization.
286+
DBGHELP.SymInitializeW().unwrap()(GetCurrentProcess(), ptr::null_mut(), TRUE);
287+
Ok(Init)
318288
}

src/symbolize/dbghelp.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ pub unsafe fn resolve(what: ResolveWhat, cb: &mut FnMut(&super::Symbol)) {
9898
}
9999

100100
unsafe fn resolve_with_inline(
101-
dbghelp: &dbghelp::Cleanup,
101+
dbghelp: &dbghelp::Init,
102102
frame: &STACKFRAME_EX,
103103
cb: &mut FnMut(&super::Symbol),
104104
) {
@@ -127,7 +127,7 @@ unsafe fn resolve_with_inline(
127127
}
128128

129129
unsafe fn resolve_without_inline(
130-
dbghelp: &dbghelp::Cleanup,
130+
dbghelp: &dbghelp::Init,
131131
addr: *mut c_void,
132132
cb: &mut FnMut(&super::Symbol),
133133
) {

0 commit comments

Comments
 (0)