rust-lang/rust-analyzer · error

profiler already started

Error message

profiler already started

What it means

The google_cpu_profiler wrapper in the `profile` crate is single-instance: an atomic STATE machine enforces OFF -> PENDING -> ON transitions. `start` panics with 'profiler already started' if the profiler is not in the OFF state when starting, i.e. a previous session was never stopped.

Source

Thrown at crates/profile/src/google_cpu_profiler.rs:29

#[allow(non_snake_case)]
unsafe extern "C" {
    fn ProfilerStart(fname: *const c_char) -> i32;
    fn ProfilerStop();
}

const OFF: usize = 0;
const ON: usize = 1;
const PENDING: usize = 2;

fn transition(current: usize, new: usize) -> bool {
    static STATE: AtomicUsize = AtomicUsize::new(OFF);

    STATE.compare_exchange(current, new, Ordering::SeqCst, Ordering::SeqCst).is_ok()
}

pub(crate) fn start(path: &Path) {
    if !transition(OFF, PENDING) {
        panic!("profiler already started");
    }
    let path = CString::new(path.display().to_string()).unwrap();
    if unsafe { ProfilerStart(path.as_ptr()) } == 0 {
        panic!("profiler failed to start")
    }
    assert!(transition(PENDING, ON));
}

pub(crate) fn stop() {
    if !transition(ON, PENDING) {
        panic!("profiler is not started")
    }
    unsafe { ProfilerStop() };
    assert!(transition(PENDING, OFF));
}

View on GitHub (pinned to e8f7e90aa3)

Solutions

  1. Ensure every `start()` is paired with `stop()`, including on error paths (use a guard or defer pattern)
  2. Check current state before starting, or restructure to start profiling once and toggle collection instead
  3. Fix the exception/interrupt path that skipped the previous `stop()` call

Example fix

// before
profiler::start(&path);
run_work(); // early return skips stop
profiler::start(&path); // panics
// after
profiler::start(&path);
let result = run_work();
profiler::stop();
result
Defensive patterns

Strategy: try-catch

Validate before calling

// no public state accessor; guard at call site
static PROFILING: AtomicBool = AtomicBool::new(false);
fn can_start() -> bool { !PROFILING.load(Ordering::SeqCst) }

Try / catch

std::panic::catch_unwind(|| profiler::start(&path))
    .err()
    .map(|_| eprintln!("profiler already running; skipping start"));

Prevention

When it happens

Trigger: Calling `profile::start()` twice without an intervening `stop()`; a prior `stop()` failed or was skipped; concurrent startup from two threads (the compare_exchange ensures only one wins).

Common situations: Long-lived processes toggling CPU profiling where an early error path skipped `stop()`; tests or tools that start profiling in each iteration without cleanup; two profiling entry points racing.

Related errors


AI-assisted analysis of rust-lang/rust-analyzer@e8f7e90aa3 (2026-09-03). Data as JSON: /api/errors/4c20142988d62196. Report an issue: GitHub.