MCPcopy Create free account
hub / github.com/bytecodealliance/wasmtime / sampling_thread

Function sampling_thread

crates/wasmtime/src/profiling_agent/pulley.rs:167–234  ·  view source on GitHub ↗

Execution of the thread responsible for sampling interpreters. This thread has a few tasks: Needs to sample, at `state.sampling_freq`, the state of all known interpreters. Ideally this sampling is as steady as possible. Needs to clean up interpreters which have been destroyed as there's otherwise no hook for doing so. Needs to send batches of samples to the recording thread to get written to the

(state: &State, to_record: mpsc::Sender<Samples>)

Source from the content-addressed store, hash-verified

165/// * Needs to send batches of samples to the recording thread to get written to
166/// the filesystem.
167fn sampling_thread(state: &State, to_record: mpsc::Sender<Samples>) {
168 // Calculate the `Duration` between each sample which will be in
169 // nanoseconds. This duration is then used to create an `Instant` in time
170 // where we'll be collecting the next sample.
171 let between_ticks = Duration::new(0, 1_000_000_000 / state.sampling_freq);
172 let start = Instant::now();
173 let mut next_sample = start + between_ticks;
174
175 // Helper closure to send off a batch of samples to the recording thread.
176 // Note that recording is done off-thread to ensure that the filesystem I/O
177 // interferes as little as possible with the sampling rate here.
178 let record = |sampling: &mut SamplingState| {
179 if sampling.samples.num_samples() == 0 {
180 return;
181 }
182 let samples = mem::take(&mut sampling.samples);
183 to_record.send(samples).unwrap();
184 };
185
186 let mut sampling = state.sampling.lock().unwrap();
187
188 loop {
189 // Calculate the duration, from this current moment in time, to when the
190 // next sample is supposed to be taken. If the next sampling time is in
191 // the past then this won't sleep but will still check the condvar.
192 let dur = next_sample
193 .checked_duration_since(Instant::now())
194 .unwrap_or(Duration::new(0, 0));
195
196 // Wait on `state.sampling_done`, but with the timeout we've calculated.
197 // If this times out that means that the next sample can proceed.
198 // Otherwise if this did not time out then it means that sampling should
199 // cease as the profiler is being destroyed.
200 let (guard, result) = state.sampling_done.wait_timeout(sampling, dur).unwrap();
201 sampling = guard;
202 if !result.timed_out() {
203 break;
204 }
205
206 // Now that we've decided to take a sample increment the next sample
207 // time by our interval. Once we're done sampling below we'll then sleep
208 // again up to this time.
209 next_sample += between_ticks;
210
211 // Sample the state of all interpreters known. This first starts by
212 // discarding any interpreters that are offline. Samples without a PC
213 // are additionally discarded as it means the interpreter is inactive.
214 //
215 // Once enough samples have been collected they're flushed to the
216 // recording thread.
217 let SamplingState {
218 interpreters,
219 samples,
220 } = &mut *sampling;
221 interpreters.retain(|a| !a.is_done());
222 for interpreter in interpreters.iter() {
223 if let Some(pc) = interpreter.get() {
224 samples.append(pc);

Callers 1

newFunction · 0.85

Calls 10

num_samplesMethod · 0.80
is_doneMethod · 0.80
newFunction · 0.70
unwrapMethod · 0.45
sendMethod · 0.45
lockMethod · 0.45
retainMethod · 0.45
iterMethod · 0.45
getMethod · 0.45
appendMethod · 0.45

Tested by

no test coverage detected