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>)
| 165 | /// * Needs to send batches of samples to the recording thread to get written to |
| 166 | /// the filesystem. |
| 167 | fn 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); |