Skip to content

Commit 3e9d8c7

Browse files
committed
Refactor dbghelp synchronization
1 parent 8f929c4 commit 3e9d8c7

File tree

5 files changed

+83
-72
lines changed

5 files changed

+83
-72
lines changed

Cargo.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,14 +67,14 @@ std = []
6767
# - unix-backtrace: this uses the backtrace(3) function to acquire a
6868
# backtrace, but is not as reliable as libunwind. It is, however,
6969
# generally found in more locations.
70-
# - dbghelp: on windows this enables usage of dbghelp.dll to find a
71-
# backtrace at runtime
70+
# - dbghelp: on windows this enables usage of dbghelp.dll to acquire and
71+
# resolve a backtrace at runtime
7272
# - kernel32: on windows this enables using RtlCaptureStackBackTrace as the
7373
# function to acquire a backtrace
7474
libunwind = []
7575
unix-backtrace = []
76-
dbghelp = ["std"]
77-
kernel32 = ["dbghelp"]
76+
dbghelp = []
77+
kernel32 = []
7878

7979
#=======================================
8080
# Methods of resolving symbols

ci/azure-test-all.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,5 +37,9 @@ steps:
3737
displayName: "Test backtrace (-default + gimli-symbolize + std)"
3838
- bash: cargo test --no-default-features --features 'dbghelp std'
3939
displayName: "Test backtrace (-default + dbghelp + std)"
40+
- bash: cargo test --no-default-features --features 'kernel32 std'
41+
displayName: "Test backtrace (-default + kernel32 + std)"
42+
- bash: cargo test --no-default-features --features 'kernel32 dbghelp std'
43+
displayName: "Test backtrace (-default + kernel32 + dbghelp + std)"
4044
- bash: cd ./cpp_smoke_test && cargo test
4145
displayName: "Test cpp_smoke_test"

src/backtrace/dbghelp.rs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ pub unsafe fn trace(cb: &mut FnMut(&super::Frame) -> bool) {
6262
};
6363
let image = init_frame(&mut frame.inner.inner, &context.0);
6464

65-
let cleanup_on_drop = CleanupOnDrop;
65+
let _cleanup_on_drop = CleanupOnDrop;
6666

6767
::TRACE_CLEANUP.with(|trace_cleanup| {
6868
let mut trace_cleanup = trace_cleanup.borrow_mut();
@@ -120,8 +120,6 @@ pub unsafe fn trace(cb: &mut FnMut(&super::Frame) -> bool) {
120120
break
121121
}
122122
}
123-
124-
drop(cleanup_on_drop);
125123
}
126124
}
127125

src/capture.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ impl Backtrace {
6868
/// ```
6969
#[inline(never)] // want to make sure there's a frame here to remove
7070
pub fn new() -> Backtrace {
71+
let _lock = ::lock::lock();
72+
7173
// initialize dbghelp only once for both the trace and the resolve
7274
#[cfg(all(windows, feature = "dbghelp"))]
7375
let _c = unsafe { ::dbghelp_init() };
@@ -145,6 +147,8 @@ impl Backtrace {
145147
/// If this backtrace has been previously resolved or was created through
146148
/// `new`, this function does nothing.
147149
pub fn resolve(&mut self) {
150+
let _lock = ::lock::lock();
151+
148152
// initialize dbghelp only once for all frames
149153
#[cfg(all(windows, feature = "dbghelp"))]
150154
let _c = unsafe { ::dbghelp_init() };

src/lib.rs

Lines changed: 70 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -180,29 +180,18 @@ mod lock {
180180
}
181181

182182
#[cfg(all(windows, feature = "dbghelp"))]
183-
struct Cleanup {
184-
handle: winapi::um::winnt::HANDLE,
185-
opts: winapi::shared::minwindef::DWORD,
186-
}
183+
mod dbghelp {
184+
use core::marker::PhantomData;
185+
use winapi::um::dbghelp::{SymInitializeW, SymCleanup};
187186

188-
#[cfg(all(windows, feature = "dbghelp"))]
189-
enum Trace {
190-
Inside(Option<Cleanup>),
191-
Outside,
192-
}
193-
194-
thread_local! {
195-
#[cfg(all(windows, feature = "dbghelp"))]
196-
static TRACE_CLEANUP: std::cell::RefCell<Trace> = std::cell::RefCell::new(Trace::Outside);
197-
}
187+
// Not sure why these are missing in winapi
198188

199-
#[cfg(all(windows, feature = "dbghelp"))]
200-
unsafe fn dbghelp_init() -> Option<Cleanup> {
201-
use winapi::shared::minwindef;
202-
use winapi::um::{dbghelp, processthreadsapi};
189+
const SYMOPT_DEFERRED_LOADS: winapi::shared::minwindef::DWORD = 0x00000004;
203190

204-
use std::sync::{Mutex, Once, ONCE_INIT};
205-
use std::boxed::Box;
191+
extern "system" {
192+
fn SymGetOptions() -> winapi::shared::minwindef::DWORD;
193+
fn SymSetOptions(options: winapi::shared::minwindef::DWORD);
194+
}
206195

207196
// Initializing symbols has significant overhead, but initializing only once
208197
// without cleanup causes problems for external sources. For example, the
@@ -214,68 +203,84 @@ unsafe fn dbghelp_init() -> Option<Cleanup> {
214203
// As a compromise, we'll keep track of the number of internal initialization
215204
// requests within a single API call in order to minimize the number of
216205
// init/cleanup cycles.
217-
static mut REF_COUNT: *mut Mutex<usize> = 0 as *mut _;
218-
static mut INIT: Once = ONCE_INIT;
206+
static mut DBGHELP: DbgHelp = DbgHelp {
207+
ref_count: 0,
208+
opts: 0,
209+
handle: core::ptr::null_mut()
210+
};
211+
212+
struct DbgHelp {
213+
ref_count: usize,
214+
handle: winapi::um::winnt::HANDLE,
215+
opts: winapi::shared::minwindef::DWORD,
216+
}
219217

220-
INIT.call_once(|| {
221-
REF_COUNT = Box::into_raw(Box::new(Mutex::new(0)));
222-
});
218+
pub struct Cleanup(PhantomData<*mut ()>);
223219

224-
// Not sure why these are missing in winapi
225-
const SYMOPT_DEFERRED_LOADS: minwindef::DWORD = 0x00000004;
226-
extern "system" {
227-
fn SymGetOptions() -> minwindef::DWORD;
228-
fn SymSetOptions(options: minwindef::DWORD);
220+
impl Clone for Cleanup {
221+
fn clone(&self) -> Cleanup {
222+
unsafe {
223+
DBGHELP.ref_count += 1;
224+
}
225+
Cleanup(PhantomData)
226+
}
229227
}
230228

231229
impl Drop for Cleanup {
232230
fn drop(&mut self) {
233231
unsafe {
234-
let mut ref_count_guard = (&*REF_COUNT).lock().unwrap();
235-
*ref_count_guard -= 1;
236-
237-
if *ref_count_guard == 0 {
238-
dbghelp::SymCleanup(self.handle);
239-
SymSetOptions(self.opts);
232+
DBGHELP.ref_count -= 1;
233+
if DBGHELP.ref_count == 0 {
234+
SymCleanup(DBGHELP.handle);
235+
SymSetOptions(DBGHELP.opts);
240236
}
241237
}
242238
}
243239
}
244240

245-
impl Clone for Cleanup {
246-
fn clone(&self) -> Cleanup {
247-
unsafe {
248-
let mut ref_count_guard = (&*REF_COUNT).lock().unwrap();
249-
*ref_count_guard += 1;
250-
}
251-
Cleanup {
252-
opts: self.opts,
253-
handle: self.handle
254-
}
255-
}
241+
/// Tracks whether or not we have initialized dbghelp.dll inside of a call to `trace`.
242+
pub enum Trace {
243+
Inside(Option<Cleanup>),
244+
Outside,
256245
}
257246

258-
let opts = SymGetOptions();
259-
let handle = processthreadsapi::GetCurrentProcess();
247+
thread_local! {
248+
pub static TRACE_CLEANUP: core::cell::RefCell<Trace> = core::cell::RefCell::new(Trace::Outside);
249+
}
260250

261-
let mut ref_count_guard = (&*REF_COUNT).lock().unwrap();
251+
/// Requires external synchronization
252+
pub unsafe fn init() -> Option<Cleanup> {
253+
use winapi::shared::minwindef::TRUE;
254+
use winapi::um::processthreadsapi::GetCurrentProcess;
262255

263-
if *ref_count_guard > 0 {
264-
*ref_count_guard += 1;
265-
return Some(Cleanup { handle, opts });
266-
}
256+
if DBGHELP.ref_count > 0 {
257+
DBGHELP.ref_count += 1;
258+
return Some(Cleanup(core::marker::PhantomData));
259+
}
260+
261+
let opts = SymGetOptions();
262+
let handle = GetCurrentProcess();
267263

268-
SymSetOptions(opts | SYMOPT_DEFERRED_LOADS);
264+
SymSetOptions(opts | SYMOPT_DEFERRED_LOADS);
269265

270-
let ret = dbghelp::SymInitializeW(handle,
271-
0 as *mut _,
272-
minwindef::TRUE);
266+
let ret = SymInitializeW(handle, 0 as *mut _,TRUE);
267+
if ret != TRUE {
268+
// Revert our changes
269+
SymSetOptions(opts);
273270

274-
if ret != minwindef::TRUE {
275-
// Symbols may have been initialized by another library or an external debugger
276-
None
277-
} else {
278-
*ref_count_guard += 1;
279-
Some(Cleanup { handle, opts })
271+
// Symbols may have been initialized by another library or an external debugger
272+
None
273+
} else {
274+
DBGHELP.ref_count += 1;
275+
DBGHELP.handle = handle;
276+
DBGHELP.opts = opts;
277+
Some(Cleanup(PhantomData))
278+
}
280279
}
281-
}
280+
}
281+
282+
#[cfg(all(windows, feature = "dbghelp"))]
283+
use dbghelp::init as dbghelp_init;
284+
285+
#[cfg(all(windows, feature = "dbghelp"))]
286+
use dbghelp::{Trace, TRACE_CLEANUP};

0 commit comments

Comments
 (0)