iai_callgrind/bin_bench.rs
1use std::ffi::{OsStr, OsString};
2use std::fmt::Display;
3use std::fs::File;
4use std::io::{BufRead, BufReader};
5use std::net::SocketAddr;
6use std::path::{Path, PathBuf};
7use std::time::Duration;
8
9use derive_more::AsRef;
10use iai_callgrind_macros::IntoInner;
11use iai_callgrind_runner::api::RawArgs;
12
13use crate::{internal, DelayKind, Stdin, Stdio};
14
15/// [low level api](`crate::binary_benchmark_group`) only: Create a new benchmark id
16#[derive(Debug, Clone, PartialEq, Eq, Hash)]
17pub struct BenchmarkId(String);
18
19/// The configuration of a binary benchmark
20///
21/// The [`BinaryBenchmarkConfig`] can be specified at multiple levels and configures the benchmarks
22/// at this level. For example a [`BinaryBenchmarkConfig`] at (`main`)[`crate::main`] level
23/// configures all benchmarks. A configuration at [`group`](crate::binary_benchmark_group) level
24/// configures all benchmarks in this group inheriting the configuration of the `main` level and if
25/// not specified otherwise overwrites the values of the `main` configuration if the option is
26/// specified in both [`BinaryBenchmarkConfig`]s. The deeper levels are the
27/// (`#[binary_benchmark] attribute`)[`crate::binary_benchmark`], then `#[bench]` and the
28/// `#[benches]` attribute.
29///
30/// # Examples
31///
32/// ```rust
33/// # use iai_callgrind::binary_benchmark_group;
34/// # binary_benchmark_group!(name = some_group; benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
35/// use iai_callgrind::{BinaryBenchmarkConfig, main};
36///
37/// main!(
38/// config = BinaryBenchmarkConfig::default().callgrind_args(["toggle-collect=something"]);
39/// binary_benchmark_groups = some_group
40/// );
41/// ```
42#[derive(Debug, Default, Clone, IntoInner, AsRef)]
43pub struct BinaryBenchmarkConfig(internal::InternalBinaryBenchmarkConfig);
44
45/// [low level api](`crate::binary_benchmark_group`) only: The top level struct to add binary
46/// benchmarks to
47///
48/// This struct doesn't need to be instantiated by yourself. It is passed as mutable reference to
49/// the expression in `benchmarks`.
50///
51/// ```rust
52/// use iai_callgrind::binary_benchmark_group;
53///
54/// binary_benchmark_group!(
55/// name = my_group;
56/// benchmarks = |_group: &mut BinaryBenchmarkGroup| {
57/// // Access the BinaryBenchmarkGroup with the identifier `group` to add benchmarks to the
58/// // group.
59/// //
60/// // group.binary_benchmark(/* BinaryBenchmark::new(...) */);
61/// }
62/// );
63/// ```
64#[derive(Debug, Default, PartialEq, Clone)]
65pub struct BinaryBenchmarkGroup {
66 /// All [`BinaryBenchmark`]s
67 pub binary_benchmarks: Vec<BinaryBenchmark>,
68}
69
70/// [low level api](`crate::binary_benchmark_group`) only: This struct mirrors the `#[bench]` and
71/// `#[benches]` attribute of a [`crate::binary_benchmark`]
72#[derive(Debug, Clone, PartialEq)]
73pub struct Bench {
74 /// The [`BenchmarkId`] used to uniquely identify this benchmark within a [`BinaryBenchmark`]
75 pub id: BenchmarkId,
76 /// All [`Command`]s
77 pub commands: Vec<Command>,
78 /// An optional [`BinaryBenchmarkConfig`]
79 ///
80 /// This field stores the internal representation of the [`BinaryBenchmarkConfig`]. Use
81 /// `BinaryBenchmarkConfig::into` to generate the internal configuration from a
82 /// [`BinaryBenchmarkConfig`]
83 pub config: Option<internal::InternalBinaryBenchmarkConfig>,
84 /// The `setup` function to be executed before the [`Command`] is executed
85 pub setup: Option<fn()>,
86 /// The `teardown` function to be executed after the [`Command`] is executed
87 pub teardown: Option<fn()>,
88}
89
90/// [low level api](`crate::binary_benchmark_group`) only: Mirror the [`crate::binary_benchmark`]
91/// attribute
92///
93/// A `BinaryBenchmark` can be created in two ways. Either with [`BinaryBenchmark::new`]. Or via the
94/// [`crate::binary_benchmark_attribute`] macro used with a function annotated with the
95/// [`crate::binary_benchmark`] attribute. So, you can start with the high-level api using the
96/// attribute and then go on in the low-level api.
97///
98/// # Examples
99///
100/// For examples using [`BinaryBenchmark::new`], see there. Here's an example using the
101/// [`crate::binary_benchmark_attribute`]
102///
103/// ```rust
104/// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
105/// use iai_callgrind::{
106/// binary_benchmark, binary_benchmark_group, binary_benchmark_attribute, Bench
107/// };
108///
109/// #[binary_benchmark]
110/// #[bench::foo("foo")]
111/// fn bench_binary(arg: &str) -> iai_callgrind::Command {
112/// iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-foo"))
113/// .arg(arg)
114/// .build()
115/// }
116///
117/// binary_benchmark_group!(
118/// name = my_group;
119/// benchmarks = |group: &mut BinaryBenchmarkGroup| {
120/// let mut binary_benchmark = binary_benchmark_attribute!(bench_binary);
121///
122/// // Continue and add another `Bench` to the `BinaryBenchmark`
123/// binary_benchmark.bench(Bench::new("bar")
124/// .command(iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-foo"))
125/// .arg("bar")
126/// )
127/// );
128///
129/// // Finally, add the `BinaryBenchmark` to the group
130/// group
131/// .binary_benchmark(binary_benchmark);
132/// }
133/// );
134/// # fn main() {}
135/// ```
136#[derive(Debug, Clone, PartialEq)]
137pub struct BinaryBenchmark {
138 /// An id which has to be unique within the same [`BinaryBenchmarkGroup`]
139 ///
140 /// In the high-level api this is the name of the function which is annotated by
141 /// [`crate::binary_benchmark`]
142 pub id: BenchmarkId,
143 /// An optional [`BinaryBenchmarkConfig`] which is applied to all [`Command`]s within this
144 /// [`BinaryBenchmark`]
145 pub config: Option<internal::InternalBinaryBenchmarkConfig>,
146 /// All [`Bench`]es which were added to this [`BinaryBenchmark`]
147 pub benches: Vec<Bench>,
148 /// The default `setup` function for all [`Bench`]es within this [`BinaryBenchmark`]. It can be
149 /// overwritten in a [`Bench`]
150 pub setup: Option<fn()>,
151 /// The default `teardown` function for all [`Bench`]es within this [`BinaryBenchmark`]. It can
152 /// be overwritten in a [`Bench`]
153 pub teardown: Option<fn()>,
154}
155
156/// Provide the [`Command`] to be benchmarked
157///
158/// `Command` is a builder for the binary which is going to be benchmarked providing fine-grained
159/// control over how the `Command` for the valgrind benchmark should be executed.
160///
161/// The default configuration is created with [`Command::new`] providing a path to an executable.
162/// Adding a crate's binary is usually done with `env!("CARGO_BIN_EXE_<name>")` where `<name>` is
163/// the name of the binary. The builder methods allow the configuration to be changed prior to
164/// [`Command::build`]. The [`Command`] can be reused to build multiple processes.
165///
166/// # Examples
167///
168/// Suppose your crate's binary is called `my-echo`:
169///
170/// ```rust
171/// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
172/// use iai_callgrind::Command;
173/// let command = Command::new(env!("CARGO_BIN_EXE_my-echo"));
174/// ```
175///
176/// However, an iai-callgrind benchmark is not limited to a crate's binaries, it can be any
177/// executable in the `$PATH`, or an absolute path to a binary installed on your system. The
178/// following will create a `Command` for the system's `echo` from the `$PATH`:
179///
180/// ```rust
181/// use iai_callgrind::Command;
182/// let command = Command::new("echo");
183/// ```
184#[derive(Debug, Default, Clone, PartialEq, IntoInner, AsRef)]
185pub struct Command(internal::InternalCommand);
186
187/// Provide the [`crate::Delay`] to specify the event for [`crate::Command`] execution start.
188///
189/// The default configuration is created with [`Delay::new`] providing a [`crate::DelayKind`] to
190/// specify the event type and parameters for the `Delay`.
191///
192/// Additionally, the `Delay` can be created using `from*()` methods.
193/// - [`Delay::from(duration)`](Delay::from)
194/// - [`Delay::from_tcp_socket(addr)`](Delay::from_tcp_socket)
195/// - [`Delay::from_udp_request(addr, request)`](Delay::from_udp_request)
196/// - [`Delay::from_path(path)`](Delay::from_path)
197///
198/// # Examples
199///
200/// Suppose your command needs to start 60 seconds after the benchmark started:
201///
202/// ```rust
203/// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
204/// use std::time::Duration;
205///
206/// use iai_callgrind::{Command, Delay, DelayKind};
207///
208/// let command = Command::new(env!("CARGO_BIN_EXE_my-echo")).delay(Delay::new(
209/// DelayKind::DurationElapse(Duration::from_secs(60)),
210/// ));
211///
212/// let command_from =
213/// Command::new(env!("CARGO_BIN_EXE_my-echo")).delay(Delay::from(Duration::from_secs(60)));
214///
215/// let command_duration =
216/// Command::new(env!("CARGO_BIN_EXE_my-echo")).delay(Duration::from_secs(60));
217/// ```
218///
219/// However, an iai-callgrind [`Delay`] is not limited to a duration, it can be any
220/// path creation event, a successful TCP connect or as well a received UDP response.
221///
222/// ```rust
223/// use iai_callgrind::{Command, Delay, DelayKind};
224///
225/// let command = Command::new("echo").delay(Delay::new(DelayKind::PathExists(
226/// "/your/path/to/wait/for".into(),
227/// )));
228///
229/// let command_from = Command::new("echo").delay(Delay::from_path("/your/path/to/wait/for"));
230/// ```
231///
232/// ```rust
233/// use std::net::SocketAddr;
234/// use std::time::Duration;
235///
236/// use iai_callgrind::{Command, Delay, DelayKind};
237///
238/// let command = Command::new("echo").delay(
239/// Delay::new(DelayKind::TcpConnect(
240/// "127.0.0.1:31000".parse::<SocketAddr>().unwrap(),
241/// ))
242/// .timeout(Duration::from_secs(3))
243/// .poll(Duration::from_millis(50)),
244/// );
245///
246/// let command_from = Command::new("echo").delay(
247/// Delay::from_tcp_socket("127.0.0.1:31000".parse::<SocketAddr>().unwrap())
248/// .timeout(Duration::from_secs(3))
249/// .poll(Duration::from_millis(50)),
250/// );
251/// ```
252///
253/// ```rust
254/// use std::net::SocketAddr;
255/// use std::time::Duration;
256///
257/// use iai_callgrind::{Command, Delay, DelayKind};
258///
259/// let command = Command::new("echo").delay(
260/// Delay::new(DelayKind::UdpResponse(
261/// "127.0.0.1:34000".parse::<SocketAddr>().unwrap(),
262/// vec![1],
263/// ))
264/// .timeout(Duration::from_secs(3))
265/// .poll(Duration::from_millis(50)),
266/// );
267///
268/// let command_from = Command::new("echo").delay(
269/// Delay::from_udp_request("127.0.0.1:34000".parse::<SocketAddr>().unwrap(), vec![1])
270/// .timeout(Duration::from_secs(3))
271/// .poll(Duration::from_millis(50)),
272/// );
273/// ```
274#[derive(Debug, Default, Clone, PartialEq, IntoInner, AsRef)]
275pub struct Delay(internal::InternalDelay);
276
277/// Set the expected exit status of a binary benchmark
278///
279/// Per default, the benchmarked binary is expected to succeed, but if a benchmark is expected to
280/// fail, setting this option is required.
281///
282/// # Examples
283///
284/// ```rust
285/// # use iai_callgrind::{binary_benchmark_group};
286/// # binary_benchmark_group!(
287/// # name = my_group;
288/// # benchmarks = |group: &mut BinaryBenchmarkGroup| {});
289/// use iai_callgrind::{main, BinaryBenchmarkConfig, ExitWith};
290///
291/// # fn main() {
292/// main!(
293/// config = BinaryBenchmarkConfig::default().exit_with(ExitWith::Code(1));
294/// binary_benchmark_groups = my_group
295/// );
296/// # }
297/// ```
298#[derive(Debug, Clone, Copy)]
299pub enum ExitWith {
300 /// Exit with success is similar to `ExitCode(0)`
301 Success,
302 /// Exit with failure is similar to setting the `ExitCode` to something different from `0`
303 /// without having to rely on a specific exit code
304 Failure,
305 /// The exact `ExitCode` of the benchmark run
306 Code(i32),
307}
308
309/// The `Sandbox` in which the `setup`, `teardown` and the [`Command`] are run
310///
311/// The `Sandbox` is a temporary directory which is created before the execution of the
312/// [`setup`](`crate::binary_benchmark`) and deleted after the
313/// [`teardown`](`crate::binary_benchmark`). `setup`, the [`Command`] and `teardown` are executed
314/// inside this temporary directory.
315///
316/// # Background and reasons for using a `Sandbox`
317///
318/// A [`Sandbox`] can help mitigating differences in benchmark results on different machines. As
319/// long as `$TMP_DIR` is unset or set to `/tmp`, the temporary directory has a constant length on
320/// unix machines (except android which uses `/data/local/tmp`). The directory itself
321/// is created with a constant length but random name like `/tmp/.a23sr8fk`. It is not implausible
322/// that an executable has different event counts just because the directory it is executed in has a
323/// different length. For example, if a member of your project has set up the project in
324/// `/home/bob/workspace/our-project` running the benchmarks in this directory, and the ci runs the
325/// benchmarks in `/runner/our-project`, the event counts might differ. If possible, the benchmarks
326/// should be run in an as constant as possible environment. Clearing the environment variables is
327/// also such a counter-measure.
328///
329/// Other reasons for using a `Sandbox` are convenience, such as if you're creating files during
330/// `setup` and the [`Command`] run and don't want to delete all the files manually. Or, more
331/// importantly, if the [`Command`] is destructive and deletes files, it is usually safer to execute
332/// such a [`Command`] in a temporary directory where it cannot do any harm to your or others file
333/// systems during the benchmark runs.
334///
335/// # Sandbox cleanup
336///
337/// The changes the `setup` makes in this directory persist until the `teardown` has finished. So,
338/// the [`Command`] can for example pick up any files created by the `setup` method. If run in a
339/// `Sandbox`, the `teardown` usually doesn't have to delete any files, because the whole
340/// directory is deleted after its usage. There is an exception to the rule. If any of the files
341/// inside the directory is not removable, for example because the permissions of a file don't allow
342/// the file to be deleted, then the whole directory persists. You can use the `teardown` to reset
343/// all permission bits to be readable and writable, so the cleanup can succeed.
344///
345/// To simply copy fixtures or whole directories into the `Sandbox` use [`Sandbox::fixtures`].
346#[derive(Debug, Clone, IntoInner, AsRef)]
347pub struct Sandbox(internal::InternalSandbox);
348
349impl Bench {
350 /// Create a new `Bench` with a unique [`BenchmarkId`]
351 ///
352 /// If the provided [`BenchmarkId`] is invalid, `iai-callgrind` exits with an error.
353 ///
354 /// # Scope of uniqueness of the [`BenchmarkId`]
355 ///
356 /// The id needs to be unique within the same [`BinaryBenchmark`]
357 ///
358 /// # Examples
359 ///
360 /// The [`BenchmarkId`] can be created from any &str-like
361 ///
362 /// ```
363 /// use iai_callgrind::Bench;
364 ///
365 /// let bench = Bench::new("my_unique_id");
366 /// ```
367 ///
368 /// but you can also provide the [`BenchmarkId`]
369 ///
370 /// ```
371 /// use iai_callgrind::{Bench, BenchmarkId};
372 ///
373 /// let bench = Bench::new(BenchmarkId::new("my_unique_id"));
374 /// ```
375 pub fn new<T>(id: T) -> Self
376 where
377 T: Into<BenchmarkId>,
378 {
379 Self {
380 id: id.into(),
381 config: None,
382 commands: vec![],
383 setup: None,
384 teardown: None,
385 }
386 }
387
388 /// Add a [`BinaryBenchmarkConfig`] for this `Bench`
389 ///
390 /// A `Bench` without a `BinaryBenchmarkConfig` behaves like having specified the default
391 /// [`BinaryBenchmarkConfig`]. This [`BinaryBenchmarkConfig`] overwrites the values of a
392 /// [`BinaryBenchmarkConfig`] specified at a higher level. See there for more details.
393 ///
394 /// # Examples
395 ///
396 /// ```
397 /// use iai_callgrind::{Bench, BinaryBenchmarkConfig};
398 ///
399 /// let bench = Bench::new("some_id").config(BinaryBenchmarkConfig::default().env("FOO", "BAR"));
400 /// ```
401 pub fn config<T>(&mut self, config: T) -> &mut Self
402 where
403 T: Into<internal::InternalBinaryBenchmarkConfig>,
404 {
405 self.config = Some(config.into());
406 self
407 }
408
409 /// Add a [`Command`] to this `Bench`
410 ///
411 /// A `Bench` with multiple `Commands` behaves exactly as the
412 /// [`#[benches]`](crate::binary_benchmark) attribute
413 ///
414 /// # Errors
415 ///
416 /// It is an error if a `Bench` does not contain any `Commands`, so this method or
417 /// [`Bench::commands`] has to be called at least once.
418 ///
419 /// # Examples
420 ///
421 /// Suppose the crate's binary is called `my-echo`:
422 ///
423 /// ```rust
424 /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
425 /// use iai_callgrind::{Bench, Command};
426 ///
427 /// let bench = Bench::new("some_id")
428 /// .command(Command::new(env!("CARGO_BIN_EXE_my-echo")))
429 /// .clone();
430 ///
431 /// assert_eq!(bench.commands.len(), 1);
432 /// ```
433 pub fn command<T>(&mut self, command: T) -> &mut Self
434 where
435 T: Into<Command>,
436 {
437 self.commands.push(command.into());
438 self
439 }
440
441 /// Add multiple [`Command`]s to this `Bench`
442 ///
443 /// See also [`Bench::command`].
444 ///
445 /// # Examples
446 ///
447 /// Suppose the crate's binary is called `my-echo`
448 ///
449 /// ```rust
450 /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
451 /// use iai_callgrind::{Bench, Command};
452 ///
453 /// let mut command = Command::new(env!("CARGO_BIN_EXE_my-echo"));
454 ///
455 /// let echo_foo = command.clone().arg("foo").build();
456 /// let echo_bar = command.arg("bar").build();
457 ///
458 /// let mut bench = Bench::new("some_id");
459 /// bench.commands([echo_foo, echo_bar]).clone();
460 ///
461 /// assert_eq!(bench.commands.len(), 2);
462 /// ```
463 pub fn commands<I, T>(&mut self, commands: T) -> &mut Self
464 where
465 I: Into<Command>,
466 T: IntoIterator<Item = I>,
467 {
468 self.commands.extend(commands.into_iter().map(Into::into));
469 self
470 }
471
472 /// Add a `setup` function to be executed before the [`Command`] is executed
473 ///
474 /// This `setup` function overwrites the `setup` function of [`BinaryBenchmark`]. In the
475 /// presence of a [`Sandbox`], this function is executed in the sandbox.
476 ///
477 /// # Examples
478 ///
479 /// ```rust
480 /// use iai_callgrind::Bench;
481 ///
482 /// fn my_setup() {
483 /// println!("Place everything in this function you want to be executed prior to the Command");
484 /// }
485 ///
486 /// let mut bench = Bench::new("some_id");
487 /// bench.setup(my_setup);
488 ///
489 /// assert!(bench.setup.is_some())
490 /// ```
491 ///
492 /// Overwrite the setup function from a [`BinaryBenchmark`]
493 ///
494 /// ```rust
495 /// use iai_callgrind::{Bench, BinaryBenchmark};
496 /// fn binary_benchmark_setup() {
497 /// println!("setup in BinaryBenchmark")
498 /// }
499 ///
500 /// fn bench_setup() {
501 /// println!("setup in Bench")
502 /// }
503 ///
504 /// BinaryBenchmark::new("bench_binary")
505 /// .setup(binary_benchmark_setup)
506 /// .bench(Bench::new("some_id").setup(bench_setup));
507 /// ```
508 pub fn setup(&mut self, setup: fn()) -> &mut Self {
509 self.setup = Some(setup);
510 self
511 }
512
513 /// Add a `teardown` function to be executed after the [`Command`] is executed
514 ///
515 /// This `teardown` function overwrites the `teardown` function of [`BinaryBenchmark`]. In the
516 /// presence of a [`Sandbox`], this function is executed in the sandbox.
517 ///
518 /// # Examples
519 ///
520 /// ```rust
521 /// use iai_callgrind::Bench;
522 ///
523 /// fn my_teardown() {
524 /// println!(
525 /// "Place everything in this function you want to be executed after the execution of the \
526 /// Command"
527 /// );
528 /// }
529 ///
530 /// let mut bench = Bench::new("some_id");
531 /// bench.teardown(my_teardown);
532 ///
533 /// assert!(bench.teardown.is_some())
534 /// ```
535 ///
536 /// Overwrite the teardown function from a [`BinaryBenchmark`]
537 ///
538 /// ```rust
539 /// use iai_callgrind::{Bench, BinaryBenchmark};
540 /// fn binary_benchmark_teardown() {
541 /// println!("teardown in BinaryBenchmark")
542 /// }
543 ///
544 /// fn bench_teardown() {
545 /// println!("teardown in Bench")
546 /// }
547 ///
548 /// BinaryBenchmark::new("bench_binary")
549 /// .teardown(binary_benchmark_teardown)
550 /// .bench(Bench::new("some_id").teardown(bench_teardown));
551 /// ```
552 pub fn teardown(&mut self, teardown: fn()) -> &mut Self {
553 self.teardown = Some(teardown);
554 self
555 }
556
557 /// Add each line of a file as [`Command`] to this [`Bench`] using a `generator` function.
558 ///
559 /// This method mirrors the `file` parameter of the `#[benches]` attribute as far as possible.
560 /// In the low-level api you can achieve the same or more quickly yourself and this method
561 /// exists for the sake of completeness (and convenience).
562 ///
563 /// The file has to exist at the time you're using this method and the file has to be encoded in
564 /// UTF-8. The `generator` function tells us how to convert each line of the file into a
565 /// [`Command`].
566 ///
567 /// # Notable differences to high-level api
568 ///
569 /// If the file path in the high-level api is relative we interpret the path relative to the
570 /// workspace root (and make it absolute). In this method we use the path AS IS.
571 ///
572 /// # Errors
573 ///
574 /// If the file is empty, cannot be opened for reading or a line in the file cannot be converted
575 /// to a String. Also, the error from the `generator` is propagated. The errors containing the
576 /// line number use a 0-indexed line number.
577 ///
578 /// # Examples
579 ///
580 /// Suppose your cargo's binary is named `my-echo` and you want to convert a file with inputs
581 /// `benches/inputs` into commands and each line is the only argument for your `my-echo` binary:
582 ///
583 /// ```rust
584 /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
585 /// use iai_callgrind::{binary_benchmark_group, BinaryBenchmark, Bench};
586 ///
587 /// binary_benchmark_group!(
588 /// name = my_group;
589 /// benchmarks = |group: &mut BinaryBenchmarkGroup| {
590 /// group.binary_benchmark(BinaryBenchmark::new("some_id")
591 /// .bench(Bench::new("bench_id")
592 /// .file("benches/inputs", |line| {
593 /// Ok(iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-echo"))
594 /// .arg(line)
595 /// .build())
596 /// }).unwrap()
597 /// )
598 /// )
599 /// }
600 /// );
601 /// # fn main() {}
602 /// ```
603 pub fn file<T>(
604 &mut self,
605 path: T,
606 generator: fn(String) -> Result<Command, String>,
607 ) -> Result<&mut Self, String>
608 where
609 T: AsRef<Path>,
610 {
611 let path = path.as_ref();
612 let file = File::open(path).map_err(|error| {
613 format!(
614 "{}: Error opening file '{}': {error}",
615 self.id,
616 path.display(),
617 )
618 })?;
619
620 let reader = BufReader::new(file);
621 let mut has_lines = false;
622 for (index, line) in reader.lines().enumerate() {
623 has_lines = true;
624
625 let line = line.map_err(|error| {
626 format!(
627 "{}: Error reading line {index} in file '{}': {error}",
628 self.id,
629 path.display()
630 )
631 })?;
632
633 let command = generator(line).map_err(|error| {
634 format!(
635 "{}: Error generating command from line {index} in file '{}': {error}",
636 self.id,
637 path.display()
638 )
639 })?;
640 self.commands.push(command);
641 }
642
643 if !has_lines {
644 return Err(format!("{}: Empty file '{}'", self.id, path.display()));
645 }
646 Ok(self)
647 }
648}
649
650impl From<&mut Bench> for Bench {
651 fn from(value: &mut Bench) -> Self {
652 value.clone()
653 }
654}
655
656impl From<&Bench> for Bench {
657 fn from(value: &Bench) -> Self {
658 value.clone()
659 }
660}
661
662impl BenchmarkId {
663 /// Convenience method to create a `BenchmarkId` with a parameter in the [low level
664 /// api](crate::binary_benchmark_group)
665 ///
666 /// The `parameter` is simply appended to the `id` with an underscore, so
667 /// `BenchmarkId::with_parameter("some", 1)` is equivalent to `BenchmarkId::new("some_1")`
668 ///
669 /// # Examples
670 ///
671 /// ```
672 /// use iai_callgrind::BenchmarkId;
673 ///
674 /// let new_id = BenchmarkId::new("prefix_1");
675 /// let with_parameter = BenchmarkId::with_parameter("prefix", 1);
676 /// assert_eq!(new_id, with_parameter);
677 /// ```
678 ///
679 /// ```rust
680 /// # use iai_callgrind::main;
681 /// use iai_callgrind::{binary_benchmark_group,BenchmarkId, BinaryBenchmark, Bench, Command};
682 /// use std::ffi::OsStr;
683 ///
684 /// binary_benchmark_group!(
685 /// name = low_level_group;
686 /// benchmarks = |group: &mut BinaryBenchmarkGroup| {
687 /// let mut binary_benchmark = BinaryBenchmark::new("some_id");
688 /// for arg in 0..10 {
689 /// let id = BenchmarkId::with_parameter("prefix", arg);
690 /// binary_benchmark.bench(
691 /// Bench::new(id)
692 /// .command(
693 /// Command::new("echo").arg(arg.to_string()).build()
694 /// )
695 /// );
696 /// }
697 /// group.binary_benchmark(binary_benchmark);
698 /// }
699 /// );
700 /// # fn main() {
701 /// # main!(binary_benchmark_groups = low_level_group);
702 /// # }
703 /// ```
704 pub fn with_parameter<T, P>(id: T, parameter: P) -> Self
705 where
706 T: AsRef<str>,
707 P: Display,
708 {
709 Self(format!("{}_{parameter}", id.as_ref()))
710 }
711
712 /// Create a new `BenchmarkId`
713 ///
714 /// `BenchmarkId`s can be created from any string-like input. See [`BenchmarkId::validate`] for
715 /// ids which are considered valid.
716 ///
717 /// # Examples
718 ///
719 /// ```rust
720 /// use iai_callgrind::BenchmarkId;
721 ///
722 /// let id = BenchmarkId::new("my_id");
723 ///
724 /// assert!(id.validate().is_ok());
725 /// ```
726 pub fn new<T>(id: T) -> Self
727 where
728 T: Into<String>,
729 {
730 Self(id.into())
731 }
732
733 /// Returns ok if this [`BenchmarkId`] is valid
734 ///
735 /// An id should be short, descriptive besides being unique. The requirements for the uniqueness
736 /// differ for the structs where a `BenchmarkId` is used and is further described there.
737 ///
738 /// We use a minimal subset of rust's identifiers. A valid `BenchmarkId` starts with an ascii
739 /// alphabetic letter `[a-zA-Z]` or underscore `[_]`. All following characters can be an ascii
740 /// alphabetic letter, underscore or a digit `[0-9]`. At least one valid character must be
741 /// present.
742 ///
743 /// The `BenchmarkId` is used by `iai-callgrind` as file and directory name for the output files
744 /// of the benchmarks. Therefore, the limit for an id is chosen to be 120 bytes. This is a
745 /// calculation with some headroom. There are file systems which do not even allow 255 bytes. If
746 /// you're working on such a peculiar file system, you have to restrict your ids to even fewer
747 /// bytes which is `floor(MAX_LENGTH/2) - 1`. Leaving the maximum bytes aside, the best IDs are
748 /// simple, short and descriptive.
749 ///
750 /// Usually, it is not necessary to call this function, since we already check the validity of
751 /// benchmark ids prior to the execution of the benchmark runner. But if your ids come from an
752 /// untrusted source you can use this method for more immediate feedback.
753 ///
754 /// # Errors
755 ///
756 /// This function will return an error describing the source of the error if the id is invalid
757 ///
758 /// # Examples
759 ///
760 /// ```rust
761 /// use iai_callgrind::BenchmarkId;
762 ///
763 /// assert!(BenchmarkId::new("").validate().is_err());
764 /// assert!(BenchmarkId::new("0a").validate().is_err());
765 /// assert!(BenchmarkId::new("id with spaces").validate().is_err());
766 /// assert!(BenchmarkId::new("path/to").validate().is_err());
767 /// assert!(BenchmarkId::new("no::module::too").validate().is_err());
768 ///
769 /// assert!(BenchmarkId::new("_").validate().is_ok());
770 /// assert!(BenchmarkId::new("abc").validate().is_ok());
771 /// assert!(BenchmarkId::new("a9").validate().is_ok());
772 /// assert!(BenchmarkId::new("z_").validate().is_ok());
773 /// assert!(BenchmarkId::new("some_id").validate().is_ok());
774 /// ```
775 #[allow(clippy::missing_panics_doc)]
776 pub fn validate(&self) -> Result<(), String> {
777 const MAX_LENGTH_ID: usize = 255;
778 if self.0.is_empty() {
779 return Err("Invalid id: Cannot be empty".to_owned());
780 }
781
782 let mut bytes = self.0.bytes();
783 // This unwrap is safe, since we just checked that the string is not empty
784 let first = bytes.next().unwrap();
785
786 if first.is_ascii_alphabetic() || first == b'_' {
787 for (index, byte) in (1..).zip(bytes) {
788 if index > MAX_LENGTH_ID {
789 return Err(format!(
790 "Invalid id '{}': Maximum length of {MAX_LENGTH_ID} bytes reached",
791 &self.0,
792 ));
793 }
794 if byte.is_ascii() {
795 if !(byte.is_ascii_alphanumeric() || byte == b'_') {
796 return Err(format!(
797 "Invalid id '{}' at position {index}: Invalid character '{}'",
798 &self.0,
799 char::from(byte)
800 ));
801 }
802 } else {
803 return Err(format!(
804 "Invalid id '{}' at position {index}: Encountered non-ascii character",
805 &self.0
806 ));
807 }
808 }
809 } else if first.is_ascii() {
810 return Err(format!(
811 "Invalid id '{}': As first character is '{}' not allowed",
812 &self.0,
813 char::from(first)
814 ));
815 } else {
816 return Err(format!(
817 "Invalid id '{}': Encountered non-ascii character as first character",
818 &self.0
819 ));
820 }
821 Ok(())
822 }
823}
824
825impl Display for BenchmarkId {
826 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
827 f.write_str(&self.0)
828 }
829}
830
831impl From<BenchmarkId> for String {
832 fn from(value: BenchmarkId) -> Self {
833 value.0
834 }
835}
836
837impl<T> From<T> for BenchmarkId
838where
839 T: AsRef<str>,
840{
841 fn from(value: T) -> Self {
842 Self(value.as_ref().to_owned())
843 }
844}
845
846impl BinaryBenchmark {
847 /// Create a new `BinaryBenchmark`
848 ///
849 /// A `BinaryBenchmark` is the equivalent of the
850 /// [`#[binary_benchmark]`](`crate::binary_benchmark`) attribute in the low-level api and needs
851 /// a [`BenchmarkId`]. In the high-level api the id is derived from the function name.
852 ///
853 /// The [`BenchmarkId`] is used together with the id of each [`Bench`] to create a directory
854 /// name. This usually limits the combined length of the ids to `255 - 1` but can be less
855 /// depending on the file system. See [`BenchmarkId`] for more details
856 ///
857 /// # Scope of uniqueness of the [`BenchmarkId`]
858 ///
859 /// The id needs to be unique within the same [`crate::binary_benchmark_group`]. It is
860 /// recommended to use an id which is unique within the whole benchmark file, although doing
861 /// otherwise does currently not incur any negative consequence.
862 ///
863 /// # Examples
864 ///
865 /// ```rust
866 /// use iai_callgrind::{BenchmarkId, BinaryBenchmark};
867 ///
868 /// let binary_benchmark = BinaryBenchmark::new("some_id");
869 /// assert_eq!(binary_benchmark.id, BenchmarkId::new("some_id"));
870 /// ```
871 pub fn new<T>(id: T) -> Self
872 where
873 T: Into<BenchmarkId>,
874 {
875 Self {
876 id: id.into(),
877 config: None,
878 benches: vec![],
879 setup: None,
880 teardown: None,
881 }
882 }
883
884 /// Add a [`BinaryBenchmarkConfig`] to this `BinaryBenchmark`
885 ///
886 /// # Examples
887 ///
888 /// ```rust
889 /// use iai_callgrind::{BinaryBenchmark, BinaryBenchmarkConfig};
890 ///
891 /// let binary_benchmark = BinaryBenchmark::new("some_id")
892 /// .config(BinaryBenchmarkConfig::default().env("FOO", "BAR"))
893 /// .clone();
894 ///
895 /// assert_eq!(
896 /// binary_benchmark.config,
897 /// Some(BinaryBenchmarkConfig::default().env("FOO", "BAR").into())
898 /// );
899 /// ```
900 pub fn config<T>(&mut self, config: T) -> &mut Self
901 where
902 T: Into<internal::InternalBinaryBenchmarkConfig>,
903 {
904 self.config = Some(config.into());
905 self
906 }
907
908 /// Add a [`Bench`] to this `BinaryBenchmark`
909 ///
910 /// Adding a [`Bench`] which doesn't contain a [`Command`] is an error.
911 ///
912 /// # Examples
913 ///
914 /// ```rust
915 /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
916 /// use iai_callgrind::{Bench, BinaryBenchmark, Command};
917 ///
918 /// // Each `Bench` needs at least one `Command`!
919 /// let mut bench = Bench::new("some_id");
920 /// bench.command(Command::new(env!("CARGO_BIN_EXE_my-echo")));
921 ///
922 /// let binary_benchmark = BinaryBenchmark::new("bench_binary")
923 /// .bench(bench.clone())
924 /// .clone();
925 ///
926 /// assert_eq!(binary_benchmark.benches[0], bench);
927 /// ```
928 pub fn bench<T>(&mut self, bench: T) -> &mut Self
929 where
930 T: Into<Bench>,
931 {
932 self.benches.push(bench.into());
933 self
934 }
935
936 /// Add a `setup` function to this `BinaryBenchmark`
937 ///
938 /// This `setup` function is used in all [`Bench`]es of this `BinaryBenchmark` if not overridden
939 /// by the `Bench`.
940 ///
941 /// # Examples
942 ///
943 /// ```rust
944 /// use iai_callgrind::Bench;
945 /// fn my_setup() {
946 /// println!(
947 /// "Place everything in this function you want to be executed before the execution of \
948 /// the Command"
949 /// );
950 /// }
951 ///
952 /// let bench = Bench::new("some_id").setup(my_setup).clone();
953 ///
954 /// assert!(bench.setup.is_some())
955 /// ```
956 ///
957 /// Overwrite the setup function from this `BinaryBenchmark` in a [`Bench`]
958 ///
959 /// ```rust
960 /// use iai_callgrind::{Bench, BinaryBenchmark};
961 /// fn binary_benchmark_setup() {
962 /// println!("setup in BinaryBenchmark")
963 /// }
964 ///
965 /// fn bench_setup() {
966 /// println!("setup in Bench")
967 /// }
968 ///
969 /// BinaryBenchmark::new("bench_binary")
970 /// .setup(binary_benchmark_setup)
971 /// .bench(Bench::new("some_id").setup(bench_setup));
972 /// ```
973 pub fn setup(&mut self, setup: fn()) -> &mut Self {
974 self.setup = Some(setup);
975 self
976 }
977
978 /// Add a `teardown` function to this `BinaryBenchmark`
979 ///
980 /// This `teardown` function is used in all [`Bench`]es of this `BinaryBenchmark` if not
981 /// overridden by the `Bench`.
982 ///
983 /// # Examples
984 ///
985 /// ```rust
986 /// use iai_callgrind::Bench;
987 /// fn my_teardown() {
988 /// println!(
989 /// "Place everything in this function you want to be executed after the execution of the \
990 /// Command"
991 /// );
992 /// }
993 ///
994 /// let bench = Bench::new("some_id").teardown(my_teardown).clone();
995 ///
996 /// assert!(bench.teardown.is_some())
997 /// ```
998 ///
999 /// Overwrite the teardown function from this `BinaryBenchmark` in a [`Bench`]
1000 ///
1001 /// ```rust
1002 /// use iai_callgrind::{Bench, BinaryBenchmark};
1003 /// fn binary_benchmark_teardown() {
1004 /// println!("teardown in BinaryBenchmark")
1005 /// }
1006 ///
1007 /// fn bench_teardown() {
1008 /// println!("teardown in Bench")
1009 /// }
1010 ///
1011 /// BinaryBenchmark::new("bench_binary")
1012 /// .teardown(binary_benchmark_teardown)
1013 /// .bench(Bench::new("some_id").teardown(bench_teardown));
1014 /// ```
1015 pub fn teardown(&mut self, teardown: fn()) -> &mut Self {
1016 self.teardown = Some(teardown);
1017 self
1018 }
1019}
1020
1021impl From<&mut BinaryBenchmark> for BinaryBenchmark {
1022 fn from(value: &mut BinaryBenchmark) -> Self {
1023 value.clone()
1024 }
1025}
1026
1027impl From<&BinaryBenchmark> for BinaryBenchmark {
1028 fn from(value: &BinaryBenchmark) -> Self {
1029 value.clone()
1030 }
1031}
1032
1033impl BinaryBenchmarkConfig {
1034 /// Pass arguments to valgrind's callgrind
1035 ///
1036 /// It's not needed to pass the arguments with flags. Instead of `--collect-atstart=no` simply
1037 /// write `collect-atstart=no`.
1038 ///
1039 /// See also [Callgrind Command-line
1040 /// Options](https://valgrind.org/docs/manual/cl-manual.html#cl-manual.options) for a full
1041 /// overview of possible arguments.
1042 ///
1043 /// # Examples
1044 ///
1045 /// ```rust
1046 /// use iai_callgrind::BinaryBenchmarkConfig;
1047 ///
1048 /// BinaryBenchmarkConfig::default()
1049 /// .callgrind_args(["collect-atstart=no", "toggle-collect=some::path"]);
1050 /// ```
1051 #[deprecated = "Please use callgrind_args instead"]
1052 pub fn raw_callgrind_args<I, T>(&mut self, args: T) -> &mut Self
1053 where
1054 I: AsRef<str>,
1055 T: IntoIterator<Item = I>,
1056 {
1057 self.0.callgrind_args.extend_ignore_flag(args);
1058 self
1059 }
1060
1061 /// Create a new `BinaryBenchmarkConfig` with initial callgrind arguments
1062 ///
1063 /// See also [`BinaryBenchmarkConfig::callgrind_args`].
1064 ///
1065 /// # Examples
1066 ///
1067 /// ```rust
1068 /// # use iai_callgrind::{binary_benchmark, binary_benchmark_group};
1069 /// # #[binary_benchmark]
1070 /// # fn some_func() -> iai_callgrind::Command { iai_callgrind::Command::new("/some/path") }
1071 /// # binary_benchmark_group!(name = some_group; benchmarks = some_func);
1072 /// # fn main() {
1073 /// use iai_callgrind::{BinaryBenchmarkConfig, main};
1074 ///
1075 /// main!(
1076 /// config =
1077 /// BinaryBenchmarkConfig::with_callgrind_args(["toggle-collect=something"]);
1078 /// binary_benchmark_groups = some_group
1079 /// );
1080 /// # }
1081 /// ```
1082 pub fn with_callgrind_args<I, T>(args: T) -> Self
1083 where
1084 I: AsRef<str>,
1085 T: IntoIterator<Item = I>,
1086 {
1087 Self(internal::InternalBinaryBenchmarkConfig {
1088 callgrind_args: RawArgs::from_iter(args),
1089 ..Default::default()
1090 })
1091 }
1092
1093 /// Pass arguments to valgrind's callgrind
1094 ///
1095 /// It's not needed to pass the arguments with flags. Instead of `--collect-atstart=no` simply
1096 /// write `collect-atstart=no`.
1097 ///
1098 /// See also [Callgrind Command-line
1099 /// Options](https://valgrind.org/docs/manual/cl-manual.html#cl-manual.options) for a full
1100 /// overview of possible arguments.
1101 ///
1102 /// # Examples
1103 ///
1104 /// ```rust
1105 /// use iai_callgrind::BinaryBenchmarkConfig;
1106 ///
1107 /// BinaryBenchmarkConfig::default()
1108 /// .callgrind_args(["collect-atstart=no", "toggle-collect=some::path"]);
1109 /// ```
1110 pub fn callgrind_args<I, T>(&mut self, args: T) -> &mut Self
1111 where
1112 I: AsRef<str>,
1113 T: IntoIterator<Item = I>,
1114 {
1115 self.0.callgrind_args.extend_ignore_flag(args);
1116 self
1117 }
1118
1119 /// Pass valgrind arguments to all tools
1120 ///
1121 /// Only core [valgrind
1122 /// arguments](https://valgrind.org/docs/manual/manual-core.html#manual-core.options) are
1123 /// allowed.
1124 ///
1125 /// These arguments can be overwritten by tool specific arguments for example with
1126 /// [`BinaryBenchmarkConfig::callgrind_args`] or [`crate::Tool::args`].
1127 ///
1128 /// # Examples
1129 ///
1130 /// Specify `--trace-children=no` for all configured tools (including callgrind):
1131 ///
1132 /// ```rust
1133 /// # use iai_callgrind::{binary_benchmark_group};
1134 /// # binary_benchmark_group!(
1135 /// # name = my_group;
1136 /// # benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1137 /// use iai_callgrind::{main, BinaryBenchmarkConfig, Tool, ValgrindTool};
1138 ///
1139 /// # fn main() {
1140 /// main!(
1141 /// config = BinaryBenchmarkConfig::default()
1142 /// .valgrind_args(["--trace-children=no"])
1143 /// .tool(Tool::new(ValgrindTool::DHAT));
1144 /// binary_benchmark_groups = my_group
1145 /// );
1146 /// # }
1147 /// ```
1148 ///
1149 /// Overwrite the valgrind argument `--num-callers=25` for `DHAT` with `--num-callers=30`:
1150 ///
1151 /// ```rust
1152 /// # use iai_callgrind::{binary_benchmark_group};
1153 /// # binary_benchmark_group!(
1154 /// # name = my_group;
1155 /// # benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1156 /// use iai_callgrind::{main, BinaryBenchmarkConfig, Tool, ValgrindTool};
1157 ///
1158 /// # fn main() {
1159 /// main!(
1160 /// config = BinaryBenchmarkConfig::default()
1161 /// .valgrind_args(["--num-callers=25"])
1162 /// .tool(Tool::new(ValgrindTool::DHAT)
1163 /// .args(["--num-callers=30"])
1164 /// );
1165 /// binary_benchmark_groups = my_group
1166 /// );
1167 /// # }
1168 /// ```
1169 pub fn valgrind_args<I, T>(&mut self, args: T) -> &mut Self
1170 where
1171 I: AsRef<str>,
1172 T: IntoIterator<Item = I>,
1173 {
1174 self.0.valgrind_args.extend_ignore_flag(args);
1175 self
1176 }
1177
1178 /// Add an environment variable to the [`Command`]
1179 ///
1180 /// These environment variables are available independently of the setting of
1181 /// [`BinaryBenchmarkConfig::env_clear`].
1182 ///
1183 /// # Examples
1184 ///
1185 /// An example for a custom environment variable "FOO=BAR":
1186 ///
1187 /// ```rust
1188 /// # use iai_callgrind::{binary_benchmark_group};
1189 /// # binary_benchmark_group!(
1190 /// # name = my_group;
1191 /// # benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1192 /// use iai_callgrind::{main, BinaryBenchmarkConfig};
1193 ///
1194 /// # fn main() {
1195 /// main!(
1196 /// config = BinaryBenchmarkConfig::default().env("FOO", "BAR");
1197 /// binary_benchmark_groups = my_group
1198 /// );
1199 /// # }
1200 /// ```
1201 pub fn env<K, V>(&mut self, key: K, value: V) -> &mut Self
1202 where
1203 K: Into<OsString>,
1204 V: Into<OsString>,
1205 {
1206 self.0.envs.push((key.into(), Some(value.into())));
1207 self
1208 }
1209
1210 /// Add multiple environment variables to the [`Command`]
1211 ///
1212 /// # Examples
1213 ///
1214 /// ```rust
1215 /// # use iai_callgrind::{binary_benchmark_group};
1216 /// # binary_benchmark_group!(
1217 /// # name = my_group;
1218 /// # benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1219 /// use iai_callgrind::{main, BinaryBenchmarkConfig};
1220 ///
1221 /// # fn main() {
1222 /// main!(
1223 /// config = BinaryBenchmarkConfig::default().envs([("FOO", "BAR"),("BAR", "BAZ")]);
1224 /// binary_benchmark_groups = my_group
1225 /// );
1226 /// # }
1227 /// ```
1228 pub fn envs<K, V, T>(&mut self, envs: T) -> &mut Self
1229 where
1230 K: Into<OsString>,
1231 V: Into<OsString>,
1232 T: IntoIterator<Item = (K, V)>,
1233 {
1234 self.0
1235 .envs
1236 .extend(envs.into_iter().map(|(k, v)| (k.into(), Some(v.into()))));
1237 self
1238 }
1239
1240 /// Specify a pass-through environment variable
1241 ///
1242 /// Usually, the environment variables before running a binary benchmark are cleared
1243 /// but specifying pass-through variables makes this environment variable available to
1244 /// the benchmark as it actually appeared in the root environment.
1245 ///
1246 /// Pass-through environment variables are ignored if they don't exist in the root
1247 /// environment.
1248 ///
1249 /// # Examples
1250 ///
1251 /// Here, we chose to pass through the original value of the `HOME` variable:
1252 ///
1253 /// ```rust
1254 /// # use iai_callgrind::{binary_benchmark_group};
1255 /// # binary_benchmark_group!(
1256 /// # name = my_group;
1257 /// # benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1258 /// use iai_callgrind::{main, BinaryBenchmarkConfig};
1259 ///
1260 /// # fn main() {
1261 /// main!(
1262 /// config = BinaryBenchmarkConfig::default().pass_through_env("HOME");
1263 /// binary_benchmark_groups = my_group
1264 /// );
1265 /// # }
1266 /// ```
1267 pub fn pass_through_env<K>(&mut self, key: K) -> &mut Self
1268 where
1269 K: Into<OsString>,
1270 {
1271 self.0.envs.push((key.into(), None));
1272 self
1273 }
1274
1275 /// Specify multiple pass-through environment variables
1276 ///
1277 /// See also [`crate::BinaryBenchmarkConfig::pass_through_env`].
1278 ///
1279 /// # Examples
1280 ///
1281 /// ```rust
1282 /// # use iai_callgrind::binary_benchmark_group;
1283 /// # binary_benchmark_group!(
1284 /// # name = my_group;
1285 /// # benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1286 /// use iai_callgrind::{main, BinaryBenchmarkConfig};
1287 ///
1288 /// # fn main() {
1289 /// main!(
1290 /// config = BinaryBenchmarkConfig::default().pass_through_envs(["HOME", "USER"]);
1291 /// binary_benchmark_groups = my_group
1292 /// );
1293 /// # }
1294 /// ```
1295 pub fn pass_through_envs<K, T>(&mut self, envs: T) -> &mut Self
1296 where
1297 K: Into<OsString>,
1298 T: IntoIterator<Item = K>,
1299 {
1300 self.0
1301 .envs
1302 .extend(envs.into_iter().map(|k| (k.into(), None)));
1303 self
1304 }
1305
1306 /// If false, don't clear the environment variables before running the benchmark (Default: true)
1307 ///
1308 /// # Examples
1309 ///
1310 /// ```rust
1311 /// # use iai_callgrind::{binary_benchmark_group};
1312 /// # binary_benchmark_group!(
1313 /// # name = my_group;
1314 /// # benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1315 /// use iai_callgrind::{main, BinaryBenchmarkConfig};
1316 ///
1317 /// # fn main() {
1318 /// main!(
1319 /// config = BinaryBenchmarkConfig::default().env_clear(false);
1320 /// binary_benchmark_groups = my_group
1321 /// );
1322 /// # }
1323 /// ```
1324 pub fn env_clear(&mut self, value: bool) -> &mut Self {
1325 self.0.env_clear = Some(value);
1326 self
1327 }
1328
1329 /// Set the directory of the benchmarked binary (Default: Unchanged)
1330 ///
1331 /// Unchanged means, in the case of running with the sandbox enabled, the root of the sandbox.
1332 /// In the case of running without sandboxing enabled, this'll be the directory which `cargo
1333 /// bench` sets. If running the benchmark within the sandbox, and the path is relative then this
1334 /// new directory must be contained in the sandbox.
1335 ///
1336 /// # Examples
1337 ///
1338 /// ```rust
1339 /// # use iai_callgrind::{binary_benchmark_group};
1340 /// # binary_benchmark_group!(
1341 /// # name = my_group;
1342 /// # benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1343 /// use iai_callgrind::{main, BinaryBenchmarkConfig};
1344 ///
1345 /// # fn main() {
1346 /// main!(
1347 /// config = BinaryBenchmarkConfig::default().current_dir("/tmp");
1348 /// binary_benchmark_groups = my_group
1349 /// );
1350 /// # }
1351 /// ```
1352 ///
1353 /// and the following will change the current directory to `fixtures` assuming it is
1354 /// contained in the root of the sandbox
1355 ///
1356 /// ```rust
1357 /// # use iai_callgrind::{binary_benchmark_group};
1358 /// # binary_benchmark_group!(
1359 /// # name = my_group;
1360 /// # benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1361 /// use iai_callgrind::{main, BinaryBenchmarkConfig, Sandbox};
1362 ///
1363 /// # fn main() {
1364 /// main!(
1365 /// config = BinaryBenchmarkConfig::default()
1366 /// .sandbox(Sandbox::new(true))
1367 /// .current_dir("fixtures");
1368 /// binary_benchmark_groups = my_group
1369 /// );
1370 /// # }
1371 /// ```
1372 pub fn current_dir<T>(&mut self, value: T) -> &mut Self
1373 where
1374 T: Into<PathBuf>,
1375 {
1376 self.0.current_dir = Some(value.into());
1377 self
1378 }
1379
1380 /// Set the expected exit status [`ExitWith`] of a benchmarked binary
1381 ///
1382 /// Per default, the benchmarked binary is expected to succeed which is the equivalent of
1383 /// [`ExitWith::Success`]. But, if a benchmark is expected to fail, setting this option is
1384 /// required.
1385 ///
1386 /// # Examples
1387 ///
1388 /// If the benchmark is expected to fail with a specific exit code, for example `100`:
1389 ///
1390 /// ```rust
1391 /// # use iai_callgrind::{binary_benchmark_group};
1392 /// # binary_benchmark_group!(
1393 /// # name = my_group;
1394 /// # benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1395 /// use iai_callgrind::{main, BinaryBenchmarkConfig, ExitWith};
1396 ///
1397 /// # fn main() {
1398 /// main!(
1399 /// config = BinaryBenchmarkConfig::default().exit_with(ExitWith::Code(100));
1400 /// binary_benchmark_groups = my_group
1401 /// );
1402 /// # }
1403 /// ```
1404 ///
1405 /// If a benchmark is expected to fail, but the exit code doesn't matter:
1406 ///
1407 /// ```rust
1408 /// # use iai_callgrind::{binary_benchmark_group};
1409 /// # binary_benchmark_group!(
1410 /// # name = my_group;
1411 /// # benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1412 /// use iai_callgrind::{main, BinaryBenchmarkConfig, ExitWith};
1413 ///
1414 /// # fn main() {
1415 /// main!(
1416 /// config = BinaryBenchmarkConfig::default().exit_with(ExitWith::Failure);
1417 /// binary_benchmark_groups = my_group
1418 /// );
1419 /// # }
1420 /// ```
1421 pub fn exit_with<T>(&mut self, value: T) -> &mut Self
1422 where
1423 T: Into<internal::InternalExitWith>,
1424 {
1425 self.0.exit_with = Some(value.into());
1426 self
1427 }
1428
1429 /// Option to produce flamegraphs from callgrind output using the [`crate::FlamegraphConfig`]
1430 ///
1431 /// # Examples
1432 ///
1433 /// ```rust
1434 /// # use iai_callgrind::{binary_benchmark_group};
1435 /// # binary_benchmark_group!(
1436 /// # name = my_group;
1437 /// # benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1438 /// use iai_callgrind::{main, BinaryBenchmarkConfig, FlamegraphConfig };
1439 ///
1440 /// # fn main() {
1441 /// main!(
1442 /// config = BinaryBenchmarkConfig::default().flamegraph(FlamegraphConfig::default());
1443 /// binary_benchmark_groups = my_group
1444 /// );
1445 /// # }
1446 /// ```
1447 pub fn flamegraph<T>(&mut self, config: T) -> &mut Self
1448 where
1449 T: Into<internal::InternalFlamegraphConfig>,
1450 {
1451 self.0.flamegraph_config = Some(config.into());
1452 self
1453 }
1454
1455 /// Enable performance regression checks with a [`crate::RegressionConfig`]
1456 ///
1457 /// # Examples
1458 ///
1459 /// ```rust
1460 /// # use iai_callgrind::{binary_benchmark_group};
1461 /// # binary_benchmark_group!(
1462 /// # name = my_group;
1463 /// # benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1464 /// use iai_callgrind::{main, BinaryBenchmarkConfig, RegressionConfig};
1465 ///
1466 /// # fn main() {
1467 /// main!(
1468 /// config = BinaryBenchmarkConfig::default().regression(RegressionConfig::default());
1469 /// binary_benchmark_groups = my_group
1470 /// );
1471 /// # }
1472 /// ```
1473 pub fn regression<T>(&mut self, config: T) -> &mut Self
1474 where
1475 T: Into<internal::InternalRegressionConfig>,
1476 {
1477 self.0.regression_config = Some(config.into());
1478 self
1479 }
1480
1481 /// Add a configuration to run a valgrind [`crate::Tool`] in addition to callgrind
1482 ///
1483 /// # Examples
1484 ///
1485 /// ```rust
1486 /// # use iai_callgrind::{binary_benchmark_group};
1487 /// # binary_benchmark_group!(
1488 /// # name = my_group;
1489 /// # benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1490 /// use iai_callgrind::{main, BinaryBenchmarkConfig, Tool, ValgrindTool};
1491 ///
1492 /// # fn main() {
1493 /// main!(
1494 /// config = BinaryBenchmarkConfig::default()
1495 /// .tool(
1496 /// Tool::new(ValgrindTool::DHAT)
1497 /// );
1498 /// binary_benchmark_groups = my_group
1499 /// );
1500 /// # }
1501 /// ```
1502 pub fn tool<T>(&mut self, tool: T) -> &mut Self
1503 where
1504 T: Into<internal::InternalTool>,
1505 {
1506 self.0.tools.update(tool.into());
1507 self
1508 }
1509
1510 /// Add multiple configurations to run valgrind [`crate::Tool`]s in addition to callgrind
1511 ///
1512 /// # Examples
1513 ///
1514 /// ```rust
1515 /// # use iai_callgrind::{binary_benchmark_group};
1516 /// # binary_benchmark_group!(
1517 /// # name = my_group;
1518 /// # benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1519 /// use iai_callgrind::{main, BinaryBenchmarkConfig, Tool, ValgrindTool};
1520 ///
1521 /// # fn main() {
1522 /// main!(
1523 /// config = BinaryBenchmarkConfig::default()
1524 /// .tools(
1525 /// [
1526 /// Tool::new(ValgrindTool::DHAT),
1527 /// Tool::new(ValgrindTool::Massif)
1528 /// ]
1529 /// );
1530 /// binary_benchmark_groups = my_group
1531 /// );
1532 /// # }
1533 /// ```
1534 pub fn tools<I, T>(&mut self, tools: T) -> &mut Self
1535 where
1536 I: Into<internal::InternalTool>,
1537 T: IntoIterator<Item = I>,
1538 {
1539 self.0.tools.update_all(tools.into_iter().map(Into::into));
1540 self
1541 }
1542
1543 /// Override previously defined configurations of valgrind [`crate::Tool`]s
1544 ///
1545 /// See also [`crate::LibraryBenchmarkConfig::tool_override`] for more details.
1546 ///
1547 /// # Example
1548 ///
1549 /// The following will run `DHAT` and `Massif` (and the default callgrind) for all benchmarks in
1550 /// `main!` besides for `foo` which will just run `Memcheck` (and callgrind).
1551 ///
1552 /// ```rust
1553 /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
1554 /// use iai_callgrind::{
1555 /// binary_benchmark, binary_benchmark_group, BinaryBenchmarkConfig, main, Tool,
1556 /// ValgrindTool
1557 /// };
1558 ///
1559 /// #[binary_benchmark]
1560 /// #[bench::some(
1561 /// config = BinaryBenchmarkConfig::default()
1562 /// .tool_override(Tool::new(ValgrindTool::Memcheck))
1563 /// )]
1564 /// fn bench_binary() -> iai_callgrind::Command {
1565 /// iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-exe"))
1566 /// }
1567 ///
1568 /// binary_benchmark_group!(
1569 /// name = my_group;
1570 /// benchmarks = bench_binary
1571 /// );
1572 ///
1573 /// # fn main() {
1574 /// main!(
1575 /// config = BinaryBenchmarkConfig::default()
1576 /// .tools(
1577 /// [
1578 /// Tool::new(ValgrindTool::DHAT),
1579 /// Tool::new(ValgrindTool::Massif)
1580 /// ]
1581 /// );
1582 /// binary_benchmark_groups = my_group
1583 /// );
1584 /// # }
1585 /// ```
1586 pub fn tool_override<T>(&mut self, tool: T) -> &mut Self
1587 where
1588 T: Into<internal::InternalTool>,
1589 {
1590 self.0
1591 .tools_override
1592 .get_or_insert(internal::InternalTools::default())
1593 .update(tool.into());
1594 self
1595 }
1596
1597 /// Override previously defined configurations of valgrind [`crate::Tool`]s
1598 ///
1599 /// See also [`crate::LibraryBenchmarkConfig::tool_override`] for more details.
1600 ///
1601 /// # Example
1602 ///
1603 /// The following will run `DHAT` (and the default callgrind) for all benchmarks in
1604 /// `main!` besides for `foo` which will run `Massif` and `Memcheck` (and callgrind).
1605 ///
1606 /// ```rust
1607 /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
1608 /// use iai_callgrind::{
1609 /// binary_benchmark, binary_benchmark_group, BinaryBenchmarkConfig, main, Tool,
1610 /// ValgrindTool
1611 /// };
1612 ///
1613 /// #[binary_benchmark]
1614 /// #[bench::some(
1615 /// config = BinaryBenchmarkConfig::default()
1616 /// .tools_override([
1617 /// Tool::new(ValgrindTool::Massif),
1618 /// Tool::new(ValgrindTool::Memcheck),
1619 /// ])
1620 /// )]
1621 /// fn bench_binary() -> iai_callgrind::Command {
1622 /// iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-exe"))
1623 /// }
1624 ///
1625 /// binary_benchmark_group!(
1626 /// name = my_group;
1627 /// benchmarks = bench_binary
1628 /// );
1629 ///
1630 /// # fn main() {
1631 /// main!(
1632 /// config = BinaryBenchmarkConfig::default()
1633 /// .tool(
1634 /// Tool::new(ValgrindTool::DHAT),
1635 /// );
1636 /// binary_benchmark_groups = my_group
1637 /// );
1638 /// # }
1639 /// ```
1640 pub fn tools_override<I, T>(&mut self, tools: T) -> &mut Self
1641 where
1642 I: Into<internal::InternalTool>,
1643 T: IntoIterator<Item = I>,
1644 {
1645 self.0
1646 .tools_override
1647 .get_or_insert(internal::InternalTools::default())
1648 .update_all(tools.into_iter().map(Into::into));
1649 self
1650 }
1651
1652 /// Configure benchmarks to run in a [`Sandbox`] (Default: false)
1653 ///
1654 /// If specified, we create a temporary directory in which the `setup` and `teardown` functions
1655 /// of the `#[binary_benchmark]` (`#[bench]`, `#[benches]`) and the [`Command`] itself are run.
1656 ///
1657 /// A good reason for using a temporary directory as workspace is, that the length of the path
1658 /// where a benchmark is executed may have an influence on the benchmark results. For example,
1659 /// running the benchmark in your repository `/home/me/my/repository` and someone else's
1660 /// repository located under `/home/someone/else/repository` may produce different results only
1661 /// because the length of the first path is shorter. To run benchmarks as deterministic as
1662 /// possible across different systems, the length of the path should be the same wherever the
1663 /// benchmark is executed. This crate ensures this property by using the tempfile crate which
1664 /// creates the temporary directory in `/tmp` with a random name of fixed length like
1665 /// `/tmp/.tmp12345678`. This ensures that the length of the directory will be the same on all
1666 /// unix hosts where the benchmarks are run.
1667 ///
1668 /// # Examples
1669 ///
1670 /// ```rust
1671 /// # use iai_callgrind::{binary_benchmark_group};
1672 /// # binary_benchmark_group!(
1673 /// # name = my_group;
1674 /// # benchmarks = |_group: &mut BinaryBenchmarkGroup| {});
1675 /// use iai_callgrind::{main, BinaryBenchmarkConfig, Sandbox};
1676 ///
1677 /// # fn main() {
1678 /// main!(
1679 /// config = BinaryBenchmarkConfig::default().sandbox(Sandbox::new(true));
1680 /// binary_benchmark_groups = my_group
1681 /// );
1682 /// # }
1683 /// ```
1684 pub fn sandbox<T>(&mut self, sandbox: T) -> &mut Self
1685 where
1686 T: Into<internal::InternalSandbox>,
1687 {
1688 self.0.sandbox = Some(sandbox.into());
1689 self
1690 }
1691
1692 /// Configure the [`crate::OutputFormat`] of the terminal output of Iai-Callgrind
1693 ///
1694 /// # Examples
1695 ///
1696 /// ```rust
1697 /// use iai_callgrind::{main, BinaryBenchmarkConfig, OutputFormat};
1698 /// # use iai_callgrind::{binary_benchmark, binary_benchmark_group};
1699 /// # #[binary_benchmark]
1700 /// # fn some_func() -> iai_callgrind::Command { iai_callgrind::Command::new("some/path") }
1701 /// # binary_benchmark_group!(
1702 /// # name = some_group;
1703 /// # benchmarks = some_func
1704 /// # );
1705 /// # fn main() {
1706 /// main!(
1707 /// config = BinaryBenchmarkConfig::default()
1708 /// .output_format(OutputFormat::default()
1709 /// .truncate_description(Some(200))
1710 /// );
1711 /// binary_benchmark_groups = some_group
1712 /// );
1713 /// # }
1714 pub fn output_format<T>(&mut self, output_format: T) -> &mut Self
1715 where
1716 T: Into<internal::InternalOutputFormat>,
1717 {
1718 self.0.output_format = Some(output_format.into());
1719 self
1720 }
1721
1722 /// Execute the `setup` in parallel to the [`Command`].
1723 ///
1724 /// See also [`Command::setup_parallel`]
1725 ///
1726 /// # Examples
1727 ///
1728 /// ```rust
1729 /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
1730 /// use std::time::Duration;
1731 /// use std::net::{SocketAddr, TcpListener};
1732 /// use std::thread;
1733 /// use iai_callgrind::{
1734 /// binary_benchmark_group, binary_benchmark, main, BinaryBenchmarkConfig, Command,
1735 /// Delay, DelayKind
1736 /// };
1737 ///
1738 /// fn setup_tcp_server() {
1739 /// thread::sleep(Duration::from_millis(300));
1740 /// let _listener = TcpListener::bind("127.0.0.1:31000".parse::<SocketAddr>().unwrap()).unwrap();
1741 /// thread::sleep(Duration::from_secs(1));
1742 /// }
1743 ///
1744 /// #[binary_benchmark]
1745 /// #[bench::delay(
1746 /// setup = setup_tcp_server(),
1747 /// config = BinaryBenchmarkConfig::default()
1748 /// .setup_parallel(true)
1749 /// )]
1750 /// fn bench_binary() -> iai_callgrind::Command {
1751 /// Command::new(env!("CARGO_BIN_EXE_my-echo"))
1752 /// .delay(
1753 /// Delay::new(
1754 /// DelayKind::TcpConnect("127.0.0.1:31000".parse::<SocketAddr>().unwrap()))
1755 /// .timeout(Duration::from_millis(500))
1756 /// ).build()
1757 /// }
1758 ///
1759 /// binary_benchmark_group!(name = delay; benchmarks = bench_binary);
1760 /// # fn main() {
1761 /// main!(binary_benchmark_groups = delay);
1762 /// # }
1763 /// ```
1764 pub fn setup_parallel(&mut self, setup_parallel: bool) -> &mut Self {
1765 self.0.setup_parallel = Some(setup_parallel);
1766 self
1767 }
1768}
1769
1770impl BinaryBenchmarkGroup {
1771 /// Add a [`BinaryBenchmark`] to this group
1772 ///
1773 /// This can be a binary benchmark created with [`BinaryBenchmark::new`] or a
1774 /// [`crate::binary_benchmark`] attributed function addable with the
1775 /// [`crate::binary_benchmark_attribute`] macro.
1776 ///
1777 /// It is an error to add a [`BinaryBenchmark`] without having added a [`Bench`] to it.
1778 ///
1779 /// # Examples
1780 ///
1781 /// Add a [`BinaryBenchmark`] to this group
1782 ///
1783 /// ```rust
1784 /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
1785 /// use iai_callgrind::{binary_benchmark_group, BinaryBenchmark, Bench, BinaryBenchmarkGroup};
1786 ///
1787 /// fn setup_my_group(group: &mut BinaryBenchmarkGroup) {
1788 /// group.binary_benchmark(BinaryBenchmark::new("bench_binary")
1789 /// .bench(Bench::new("foo")
1790 /// .command(iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-foo"))
1791 /// .arg("foo")
1792 /// )
1793 /// )
1794 /// );
1795 /// }
1796 ///
1797 /// binary_benchmark_group!(
1798 /// name = my_group;
1799 /// benchmarks = |group: &mut BinaryBenchmarkGroup| setup_my_group(group)
1800 /// );
1801 /// # fn main() {}
1802 /// ```
1803 ///
1804 /// Or, add a `#[binary_benchmark]` annotated function to this group:
1805 ///
1806 /// ```rust
1807 /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
1808 /// use iai_callgrind::{binary_benchmark, binary_benchmark_group, binary_benchmark_attribute};
1809 ///
1810 /// #[binary_benchmark]
1811 /// #[bench::foo("foo")]
1812 /// fn bench_binary(arg: &str) -> iai_callgrind::Command {
1813 /// iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-foo"))
1814 /// .arg(arg)
1815 /// .build()
1816 /// }
1817 ///
1818 /// binary_benchmark_group!(
1819 /// name = my_group;
1820 /// benchmarks = |group: &mut BinaryBenchmarkGroup| {
1821 /// group
1822 /// .binary_benchmark(binary_benchmark_attribute!(bench_binary))
1823 /// }
1824 /// );
1825 /// # fn main() {}
1826 /// ```
1827 pub fn binary_benchmark<T>(&mut self, binary_benchmark: T) -> &mut Self
1828 where
1829 T: Into<BinaryBenchmark>,
1830 {
1831 self.binary_benchmarks.push(binary_benchmark.into());
1832 self
1833 }
1834
1835 /// Add multiple [`BinaryBenchmark`]s at once
1836 pub fn binary_benchmarks<I, T>(&mut self, binary_benchmarks: T) -> &mut Self
1837 where
1838 I: Into<BinaryBenchmark>,
1839 T: IntoIterator<Item = I>,
1840 {
1841 self.binary_benchmarks
1842 .extend(binary_benchmarks.into_iter().map(Into::into));
1843 self
1844 }
1845}
1846
1847impl Command {
1848 /// Create a new [`Command`] which is run under valgrind.
1849 ///
1850 /// Use
1851 /// [`env!("CARGO_BIN_EXE_<name>)`](https://doc.rust-lang.org/cargo/reference/environment-variables.html#environment-variables-cargo-sets-for-crates)
1852 /// to provide the path to an executable of your project instead of `target/release/<name>`.
1853 ///
1854 /// This `Command` is a builder for the binary which is going to be benchmarked but is not
1855 /// executed right away. We simply gather all the information to be finally able to execute the
1856 /// command under valgrind, later (after we collected all the commands in this benchmark file).
1857 /// As opposed to [`std::process::Command`], the build is finalized with [`Command::build`].
1858 ///
1859 /// # Relative paths
1860 ///
1861 /// Relative paths are interpreted relative to the current directory and if not running the
1862 /// benchmarks in a [`Sandbox`], depends on where `cargo bench` sets the current directory.
1863 /// Usually, it's best to use [`Path::canonicalize`] to resolve the relative path to a binary in
1864 /// your project's directory. In case you're running the benchmark in a [`Sandbox`], the path is
1865 /// interpreted relative to the root directory of the `Sandbox`. Iai-Callgrind tries to resolve
1866 /// simple names like `Command::new("echo")` searching the `$PATH`. To disambiguate between
1867 /// simple names and relative paths, use `./`. For example `echo` is searched in the `$PATH` and
1868 /// `./echo` is interpreted relative.
1869 ///
1870 /// # Examples
1871 ///
1872 /// Assume the project's binary or one of your project's binaries name is `my-echo`:
1873 ///
1874 /// ```
1875 /// # macro_rules! env { ($m:tt) => {{ "/home/my_project/target/release/my-echo" }} }
1876 /// use iai_callgrind::Command;
1877 ///
1878 /// let command = Command::new(env!("CARGO_BIN_EXE_my-echo"));
1879 /// ```
1880 ///
1881 /// or use your system's echo from the `$PATH` with
1882 ///
1883 /// ```
1884 /// use iai_callgrind::Command;
1885 ///
1886 /// let command = Command::new("echo").arg("foo").build();
1887 /// ```
1888 pub fn new<T>(path: T) -> Self
1889 where
1890 T: AsRef<OsStr>,
1891 {
1892 Self(internal::InternalCommand {
1893 path: PathBuf::from(path.as_ref()),
1894 ..Default::default()
1895 })
1896 }
1897
1898 /// Delay the execution of the [`Command`]
1899 ///
1900 /// This option allows to delay the [`Command`] execution till a certain event has happened.
1901 /// Supported events are:
1902 /// - Timer expired
1903 /// - File path exists
1904 /// - TCP/UDP connect succeeded
1905 ///
1906 /// [`Delay`] can be used in combination with [`Command::setup_parallel`] to wait for an event
1907 /// that is triggered within the `setup()` function. E.g. the setup starts a server that is
1908 /// needed by the [`Command`].
1909 ///
1910 /// # Examples
1911 ///
1912 /// ```rust
1913 /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
1914 /// use iai_callgrind::{binary_benchmark_group, binary_benchmark, main, Command, Delay, DelayKind};
1915 ///
1916 /// fn start_server() {
1917 /// // action to start the server, creates pid file
1918 /// std::fs::File::create("/tmp/my-server.pid").unwrap();
1919 /// }
1920 ///
1921 /// #[binary_benchmark]
1922 /// #[bench::server(setup = start_server)]
1923 /// fn bench_binary() -> Command {
1924 /// Command::new(env!("CARGO_BIN_EXE_my-echo"))
1925 /// .setup_parallel(true)
1926 /// .delay(Delay::new(DelayKind::PathExists("/tmp/my-server.pid".into())))
1927 /// .build()
1928 /// }
1929 ///
1930 /// binary_benchmark_group!(name = my_group; benchmarks = bench_binary);
1931 /// # fn main() {
1932 /// main!(binary_benchmark_groups = my_group);
1933 /// # }
1934 /// ```
1935 pub fn delay<T: Into<Delay>>(&mut self, delay: T) -> &mut Self {
1936 self.0.delay = Some(delay.into().into());
1937 self
1938 }
1939
1940 /// Execute the `setup()` in parallel to the [`Command`].
1941 ///
1942 /// This option changes the execution flow in a way that the [`Command`] is executed parallel
1943 /// to the `setup` instead of waiting for the `setup` to complete.
1944 ///
1945 /// This can be combined with the usage of [`Delay`] to further control the timing when the
1946 /// [`Command`] is executed.
1947 ///
1948 /// # Examples
1949 ///
1950 /// ```rust
1951 /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
1952 /// use std::time::Duration;
1953 /// use std::net::{SocketAddr, TcpListener};
1954 /// use std::thread;
1955 /// use iai_callgrind::{binary_benchmark_group, binary_benchmark, main, Command, Delay, DelayKind};
1956 ///
1957 /// fn setup_tcp_server() {
1958 /// thread::sleep(Duration::from_millis(300));
1959 /// let _listener = TcpListener::bind("127.0.0.1:31000".parse::<SocketAddr>().unwrap()).unwrap();
1960 /// thread::sleep(Duration::from_secs(1));
1961 /// }
1962 ///
1963 /// #[binary_benchmark]
1964 /// #[bench::delay(setup = setup_tcp_server())]
1965 /// fn bench_binary() -> iai_callgrind::Command {
1966 /// Command::new(env!("CARGO_BIN_EXE_my-echo"))
1967 /// .setup_parallel(true)
1968 /// .delay(
1969 /// Delay::new(
1970 /// DelayKind::TcpConnect("127.0.0.1:31000".parse::<SocketAddr>().unwrap()))
1971 /// .timeout(Duration::from_millis(500))
1972 /// ).build()
1973 /// }
1974 ///
1975 /// binary_benchmark_group!(name = delay; benchmarks = bench_binary);
1976 /// # fn main() {
1977 /// main!(binary_benchmark_groups = delay);
1978 /// # }
1979 /// ```
1980 pub fn setup_parallel(&mut self, setup_parallel: bool) -> &mut Self {
1981 self.0.config.setup_parallel = Some(setup_parallel);
1982 self
1983 }
1984
1985 /// Adds an argument to pass to the [`Command`]
1986 ///
1987 /// This option works exactly the same way as [`std::process::Command::arg`]. To pass multiple
1988 /// arguments see [`Command::args`].
1989 ///
1990 /// # Examples
1991 ///
1992 /// ```rust
1993 /// # use iai_callgrind::main;
1994 /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
1995 /// use iai_callgrind::{binary_benchmark_group, binary_benchmark};
1996 ///
1997 /// #[binary_benchmark]
1998 /// fn bench_binary() -> iai_callgrind::Command {
1999 /// iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-echo"))
2000 /// .arg("foo")
2001 /// .build()
2002 /// }
2003 ///
2004 /// binary_benchmark_group!(
2005 /// name = my_group;
2006 /// benchmarks = bench_binary
2007 /// );
2008 /// # fn main() {
2009 /// # main!(binary_benchmark_groups = my_group);
2010 /// # }
2011 /// ```
2012 pub fn arg<T>(&mut self, arg: T) -> &mut Self
2013 where
2014 T: Into<OsString>,
2015 {
2016 self.0.args.push(arg.into());
2017 self
2018 }
2019
2020 /// Adds multiple arguments to pass to the [`Command`]
2021 ///
2022 /// This option works exactly the same way as [`std::process::Command::args`].
2023 ///
2024 /// # Examples
2025 ///
2026 /// The following will execute `my-echo foo`.
2027 ///
2028 /// ```rust
2029 /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2030 /// # use iai_callgrind::main;
2031 /// use iai_callgrind::{binary_benchmark_group, binary_benchmark};
2032 ///
2033 /// #[binary_benchmark]
2034 /// fn bench_binary() -> iai_callgrind::Command {
2035 /// iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-echo"))
2036 /// .arg("foo")
2037 /// .build()
2038 /// }
2039 ///
2040 /// binary_benchmark_group!(
2041 /// name = my_group;
2042 /// benchmarks = bench_binary
2043 /// );
2044 /// # fn main() {
2045 /// # main!(binary_benchmark_groups = my_group);
2046 /// # }
2047 /// ```
2048 pub fn args<I, T>(&mut self, args: T) -> &mut Self
2049 where
2050 I: Into<OsString>,
2051 T: IntoIterator<Item = I>,
2052 {
2053 self.0.args.extend(args.into_iter().map(Into::into));
2054 self
2055 }
2056
2057 /// Configuration for the process's standard input ([`Stdin`])
2058 ///
2059 /// This method takes an [`Stdin`], [`Stdio`] and everything that implements `Into<Stdin>`. The
2060 /// [`Stdin`] enum mirrors most of the possibilities of [`std::process::Stdio`] but also some
2061 /// additional possibilities most notably [`Stdin::Setup`] (see there for more details).
2062 ///
2063 /// Per default, the stdin is not inherited from the parent and any attempt by the child process
2064 /// to read from the stdin stream will result in the stream immediately closing.
2065 ///
2066 /// The options you might be interested in the most are [`Stdin::File`], which mirrors the
2067 /// behaviour of [`std::process::Stdio`] if `Stdio` is a [`std::fs::File`], and
2068 /// [`Stdin::Setup`], which is special to `iai-callgrind` and lets you pipe the output of
2069 /// the `setup` function into the Stdin of this [`Command`]. If you need to delay the `Command`
2070 /// when using [`Stdin::Setup`], you can do so with [`Command::delay`].
2071 ///
2072 /// # Implementation details
2073 ///
2074 /// As the [`Command`] itself is not executed immediately, the [`std::process::Stdio`] is not
2075 /// created either. We only use the information from here to create the [`std::process::Stdio`]
2076 /// later after we collected all commands. Setting the Stdin to `Inherit` is discouraged and
2077 /// won't have the effect you might expect, since the benchmark runner (the parent) uses the
2078 /// Stdin for its own purposes and closes it before this [`Command`] is executed.
2079 ///
2080 /// # Examples
2081 ///
2082 /// Pipe the content of a file (`benches/fixture.txt`) into the stdin of this [`Command`]:
2083 ///
2084 /// ```rust
2085 /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2086 /// # use iai_callgrind::main;
2087 /// use iai_callgrind::{binary_benchmark_group, binary_benchmark, Stdio};
2088 ///
2089 /// #[binary_benchmark]
2090 /// fn bench_binary() -> iai_callgrind::Command {
2091 /// iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-exe"))
2092 /// .stdin(Stdio::File("benches/fixture.txt".into()))
2093 /// .build()
2094 /// }
2095 ///
2096 /// binary_benchmark_group!(
2097 /// name = my_group;
2098 /// benchmarks = bench_binary
2099 /// );
2100 /// # fn main() {
2101 /// # main!(binary_benchmark_groups = my_group);
2102 /// # }
2103 /// ```
2104 ///
2105 /// Pipe the Stdout of setup into the Stdin of this [`Command`]:
2106 ///
2107 /// ```rust
2108 /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2109 /// # use iai_callgrind::main;
2110 /// use iai_callgrind::{binary_benchmark_group, binary_benchmark, Stdin, Pipe};
2111 ///
2112 /// fn setup_pipe() {
2113 /// // All output to Stdout of this function will be piped into the Stdin of `my-exe`
2114 /// println!("This string will be piped into the stdin of my-exe");
2115 /// }
2116 ///
2117 /// #[binary_benchmark(setup = setup_pipe())]
2118 /// fn bench_binary() -> iai_callgrind::Command {
2119 /// iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-exe"))
2120 /// .stdin(Stdin::Setup(Pipe::Stdout))
2121 /// .build()
2122 /// }
2123 ///
2124 /// binary_benchmark_group!(
2125 /// name = my_group;
2126 /// benchmarks = bench_binary
2127 /// );
2128 /// # fn main() {
2129 /// # main!(binary_benchmark_groups = my_group);
2130 /// # }
2131 pub fn stdin<T>(&mut self, stdin: T) -> &mut Self
2132 where
2133 T: Into<Stdin>,
2134 {
2135 self.0.stdin = Some(stdin.into());
2136 self
2137 }
2138
2139 /// Configuration for the [`Command`]s standard output (Stdout) handle.
2140 ///
2141 /// The output of benchmark commands and functions are usually captured by the benchmark runner.
2142 /// This can be changed for example with the `--nocapture` option or here. Any option specified
2143 /// here takes precedence over the changes which `--nocapture` makes to the Stdout of the
2144 /// command.
2145 ///
2146 /// # Examples
2147 ///
2148 /// To see the output of this [`Command`] regardless of `--nocapture` in the benchmark output
2149 ///
2150 /// ```rust
2151 /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2152 /// # use iai_callgrind::main;
2153 /// use iai_callgrind::{binary_benchmark_group, binary_benchmark, Stdio};
2154 ///
2155 /// #[binary_benchmark]
2156 /// fn bench_binary() -> iai_callgrind::Command {
2157 /// iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-exe"))
2158 /// .stdout(Stdio::Inherit)
2159 /// .build()
2160 /// }
2161 ///
2162 /// binary_benchmark_group!(
2163 /// name = my_group;
2164 /// benchmarks = bench_binary
2165 /// );
2166 /// # fn main() {
2167 /// # main!(binary_benchmark_groups = my_group);
2168 /// # }
2169 /// ```
2170 ///
2171 /// To pipe the Stdout into a file `/tmp/benchmark.output`:
2172 ///
2173 /// ```rust
2174 /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2175 /// # use iai_callgrind::main;
2176 /// use iai_callgrind::{binary_benchmark_group, binary_benchmark, Stdio};
2177 /// use std::path::PathBuf;
2178 ///
2179 /// #[binary_benchmark]
2180 /// fn bench_binary() -> iai_callgrind::Command {
2181 /// iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-exe"))
2182 /// .stdout(Stdio::File("/tmp/benchmark.output".into()))
2183 /// // or
2184 /// .stdout(PathBuf::from("/tmp/benchmark.output"))
2185 /// .build()
2186 /// }
2187 ///
2188 /// binary_benchmark_group!(
2189 /// name = my_group;
2190 /// benchmarks = bench_binary
2191 /// );
2192 /// # fn main() {
2193 /// # main!(binary_benchmark_groups = my_group);
2194 /// # }
2195 /// ```
2196 pub fn stdout<T>(&mut self, stdio: T) -> &mut Self
2197 where
2198 T: Into<Stdio>,
2199 {
2200 self.0.stdout = Some(stdio.into());
2201 self
2202 }
2203
2204 /// Configuration for the [`Command`]s standard error output (Stderr) handle.
2205 ///
2206 /// This option is similar to [`Command::stdout`] but configures the Stderr. See there for more
2207 /// details.
2208 ///
2209 /// # Examples
2210 ///
2211 /// To see the error output of this [`Command`] regardless of `--nocapture` in the benchmark
2212 /// output
2213 ///
2214 /// ```rust
2215 /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2216 /// # use iai_callgrind::main;
2217 /// use iai_callgrind::{binary_benchmark_group, binary_benchmark, Stdio};
2218 ///
2219 /// #[binary_benchmark]
2220 /// fn bench_binary() -> iai_callgrind::Command {
2221 /// iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-exe"))
2222 /// .stderr(Stdio::Inherit)
2223 /// .build()
2224 /// }
2225 ///
2226 /// binary_benchmark_group!(
2227 /// name = my_group;
2228 /// benchmarks = bench_binary
2229 /// );
2230 /// # fn main() {
2231 /// # main!(binary_benchmark_groups = my_group);
2232 /// # }
2233 /// ```
2234 ///
2235 /// To pipe the Stderr into a file `/tmp/benchmark.output`:
2236 ///
2237 /// ```rust
2238 /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2239 /// # use iai_callgrind::main;
2240 /// use iai_callgrind::{binary_benchmark_group, binary_benchmark, Stdio};
2241 /// use std::path::PathBuf;
2242 ///
2243 /// #[binary_benchmark]
2244 /// fn bench_binary() -> iai_callgrind::Command {
2245 /// iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-exe"))
2246 /// .stderr(Stdio::File("/tmp/benchmark.output".into()))
2247 /// // or
2248 /// .stderr(PathBuf::from("/tmp/benchmark.output"))
2249 /// .build()
2250 /// }
2251 ///
2252 /// binary_benchmark_group!(
2253 /// name = my_group;
2254 /// benchmarks = bench_binary
2255 /// );
2256 /// # fn main() {
2257 /// # main!(binary_benchmark_groups = my_group);
2258 /// # }
2259 /// ```
2260 pub fn stderr<T>(&mut self, stdio: T) -> &mut Self
2261 where
2262 T: Into<Stdio>,
2263 {
2264 self.0.stderr = Some(stdio.into());
2265 self
2266 }
2267
2268 /// Add an environment variable available for this [`Command`]
2269 ///
2270 /// These environment variables are available independently of the setting of
2271 /// [`BinaryBenchmarkConfig::env_clear`] and additive to environment variables added with
2272 /// [`BinaryBenchmarkConfig::env`].
2273 ///
2274 /// # Examples
2275 ///
2276 /// An example for a custom environment variable "FOO=BAR":
2277 ///
2278 /// ```rust
2279 /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2280 /// # use iai_callgrind::main;
2281 /// use iai_callgrind::{binary_benchmark_group, binary_benchmark};
2282 ///
2283 /// #[binary_benchmark]
2284 /// fn bench_binary() -> iai_callgrind::Command {
2285 /// iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-exe"))
2286 /// .env("FOO", "BAR")
2287 /// .build()
2288 /// }
2289 ///
2290 /// binary_benchmark_group!(
2291 /// name = my_group;
2292 /// benchmarks = bench_binary
2293 /// );
2294 /// # fn main() {
2295 /// # main!(binary_benchmark_groups = my_group);
2296 /// # }
2297 pub fn env<K, V>(&mut self, key: K, value: V) -> &mut Self
2298 where
2299 K: Into<OsString>,
2300 V: Into<OsString>,
2301 {
2302 self.0.config.envs.push((key.into(), Some(value.into())));
2303 self
2304 }
2305
2306 /// Add multiple environment variables available for this [`Command`]
2307 ///
2308 /// See [`Command::env`] for more details.
2309 ///
2310 /// # Examples
2311 ///
2312 /// Add the custom environment variables "FOO=BAR" and `BAR=BAZ`:
2313 ///
2314 /// ```rust
2315 /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2316 /// # use iai_callgrind::main;
2317 /// use iai_callgrind::{binary_benchmark_group, binary_benchmark};
2318 ///
2319 /// #[binary_benchmark]
2320 /// fn bench_binary() -> iai_callgrind::Command {
2321 /// iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-exe"))
2322 /// .envs([("FOO", "BAR"), ("BAR", "BAZ")])
2323 /// .build()
2324 /// }
2325 ///
2326 /// binary_benchmark_group!(
2327 /// name = my_group;
2328 /// benchmarks = bench_binary
2329 /// );
2330 /// # fn main() {
2331 /// # main!(binary_benchmark_groups = my_group);
2332 /// # }
2333 pub fn envs<I, K, V>(&mut self, vars: I) -> &mut Self
2334 where
2335 I: IntoIterator<Item = (K, V)>,
2336 K: Into<OsString>,
2337 V: Into<OsString>,
2338 {
2339 self.0
2340 .config
2341 .envs
2342 .extend(vars.into_iter().map(|(k, v)| (k.into(), Some(v.into()))));
2343 self
2344 }
2345
2346 /// Set the directory of the benchmarked binary (Default: Unchanged)
2347 ///
2348 /// See also [`BinaryBenchmarkConfig::current_dir`]
2349 ///
2350 /// # Examples
2351 ///
2352 /// To set the working directory of your [`Command`] to `/tmp`:
2353 ///
2354 /// ```rust
2355 /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2356 /// # use iai_callgrind::main;
2357 /// use iai_callgrind::{binary_benchmark_group, binary_benchmark};
2358 ///
2359 /// #[binary_benchmark]
2360 /// fn bench_binary() -> iai_callgrind::Command {
2361 /// iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-exe"))
2362 /// .current_dir("/tmp")
2363 /// .build()
2364 /// }
2365 ///
2366 /// binary_benchmark_group!(
2367 /// name = my_group;
2368 /// benchmarks = bench_binary
2369 /// );
2370 /// # fn main() {
2371 /// # main!(binary_benchmark_groups = my_group);
2372 /// # }
2373 /// ```
2374 ///
2375 /// and the following will change the current directory to `fixtures` located in the root of the
2376 /// [`BinaryBenchmarkConfig::sandbox`]
2377 ///
2378 /// ```rust
2379 /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2380 /// # use iai_callgrind::main;
2381 /// use iai_callgrind::{binary_benchmark_group, binary_benchmark, Sandbox, BinaryBenchmarkConfig};
2382 ///
2383 /// fn setup_sandbox() {
2384 /// std::fs::create_dir("fixtures").unwrap();
2385 /// }
2386 ///
2387 /// #[binary_benchmark(
2388 /// setup = setup_sandbox(),
2389 /// config = BinaryBenchmarkConfig::default().sandbox(Sandbox::new(true))
2390 /// )]
2391 /// fn bench_binary() -> iai_callgrind::Command {
2392 /// iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-exe"))
2393 /// .current_dir("fixtures")
2394 /// .build()
2395 /// }
2396 ///
2397 /// binary_benchmark_group!(
2398 /// name = my_group;
2399 /// benchmarks = bench_binary
2400 /// );
2401 /// # fn main() {
2402 /// # main!(binary_benchmark_groups = my_group);
2403 /// # }
2404 /// ```
2405 pub fn current_dir<T>(&mut self, value: T) -> &mut Self
2406 where
2407 T: Into<PathBuf>,
2408 {
2409 self.0.config.current_dir = Some(value.into());
2410 self
2411 }
2412
2413 /// Set the expected exit status [`ExitWith`] of this [`Command`]
2414 ///
2415 /// See also [`BinaryBenchmarkConfig::exit_with`]. This setting overwrites the setting of the
2416 /// [`BinaryBenchmarkConfig`].
2417 ///
2418 /// # Examples
2419 ///
2420 /// If the command is expected to exit with a specific code, for example `100`:
2421 ///
2422 /// ```rust
2423 /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2424 /// # use iai_callgrind::main;
2425 /// use iai_callgrind::{binary_benchmark_group, binary_benchmark, ExitWith};
2426 ///
2427 /// #[binary_benchmark]
2428 /// fn bench_binary() -> iai_callgrind::Command {
2429 /// iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-exe"))
2430 /// .exit_with(ExitWith::Code(100))
2431 /// .build()
2432 /// }
2433 ///
2434 /// binary_benchmark_group!(
2435 /// name = my_group;
2436 /// benchmarks = bench_binary
2437 /// );
2438 /// # fn main() {
2439 /// # main!(binary_benchmark_groups = my_group);
2440 /// # }
2441 /// ```
2442 ///
2443 /// If a command is expected to fail, but the exit code doesn't matter:
2444 ///
2445 /// ```rust
2446 /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2447 /// # use iai_callgrind::main;
2448 /// use iai_callgrind::{binary_benchmark_group, binary_benchmark, ExitWith};
2449 ///
2450 /// #[binary_benchmark]
2451 /// fn bench_binary() -> iai_callgrind::Command {
2452 /// iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-exe"))
2453 /// .exit_with(ExitWith::Failure)
2454 /// .build()
2455 /// }
2456 ///
2457 /// binary_benchmark_group!(
2458 /// name = my_group;
2459 /// benchmarks = bench_binary
2460 /// );
2461 /// # fn main() {
2462 /// # main!(binary_benchmark_groups = my_group);
2463 /// # }
2464 /// ```
2465 pub fn exit_with(&mut self, exit_with: ExitWith) -> &mut Self {
2466 self.0.config.exit_with = Some(exit_with.into());
2467 self
2468 }
2469
2470 /// Finalize and build this [`Command`]
2471 ///
2472 /// # Examples
2473 ///
2474 /// ```rust
2475 /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2476 /// use iai_callgrind::Command;
2477 ///
2478 /// let command: Command = Command::new(env!("CARGO_BIN_EXE_my-exe"))
2479 /// .arg("foo")
2480 /// .build();
2481 /// ```
2482 pub fn build(&mut self) -> Self {
2483 self.clone()
2484 }
2485}
2486
2487impl From<&mut Command> for Command {
2488 fn from(value: &mut Command) -> Self {
2489 value.clone()
2490 }
2491}
2492
2493impl From<&Command> for Command {
2494 fn from(value: &Command) -> Self {
2495 value.clone()
2496 }
2497}
2498
2499impl Delay {
2500 /// Instantiate a [`Delay`] which will wait until a path exists ([`Path::exists`]).
2501 ///
2502 /// ```rust
2503 /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2504 ///
2505 /// use iai_callgrind::{Command, Delay};
2506 ///
2507 /// let command = Command::new("echo").delay(Delay::from_path("/your/path/to/wait/for"));
2508 /// ```
2509 pub fn from_path<T>(path: T) -> Self
2510 where
2511 T: Into<PathBuf>,
2512 {
2513 Self(internal::InternalDelay {
2514 kind: DelayKind::PathExists(path.into()),
2515 ..Default::default()
2516 })
2517 }
2518
2519 /// Instantiate a [`Delay`] which will wait until successful TCP connect
2520 /// ([`std::net::TcpStream::connect`]).
2521 ///
2522 /// ```rust
2523 /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2524 ///
2525 /// use std::net::SocketAddr;
2526 ///
2527 /// use iai_callgrind::{Command, Delay};
2528 ///
2529 /// let command = Command::new("echo").delay(Delay::from_tcp_socket(
2530 /// "127.0.0.1:31000".parse::<SocketAddr>().unwrap(),
2531 /// ));
2532 /// ```
2533 pub fn from_tcp_socket<T>(addr: T) -> Self
2534 where
2535 T: Into<SocketAddr>,
2536 {
2537 Self(internal::InternalDelay {
2538 kind: DelayKind::TcpConnect(addr.into()),
2539 ..Default::default()
2540 })
2541 }
2542
2543 /// Instantiate a [`Delay`] which will wait until a UDP response is received after
2544 /// sending the UDP request. The poll duration is also used as the reconnect and request resend
2545 /// interval. ([`std::net::UdpSocket::bind`], [`std::net::UdpSocket::connect`],
2546 /// [`std::net::UdpSocket::recv`]).
2547 ///
2548 /// ```rust
2549 /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2550 ///
2551 /// use std::net::SocketAddr;
2552 ///
2553 /// use iai_callgrind::{Command, Delay};
2554 ///
2555 /// let command = Command::new("echo").delay(Delay::from_udp_request(
2556 /// "127.0.0.1:34000".parse::<SocketAddr>().unwrap(),
2557 /// vec![1],
2558 /// ));
2559 /// ```
2560 pub fn from_udp_request<T, U>(addr: T, req: U) -> Self
2561 where
2562 T: Into<SocketAddr>,
2563 U: Into<Vec<u8>>,
2564 {
2565 Self(internal::InternalDelay {
2566 kind: DelayKind::UdpResponse(addr.into(), req.into()),
2567 ..Default::default()
2568 })
2569 }
2570
2571 /// Instantiate a [`Delay`] waiting until an event has happened.
2572 ///
2573 /// The possible events are defined in [`DelayKind`].
2574 ///
2575 /// ```rust
2576 /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2577 ///
2578 /// use std::time::Duration;
2579 ///
2580 /// use iai_callgrind::{Command, Delay, DelayKind};
2581 ///
2582 /// let command = Command::new("echo").delay(Delay::new(DelayKind::DurationElapse(
2583 /// Duration::from_secs(15),
2584 /// )));
2585 /// ```
2586 pub fn new(kind: DelayKind) -> Self {
2587 Self(internal::InternalDelay {
2588 kind,
2589 ..Default::default()
2590 })
2591 }
2592
2593 /// Update the [`Delay`] poll interval.
2594 ///
2595 /// The poll interval should be considered together with the [`Delay::timeout`], and ideally
2596 /// should have a value of `n * timeout duration`.
2597 ///
2598 /// In case the poll interval is set to a value `>=` timeout duration it is attempted to set
2599 /// the poll interval to a value of `timeout duration - 5ms`.
2600 ///
2601 /// ```rust
2602 /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2603 ///
2604 /// use std::net::SocketAddr;
2605 /// use std::time::Duration;
2606 ///
2607 /// use iai_callgrind::{Command, Delay};
2608 ///
2609 /// let command = Command::new("echo").delay(
2610 /// Delay::from_udp_request("127.0.0.1:34000".parse::<SocketAddr>().unwrap(), vec![1])
2611 /// .poll(Duration::from_millis(150)),
2612 /// );
2613 /// ```
2614 pub fn poll<T: Into<Duration>>(mut self, duration: T) -> Self {
2615 self.0.poll = Some(duration.into());
2616 self
2617 }
2618
2619 /// Update the [`Delay`] timeout interval.
2620 ///
2621 /// The timeout duration should be considered together with the poll interval. For further
2622 /// details please refer to [`Delay::poll`]. The minimum timeout duration is `10ms`.
2623 ///
2624 /// ```rust
2625 /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2626 ///
2627 /// use std::net::SocketAddr;
2628 /// use std::time::Duration;
2629 ///
2630 /// use iai_callgrind::{Command, Delay};
2631 /// let command = Command::new("echo").delay(
2632 /// Delay::from_tcp_socket("127.0.0.1:31000".parse::<SocketAddr>().unwrap())
2633 /// .timeout(Duration::from_secs(5)),
2634 /// );
2635 /// ```
2636 pub fn timeout<T: Into<Duration>>(mut self, duration: T) -> Self {
2637 self.0.timeout = Some(duration.into());
2638 self
2639 }
2640}
2641
2642impl<T> From<T> for Delay
2643where
2644 T: Into<Duration>,
2645{
2646 /// Instantiate a [`Delay`] which will wait until the duration has elapsed.
2647 ///
2648 /// ```rust
2649 /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2650 ///
2651 /// use std::time::Duration;
2652 ///
2653 /// use iai_callgrind::{Command, Delay};
2654 ///
2655 /// let command = Command::new("echo").delay(Delay::from(Duration::from_secs(10)));
2656 /// ```
2657 fn from(duration: T) -> Self {
2658 Self(internal::InternalDelay {
2659 kind: DelayKind::DurationElapse(duration.into()),
2660 ..Default::default()
2661 })
2662 }
2663}
2664
2665impl From<ExitWith> for internal::InternalExitWith {
2666 fn from(value: ExitWith) -> Self {
2667 match value {
2668 ExitWith::Success => Self::Success,
2669 ExitWith::Failure => Self::Failure,
2670 ExitWith::Code(c) => Self::Code(c),
2671 }
2672 }
2673}
2674
2675impl From<&ExitWith> for internal::InternalExitWith {
2676 fn from(value: &ExitWith) -> Self {
2677 match value {
2678 ExitWith::Success => Self::Success,
2679 ExitWith::Failure => Self::Failure,
2680 ExitWith::Code(c) => Self::Code(*c),
2681 }
2682 }
2683}
2684
2685impl Sandbox {
2686 /// Create a new `Sandbox` builder
2687 ///
2688 /// Per default, a [`Command`] is not run in a `Sandbox` because setting up a `Sandbox` usually
2689 /// involves some user interaction, for example copying fixtures into it with
2690 /// [`Sandbox::fixtures`].
2691 ///
2692 /// The temporary directory is only created immediately before the `setup` and the [`Command`]
2693 /// are executed.
2694 ///
2695 /// # Examples
2696 ///
2697 /// Enable the sandbox for all benchmarks
2698 ///
2699 /// ```rust
2700 /// use iai_callgrind::{BinaryBenchmarkConfig, Sandbox, main};
2701 /// # use iai_callgrind::binary_benchmark_group;
2702 /// # binary_benchmark_group!(name = my_group; benchmarks = |_group| {});
2703 /// # fn main() {
2704 /// main!(
2705 /// config = BinaryBenchmarkConfig::default().sandbox(Sandbox::new(true));
2706 /// binary_benchmark_groups = my_group
2707 /// );
2708 /// # }
2709 /// ```
2710 pub fn new(enabled: bool) -> Self {
2711 Self(internal::InternalSandbox {
2712 enabled: Some(enabled),
2713 ..Default::default()
2714 })
2715 }
2716
2717 /// Specify the directories and/or files you want to copy into the root of the `Sandbox`
2718 ///
2719 /// The paths are interpreted relative to the workspace root as it is reported by `cargo`. In a
2720 /// multi-crate project this is the directory with the top-level `Cargo.toml`. Otherwise, it is
2721 /// simply the directory with your `Cargo.toml` file in it.
2722 ///
2723 /// # Examples
2724 ///
2725 /// Assuming you crate's binary is called `my-foo` taking a file path as the first argument and
2726 /// the fixtures directory is `$WORKSPACE_ROOT/benches/fixtures` containing a fixture
2727 /// `fix_1.txt`:
2728 ///
2729 /// ```rust
2730 /// # macro_rules! env { ($m:tt) => {{ "/some/path" }} }
2731 /// # use iai_callgrind::{binary_benchmark_group, main};
2732 /// use iai_callgrind::{binary_benchmark, BinaryBenchmarkConfig, Sandbox};
2733 ///
2734 /// #[binary_benchmark]
2735 /// #[bench::fix_1(
2736 /// args = ("fix_1.txt"),
2737 /// config = BinaryBenchmarkConfig::default()
2738 /// .sandbox(Sandbox::new(true)
2739 /// .fixtures(["benches/fixtures/fix_1.txt"])
2740 /// )
2741 /// )]
2742 /// fn bench_with_fixtures(path: &str) -> iai_callgrind::Command {
2743 /// iai_callgrind::Command::new(env!("CARGO_BIN_EXE_my-foo"))
2744 /// .arg(path)
2745 /// .build()
2746 /// }
2747 /// # binary_benchmark_group!(name = my_group; benchmarks = bench_with_fixtures);
2748 /// # fn main() { main!(binary_benchmark_groups = my_group); }
2749 /// ```
2750 pub fn fixtures<I, T>(&mut self, paths: T) -> &mut Self
2751 where
2752 I: Into<PathBuf>,
2753 T: IntoIterator<Item = I>,
2754 {
2755 self.0.fixtures.extend(paths.into_iter().map(Into::into));
2756 self
2757 }
2758}
2759
2760#[cfg(test)]
2761mod tests {
2762 use rstest::rstest;
2763
2764 use super::*;
2765
2766 #[rstest]
2767 #[case::empty("")]
2768 #[case::simple_invalid("-")]
2769 #[case::when_0_char_minus_1("a\x2f")]
2770 #[case::when_9_char_plus_1("a\x3a")]
2771 #[case::when_big_a_char_minus_1("\x40")]
2772 #[case::when_big_z_char_plus_1("\x5b")]
2773 #[case::when_low_a_char_minus_1("\x60")]
2774 #[case::when_low_z_char_plus_1("\x7b")]
2775 #[case::invalid_2nd("a-")]
2776 #[case::invalid_3rd("ab-")]
2777 #[case::all_invalid("---")]
2778 #[case::number_as_first("0")]
2779 // This would be a valid rust identifier, but we don't allow the whole set
2780 #[case::non_ascii_1st("µ")]
2781 #[case::non_ascii_2nd("aµ")]
2782 #[case::non_ascii_3rd("aaµ")]
2783 #[case::non_ascii_middle("aµa")]
2784 fn test_benchmark_id_validate_when_error(#[case] id: &str) {
2785 let id = BenchmarkId::new(id);
2786 assert!(id.validate().is_err());
2787 }
2788
2789 #[rstest]
2790 #[case::lowercase_a("a")]
2791 #[case::lowercase_b("b")]
2792 #[case::lowercase_m("m")]
2793 #[case::lowercase_y("y")]
2794 #[case::lowercase_z("z")]
2795 #[case::uppercase_a("A")]
2796 #[case::uppercase_b("B")]
2797 #[case::uppercase_n("N")]
2798 #[case::uppercase_y("Y")]
2799 #[case::uppercase_z("Z")]
2800 #[case::zero_2nd("a0")]
2801 #[case::one_2nd("a1")]
2802 #[case::eight_2nd("a8")]
2803 #[case::nine_2nd("a9")]
2804 #[case::number_middle("b4t")]
2805 #[case::underscore("_")]
2806 #[case::only_underscore("___")]
2807 #[case::underscore_last("a_")]
2808 #[case::mixed_all("auAEwer9__2xcd")]
2809 fn test_benchmark_id_validate(#[case] id: &str) {
2810 let id = BenchmarkId::new(id);
2811 assert!(id.validate().is_ok());
2812 }
2813
2814 #[rstest]
2815 #[case::empty("", "Invalid id: Cannot be empty")]
2816 #[case::non_ascii_first(
2817 "µ",
2818 "Invalid id 'µ': Encountered non-ascii character as first character"
2819 )]
2820 #[case::multibyte_middle(
2821 "aµ",
2822 "Invalid id 'aµ' at position 1: Encountered non-ascii character"
2823 )]
2824 #[case::non_ascii_middle("a-", "Invalid id 'a-' at position 1: Invalid character '-'")]
2825 #[case::invalid_first("-", "Invalid id '-': As first character is '-' not allowed")]
2826 fn test_benchmark_id_validate_error_message(#[case] id: &str, #[case] message: &str) {
2827 let id = BenchmarkId::new(id);
2828 assert_eq!(
2829 id.validate()
2830 .expect_err("Validation should return an error"),
2831 message
2832 );
2833 }
2834}