Skip to content

Commit a7d3567

Browse files
committed
Refactor dbghelp synchronization
1 parent 18bac23 commit a7d3567

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
@@ -65,14 +65,14 @@ std = []
6565
# - unix-backtrace: this uses the backtrace(3) function to acquire a
6666
# backtrace, but is not as reliable as libunwind. It is, however,
6767
# generally found in more locations.
68-
# - dbghelp: on windows this enables usage of dbghelp.dll to find a
69-
# backtrace at runtime
68+
# - dbghelp: on windows this enables usage of dbghelp.dll to acquire and
69+
# resolve a backtrace at runtime
7070
# - kernel32: on windows this enables using RtlCaptureStackBackTrace as the
7171
# function to acquire a backtrace
7272
libunwind = []
7373
unix-backtrace = []
74-
dbghelp = ["std"]
75-
kernel32 = ["dbghelp"]
74+
dbghelp = []
75+
kernel32 = []
7676

7777
#=======================================
7878
# 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
@@ -178,29 +178,18 @@ mod lock {
178178
}
179179

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

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

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

202-
use std::sync::{Mutex, Once, ONCE_INIT};
203-
use std::boxed::Box;
189+
extern "system" {
190+
fn SymGetOptions() -> winapi::shared::minwindef::DWORD;
191+
fn SymSetOptions(options: winapi::shared::minwindef::DWORD);
192+
}
204193

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

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

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

229227
impl Drop for Cleanup {
230228
fn drop(&mut self) {
231229
unsafe {
232-
let mut ref_count_guard = (&*REF_COUNT).lock().unwrap();
233-
*ref_count_guard -= 1;
234-
235-
if *ref_count_guard == 0 {
236-
dbghelp::SymCleanup(self.handle);
237-
SymSetOptions(self.opts);
230+
DBGHELP.ref_count -= 1;
231+
if DBGHELP.ref_count == 0 {
232+
SymCleanup(DBGHELP.handle);
233+
SymSetOptions(DBGHELP.opts);
238234
}
239235
}
240236
}
241237
}
242238

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

256-
let opts = SymGetOptions();
257-
let handle = processthreadsapi::GetCurrentProcess();
245+
thread_local! {
246+
pub static TRACE_CLEANUP: core::cell::RefCell<Trace> = core::cell::RefCell::new(Trace::Outside);
247+
}
258248

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

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

266-
SymSetOptions(opts | SYMOPT_DEFERRED_LOADS);
262+
SymSetOptions(opts | SYMOPT_DEFERRED_LOADS);
267263

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

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

0 commit comments

Comments
 (0)