1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
//! Per-sub-phase JSONL trace inside the embedded compile path.
//!
//! Diagnostic-only — written when the `ZCCACHE_INNER_TRACE` env var
//! points at a writable file path. Off by default (one atomic `OnceLock`
//! load per call, no allocation when disabled). Records elapsed micros
//! per named sub-phase plus optional byte counters, one JSON object per
//! line:
//!
//! ```jsonl
//! {"ts_ns":<u128>, "phase":"<name>", "micros":<u64>, "compile_id":"<str>"}
//! ```
//!
//! ## Why this exists
//!
//! The soldr-side per-phase JSONL trace
//! (`crates/soldr-cli/src/daemon/compile_trace.rs` in zackees/soldr,
//! soldr#985) revealed that **99.7% of cold-build dispatch time on a
//! medium-fixture cargo build sits inside `ZccacheService::compile`**
//! as a single opaque async call. The wire-side IPC framing (stdout
//! chunk loop, stderr chunk loop, terminal frame) totals 0.04% of
//! dispatch budget — under 40 ms across 146 compiles.
//!
//! That number falsified the prior "buffer-elimination" optimization
//! plan in zccache#939 (two attempts moved cold by zero seconds). The
//! actually-load-bearing work — input hashing, cache lookup, the rustc
//! subprocess, pipe drains, the cache-miss store — all sit inside
//! the embedded compile pipeline where soldr can't see them.
//!
//! This module is the diagnostic layer that lets us see them.
//!
//! ## Wire format and ABI compatibility with soldr's trace
//!
//! The JSONL shape is byte-for-byte the same as soldr's daemon trace.
//! soldr's `bench/parse_compile_trace.py` reads either file with no
//! changes — it buckets by the `phase` field. Hosts that point
//! `ZCCACHE_INNER_TRACE` and `SOLDR_DAEMON_TRACE` at *the same path*
//! get a unified per-compile timeline; pointing them at different
//! paths keeps the two layers separate.
//!
//! ## Hot-path cost
//!
//! - **Off** (env var unset): one [`std::sync::OnceLock::get_or_init`] miss
//! resolves to `None`; subsequent calls hit the `None` arm and
//! return immediately. Below noise.
//! - **On**: one [`std::time::Instant::elapsed`], one [`format!`], one mutex-guarded
//! [`std::io::Write::write_all`]. The mutex contention is bounded by the
//! number of sub-phase records per compile (~6 today) times the
//! embedded daemon's compile concurrency — fine for diagnostic use,
//! not enabled in production.
//!
//! Errors during open + write are silently dropped. The trace site
//! **must never** block, fail, or perturb the compile it's measuring.
use Write;
use PathBuf;
use ;
use ;
static TRACE_FILE: = new;
/// Name of the env var that, when set to a writable file path, enables
/// the JSONL trace. Unset = trace is off.
pub const ENV_VAR: &str = "ZCCACHE_INNER_TRACE";
/// Append a single phase-record line to the trace file. No-op when
/// [`ENV_VAR`] is unset. Errors silently dropped — the trace file is
/// diagnostic-only and must never block compile work.
///
/// `phase` is a short stable name (snake_case is conventional);
/// `micros` is the elapsed wall time in microseconds; `compile_id` is
/// the per-compile identifier the caller already tracks for audit
/// correlation (the embedded API surfaces this through
/// [`crate::audit::AuditId`] / `CompileResponse::compile_id`).
/// RAII guard that records on drop. Use for scope-bounded sub-phases:
///
/// ```ignore
/// {
/// let _p = Phase::start("cache_lookup", &compile_id);
/// // … work …
/// } // recorded here
/// ```