You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
⚠️ AI-generated issue — requires human investigation before acting on it.
Summary
ReportBuilder::build_unresolved acquires a read lock (profiler.read()) while the signal handler acquires an exclusive write lock (PROFILER.try_write()). With spin::RwLock, a read lock prevents any writer from acquiring the lock. Therefore, every SIGPROF signal delivered while build_unresolved holds the read lock is silently dropped — the signal handler's try_write() fails and the sample is lost.
Location
src/report.rs, line 66: match self.profiler.read().as_ref()
src/profiler.rs, line 324: if let Some(mut guard) = PROFILER.try_write()
Impact
During the entire duration of build_unresolved — which involves disk reads (TempFdArray::try_iter re-opens and reads a temp file) and HashMap insertions — all CPU samples are dropped. At 99 Hz sampling, even a 10 ms report build window drops ~1 sample. At higher frequencies or with large overflow files, the impact is larger and proportional to report-build time.
By contrast, build (the resolved builder) uses a write lock, which has the same sample-loss effect but is at least consistent in its locking model.
Notes
This is partly a design trade-off: there is no way to read the Collector concurrently with the signal handler writing to it without some form of synchronisation.
The issue is that build_unresolved appears to be "lighter" (read-only semantics) but actually causes the same sample loss as the exclusive build, just less obviously.
At minimum this should be documented. A better solution might be double-buffering the collector so reports can be built on a snapshot.
Summary
ReportBuilder::build_unresolvedacquires a read lock (profiler.read()) while the signal handler acquires an exclusive write lock (PROFILER.try_write()). Withspin::RwLock, a read lock prevents any writer from acquiring the lock. Therefore, everySIGPROFsignal delivered whilebuild_unresolvedholds the read lock is silently dropped — the signal handler'stry_write()fails and the sample is lost.Location
src/report.rs, line 66:match self.profiler.read().as_ref()src/profiler.rs, line 324:if let Some(mut guard) = PROFILER.try_write()Impact
During the entire duration of
build_unresolved— which involves disk reads (TempFdArray::try_iterre-opens and reads a temp file) and HashMap insertions — all CPU samples are dropped. At 99 Hz sampling, even a 10 ms report build window drops ~1 sample. At higher frequencies or with large overflow files, the impact is larger and proportional to report-build time.By contrast,
build(the resolved builder) uses a write lock, which has the same sample-loss effect but is at least consistent in its locking model.Notes
Collectorconcurrently with the signal handler writing to it without some form of synchronisation.build_unresolvedappears to be "lighter" (read-only semantics) but actually causes the same sample loss as the exclusivebuild, just less obviously.