nextest_runner/
usdt.rs

1// Copyright (c) The nextest Contributors
2// SPDX-License-Identifier: MIT OR Apache-2.0
3
4//! [USDT](usdt) probes for nextest.
5//!
6//! This module acts as documentation for USDT (Userland Statically Defined
7//! Tracing) probes defined by nextest.
8//!
9//! USDT probes are supported:
10//!
11//! * On x86_64: Linux, via [bpftrace](https://bpftrace.org/)
12//! * On aarch64: macOS, via [DTrace](https://dtrace.org/)
13//! * On x86_64 and aarch64: illumos and other Solaris derivatives, and FreeBSD, via [DTrace](https://dtrace.org/)
14//!
15//! The probes and their contents are not part of nextest's stability guarantees.
16//!
17//! For more information and examples, see the [nextest documentation](https://nexte.st/docs/integrations/usdt).
18
19use nextest_metadata::RustBinaryId;
20use quick_junit::ReportUuid;
21use serde::Serialize;
22
23/// Register USDT probes on supported platforms.
24#[cfg(any(
25    all(
26        target_arch = "x86_64",
27        any(target_os = "linux", target_os = "freebsd", target_os = "illumos")
28    ),
29    all(
30        target_arch = "aarch64",
31        any(target_os = "macos", target_os = "freebsd", target_os = "illumos")
32    )
33))]
34pub fn register_probes() -> Result<(), usdt::Error> {
35    usdt::register_probes()
36}
37
38/// No-op for unsupported platforms.
39#[cfg(not(any(
40    all(
41        target_arch = "x86_64",
42        any(target_os = "linux", target_os = "freebsd", target_os = "illumos")
43    ),
44    all(
45        target_arch = "aarch64",
46        any(target_os = "macos", target_os = "freebsd", target_os = "illumos")
47    )
48)))]
49pub fn register_probes() -> Result<(), std::convert::Infallible> {
50    Ok(())
51}
52
53#[cfg(any(
54    all(
55        target_arch = "x86_64",
56        any(target_os = "linux", target_os = "freebsd", target_os = "illumos")
57    ),
58    all(
59        target_arch = "aarch64",
60        any(target_os = "macos", target_os = "freebsd", target_os = "illumos")
61    )
62))]
63#[usdt::provider(provider = "nextest")]
64pub mod usdt_probes {
65    use crate::usdt::*;
66
67    pub fn test__attempt__start(
68        attempt: &UsdtTestAttemptStart,
69        attempt_id: &str,
70        binary_id: &str,
71        test_name: &str,
72        pid: u32,
73    ) {
74    }
75    pub fn test__attempt__done(
76        attempt: &UsdtTestAttemptDone,
77        attempt_id: &str,
78        binary_id: &str,
79        test_name: &str,
80        result: &str,
81        duration_nanos: u64,
82    ) {
83    }
84    pub fn test__attempt__slow(
85        slow: &UsdtTestAttemptSlow,
86        attempt_id: &str,
87        binary_id: &str,
88        test_name: &str,
89        elapsed_nanos: u64,
90    ) {
91    }
92    pub fn setup__script__start(
93        script: &UsdtSetupScriptStart,
94        id: &str,
95        script_id: &str,
96        pid: u32,
97    ) {
98    }
99    pub fn setup__script__slow(
100        script: &UsdtSetupScriptSlow,
101        id: &str,
102        script_id: &str,
103        elapsed_nanos: u64,
104    ) {
105    }
106    pub fn setup__script__done(
107        script: &UsdtSetupScriptDone,
108        id: &str,
109        script_id: &str,
110        result: &str,
111        duration_nanos: u64,
112    ) {
113    }
114    pub fn run__start(run: &UsdtRunStart, run_id: ReportUuid) {}
115    pub fn run__done(run: &UsdtRunDone, run_id: ReportUuid) {}
116    pub fn stress__sub__run__start(
117        sub_run: &UsdtStressSubRunStart,
118        stress_sub_run_id: &str,
119        stress_current: u32,
120    ) {
121    }
122    pub fn stress__sub__run__done(
123        sub_run: &UsdtStressSubRunDone,
124        stress_sub_run_id: &str,
125        stress_current: u32,
126    ) {
127    }
128}
129
130/// Fires a USDT probe on supported platforms.
131#[cfg(any(
132    all(
133        target_arch = "x86_64",
134        any(target_os = "linux", target_os = "freebsd", target_os = "illumos")
135    ),
136    all(
137        target_arch = "aarch64",
138        any(target_os = "macos", target_os = "freebsd", target_os = "illumos")
139    )
140))]
141#[macro_export]
142macro_rules! fire_usdt {
143    (UsdtTestAttemptStart { $($tt:tt)* }) => {{
144        $crate::usdt::usdt_probes::test__attempt__start!(|| {
145            let probe = $crate::usdt::UsdtTestAttemptStart { $($tt)* };
146            let attempt_id = probe.attempt_id.clone();
147            let binary_id = probe.binary_id.to_string();
148            let test_name = probe.test_name.clone();
149            let pid = probe.pid;
150            (probe, attempt_id, binary_id, test_name, pid)
151        })
152    }};
153    (UsdtTestAttemptDone { $($tt:tt)* }) => {{
154        $crate::usdt::usdt_probes::test__attempt__done!(|| {
155            let probe = $crate::usdt::UsdtTestAttemptDone { $($tt)* };
156            let attempt_id = probe.attempt_id.clone();
157            let binary_id = probe.binary_id.to_string();
158            let test_name = probe.test_name.clone();
159            let result = probe.result;
160            let duration_nanos = probe.duration_nanos;
161            (
162                probe,
163                attempt_id,
164                binary_id,
165                test_name,
166                result,
167                duration_nanos,
168            )
169        })
170    }};
171    (UsdtTestAttemptSlow { $($tt:tt)* }) => {{
172        $crate::usdt::usdt_probes::test__attempt__slow!(|| {
173            let probe = $crate::usdt::UsdtTestAttemptSlow { $($tt)* };
174            let attempt_id = probe.attempt_id.clone();
175            let binary_id = probe.binary_id.to_string();
176            let test_name = probe.test_name.clone();
177            let elapsed_nanos = probe.elapsed_nanos;
178            (probe, attempt_id, binary_id, test_name, elapsed_nanos)
179        })
180    }};
181    (UsdtSetupScriptStart { $($tt:tt)* }) => {{
182        $crate::usdt::usdt_probes::setup__script__start!(|| {
183            let probe = $crate::usdt::UsdtSetupScriptStart { $($tt)* };
184            let id = probe.id.clone();
185            let script_id = probe.script_id.clone();
186            let pid = probe.pid;
187            (probe, id, script_id, pid)
188        })
189    }};
190    (UsdtSetupScriptSlow { $($tt:tt)* }) => {{
191        $crate::usdt::usdt_probes::setup__script__slow!(|| {
192            let probe = $crate::usdt::UsdtSetupScriptSlow { $($tt)* };
193            let id = probe.id.clone();
194            let script_id = probe.script_id.clone();
195            let elapsed_nanos = probe.elapsed_nanos;
196            (probe, id, script_id, elapsed_nanos)
197        })
198    }};
199    (UsdtSetupScriptDone { $($tt:tt)* }) => {{
200        $crate::usdt::usdt_probes::setup__script__done!(|| {
201            let probe = $crate::usdt::UsdtSetupScriptDone { $($tt)* };
202            let id = probe.id.clone();
203            let script_id = probe.script_id.clone();
204            let result = probe.result;
205            let duration_nanos = probe.duration_nanos;
206            (probe, id, script_id, result, duration_nanos)
207        })
208    }};
209    (UsdtRunStart { $($tt:tt)* }) => {{
210        $crate::usdt::usdt_probes::run__start!(|| {
211            let probe = $crate::usdt::UsdtRunStart { $($tt)* };
212            let run_id = probe.run_id;
213            (probe, run_id)
214        })
215    }};
216    (UsdtRunDone { $($tt:tt)* }) => {{
217        $crate::usdt::usdt_probes::run__done!(|| {
218            let probe = $crate::usdt::UsdtRunDone { $($tt)* };
219            let run_id = probe.run_id;
220            (probe, run_id)
221        })
222    }};
223    (UsdtStressSubRunStart { $($tt:tt)* }) => {{
224        $crate::usdt::usdt_probes::stress__sub__run__start!(|| {
225            let probe = $crate::usdt::UsdtStressSubRunStart { $($tt)* };
226            let stress_sub_run_id = probe.stress_sub_run_id.clone();
227            let stress_current = probe.stress_current;
228            (probe, stress_sub_run_id, stress_current)
229        })
230    }};
231    (UsdtStressSubRunDone { $($tt:tt)* }) => {{
232        $crate::usdt::usdt_probes::stress__sub__run__done!(|| {
233            let probe = $crate::usdt::UsdtStressSubRunDone { $($tt)* };
234            let stress_sub_run_id = probe.stress_sub_run_id.clone();
235            let stress_current = probe.stress_current;
236            (probe, stress_sub_run_id, stress_current)
237        })
238    }};
239}
240
241/// No-op version of fire_usdt for unsupported platforms.
242#[cfg(not(any(
243    all(
244        target_arch = "x86_64",
245        any(target_os = "linux", target_os = "freebsd", target_os = "illumos")
246    ),
247    all(
248        target_arch = "aarch64",
249        any(target_os = "macos", target_os = "freebsd", target_os = "illumos")
250    )
251)))]
252#[macro_export]
253macro_rules! fire_usdt {
254    ($($tt:tt)*) => {
255        let _ = $crate::usdt::$($tt)*;
256    };
257}
258
259/// Data associated with the `test-attempt-start` probe.
260///
261/// This data is JSON-encoded as `arg0`.
262#[derive(Clone, Debug, Serialize)]
263pub struct UsdtTestAttemptStart {
264    /// A unique identifier for this test attempt, comprised of the run ID, the
265    /// binary ID, the test name, the attempt number, and the stress index.
266    ///
267    /// Also available as `arg1`.
268    pub attempt_id: String,
269
270    /// The nextest run ID, unique for each run.
271    pub run_id: ReportUuid,
272
273    /// The binary ID.
274    ///
275    /// Also available as `arg2`.
276    pub binary_id: RustBinaryId,
277
278    /// The name of the test.
279    ///
280    /// Also available as `arg3`.
281    pub test_name: String,
282
283    /// The process ID of the test.
284    ///
285    /// Also available as `arg4`.
286    pub pid: u32,
287
288    /// The program to run.
289    pub program: String,
290
291    /// The arguments to pass to the program.
292    pub args: Vec<String>,
293
294    /// The attempt number, starting at 1 and <= `total_attempts`.
295    pub attempt: u32,
296
297    /// The total number of attempts.
298    pub total_attempts: u32,
299
300    /// The 0-indexed stress run index, if running stress tests.
301    pub stress_current: Option<u32>,
302
303    /// The total number of stress runs, if available.
304    pub stress_total: Option<u32>,
305}
306
307/// Data associated with the `test-attempt-done` probe.
308///
309/// This data is JSON-encoded as `arg0`.
310#[derive(Clone, Debug, Serialize)]
311pub struct UsdtTestAttemptDone {
312    /// A unique identifier for this test attempt, comprised of the run ID, the
313    /// binary ID, the test name, the attempt number, and the stress index.
314    ///
315    /// Also available as `arg1`.
316    pub attempt_id: String,
317
318    /// The nextest run ID, unique for each run.
319    pub run_id: ReportUuid,
320
321    /// The binary ID.
322    ///
323    /// Also available as `arg2`.
324    pub binary_id: RustBinaryId,
325
326    /// The name of the test.
327    ///
328    /// Also available as `arg3`.
329    pub test_name: String,
330
331    /// The attempt number, starting at 1 and <= `total_attempts`.
332    pub attempt: u32,
333
334    /// The total number of attempts.
335    pub total_attempts: u32,
336
337    /// The test result as a string (e.g., "pass", "fail", "timeout", "exec-fail").
338    ///
339    /// Also available as `arg4`.
340    pub result: &'static str,
341
342    /// The exit code of the test process, if available.
343    pub exit_code: Option<i32>,
344
345    /// The duration of the test in nanoseconds.
346    ///
347    /// Also available as `arg5`.
348    pub duration_nanos: u64,
349
350    /// Whether file descriptors were leaked.
351    pub leaked: bool,
352
353    /// Time taken for the standard output and standard error file descriptors
354    /// to close, in nanoseconds. None if they didn't close (timed out).
355    pub time_to_close_fds_nanos: Option<u64>,
356
357    /// The 0-indexed stress run index, if running stress tests.
358    pub stress_current: Option<u32>,
359
360    /// The total number of stress runs, if available.
361    pub stress_total: Option<u32>,
362
363    /// The length of stdout in bytes, if captured.
364    pub stdout_len: Option<u64>,
365
366    /// The length of stderr in bytes, if captured.
367    pub stderr_len: Option<u64>,
368}
369
370/// Data associated with the `test-attempt-slow` probe.
371///
372/// This data is JSON-encoded as `arg0`.
373#[derive(Clone, Debug, Serialize)]
374pub struct UsdtTestAttemptSlow {
375    /// A unique identifier for this test attempt, comprised of the run ID, the
376    /// binary ID, the test name, the attempt number, and the stress index.
377    ///
378    /// Also available as `arg1`.
379    pub attempt_id: String,
380
381    /// The nextest run ID, unique for each run.
382    pub run_id: ReportUuid,
383
384    /// The binary ID. Also available as `arg2`.
385    pub binary_id: RustBinaryId,
386
387    /// The name of the test. Also available as `arg3`.
388    pub test_name: String,
389
390    /// The attempt number, starting at 1 and <= `total_attempts`.
391    pub attempt: u32,
392
393    /// The total number of attempts.
394    pub total_attempts: u32,
395
396    /// The time elapsed since the test started, in nanoseconds.
397    ///
398    /// Also available as `arg4`.
399    pub elapsed_nanos: u64,
400
401    /// Whether the test is about to be terminated due to timeout.
402    pub will_terminate: bool,
403
404    /// The 0-indexed stress run index, if running stress tests.
405    pub stress_current: Option<u32>,
406
407    /// The total number of stress runs, if available.
408    pub stress_total: Option<u32>,
409}
410
411/// Data associated with the `setup-script-start` probe.
412///
413/// This data is JSON-encoded as `arg0`.
414#[derive(Clone, Debug, Serialize)]
415pub struct UsdtSetupScriptStart {
416    /// A unique identifier for this script run, comprised of the run ID, the
417    /// script ID, and the stress index if relevant.
418    ///
419    /// Also available as `arg1`.
420    pub id: String,
421
422    /// The nextest run ID, unique for each run.
423    pub run_id: ReportUuid,
424
425    /// The script ID.
426    ///
427    /// Also available as `arg2`.
428    pub script_id: String,
429
430    /// The process ID of the script.
431    ///
432    /// Also available as `arg3`.
433    pub pid: u32,
434
435    /// The program to run.
436    pub program: String,
437
438    /// The arguments to pass to the program.
439    pub args: Vec<String>,
440
441    /// The 0-indexed stress run index, if running stress tests.
442    pub stress_current: Option<u32>,
443
444    /// The total number of stress runs, if available.
445    pub stress_total: Option<u32>,
446}
447
448/// Data associated with the `setup-script-slow` probe.
449///
450/// This data is JSON-encoded as `arg0`.
451#[derive(Clone, Debug, Serialize)]
452pub struct UsdtSetupScriptSlow {
453    /// A unique identifier for this script run, comprised of the run ID, the
454    /// script ID, and the stress index if relevant.
455    ///
456    /// Also available as `arg1`.
457    pub id: String,
458
459    /// The nextest run ID, unique for each run.
460    pub run_id: ReportUuid,
461
462    /// The script ID.
463    ///
464    /// Also available as `arg2`.
465    pub script_id: String,
466
467    /// The program to run.
468    pub program: String,
469
470    /// The arguments to pass to the program.
471    pub args: Vec<String>,
472
473    /// The time elapsed since the script started, in nanoseconds.
474    ///
475    /// Also available as `arg3`.
476    pub elapsed_nanos: u64,
477
478    /// Whether the script is about to be terminated due to timeout.
479    pub will_terminate: bool,
480
481    /// The 0-indexed stress run index, if running stress tests.
482    pub stress_current: Option<u32>,
483
484    /// The total number of stress runs, if available.
485    pub stress_total: Option<u32>,
486}
487
488/// Data associated with the `setup-script-done` probe.
489///
490/// This data is JSON-encoded as `arg0`.
491#[derive(Clone, Debug, Serialize)]
492pub struct UsdtSetupScriptDone {
493    /// A unique identifier for this script run, comprised of the run ID, the
494    /// script ID, and the stress index if relevant.
495    ///
496    /// Also available as `arg1`.
497    pub id: String,
498
499    /// The nextest run ID, unique for each run.
500    pub run_id: ReportUuid,
501
502    /// The script ID.
503    ///
504    /// Also available as `arg2`.
505    pub script_id: String,
506
507    /// The program to run.
508    pub program: String,
509
510    /// The arguments to pass to the program.
511    pub args: Vec<String>,
512
513    /// The script result as a string (e.g., "pass", "fail", "timeout",
514    /// "exec-fail").
515    ///
516    /// Also available as `arg3`.
517    pub result: &'static str,
518
519    /// The exit code of the script process, if available.
520    pub exit_code: Option<i32>,
521
522    /// The duration of the script execution in nanoseconds.
523    ///
524    /// Also available as `arg4`.
525    pub duration_nanos: u64,
526
527    /// The 0-indexed stress run index, if running stress tests.
528    pub stress_current: Option<u32>,
529
530    /// The total number of stress runs, if available.
531    pub stress_total: Option<u32>,
532
533    /// The length of stdout in bytes, if captured.
534    pub stdout_len: Option<u64>,
535
536    /// The length of stderr in bytes, if captured.
537    pub stderr_len: Option<u64>,
538}
539
540/// Data associated with the `run-start` probe.
541///
542/// This data is JSON-encoded as `arg0`.
543#[derive(Clone, Debug, Serialize)]
544pub struct UsdtRunStart {
545    /// The nextest run ID, unique for each run.
546    ///
547    /// Also available as `arg1`.
548    pub run_id: ReportUuid,
549
550    /// The profile name (e.g., "default", "ci").
551    pub profile_name: String,
552
553    /// Total number of tests in the test list.
554    pub total_tests: usize,
555
556    /// Number of tests after filtering.
557    pub filter_count: usize,
558
559    /// Number of test threads.
560    pub test_threads: usize,
561
562    /// If this is a count-based stress run with a finite number of runs, the
563    /// number of stress runs.
564    pub stress_count: Option<u32>,
565
566    /// True if this is a count-based stress run with an infinite number of
567    /// runs.
568    pub stress_infinite: bool,
569
570    /// If this is a duration-based stress run, how long we're going to run for.
571    pub stress_duration_nanos: Option<u64>,
572}
573
574/// Data associated with the `run-done` probe.
575///
576/// This data is JSON-encoded as `arg0`.
577#[derive(Clone, Debug, Serialize)]
578pub struct UsdtRunDone {
579    /// The nextest run ID, unique for each run.
580    ///
581    /// Also available as `arg1`.
582    pub run_id: ReportUuid,
583
584    /// The profile name (e.g., "default", "ci").
585    pub profile_name: String,
586
587    /// Total number of tests that were run.
588    ///
589    /// For stress runs, this consists of the last run's total test count.
590    pub total_tests: usize,
591
592    /// Number of tests that passed.
593    ///
594    /// For stress runs, this consists of the last run's passed test count.
595    pub passed: usize,
596
597    /// Number of tests that failed.
598    ///
599    /// For stress runs, this consists of the last run's failed test count.
600    pub failed: usize,
601
602    /// Number of tests that were skipped.
603    ///
604    /// For stress runs, this consists of the last run's skipped test count.
605    pub skipped: usize,
606
607    /// Total active duration of the run in nanoseconds, not including paused
608    /// time.
609    ///
610    /// For stress runs, this adds up the duration across all sub-runs.
611    pub duration_nanos: u64,
612
613    /// The number of nanoseconds the run was paused.
614    ///
615    /// For stress runs, this adds up the paused duration across all sub-runs.
616    pub paused_nanos: u64,
617
618    /// The number of stress runs completed, if this is a stress run.
619    pub stress_completed: Option<u32>,
620
621    /// The number of stress runs that succeeded, if this is a stress run.
622    pub stress_success: Option<u32>,
623
624    /// The number of stress runs that failed, if this is a stress run.
625    pub stress_failed: Option<u32>,
626}
627
628/// Data associated with the `stress-sub-run-start` probe.
629///
630/// This data is JSON-encoded as `arg0`.
631#[derive(Clone, Debug, Serialize)]
632pub struct UsdtStressSubRunStart {
633    /// A unique identifier for this stress sub-run, of the form
634    /// `{run_id}:@stress-{stress_current}`.
635    ///
636    /// Also available as `arg1`.
637    pub stress_sub_run_id: String,
638
639    /// The nextest run ID, unique for each run.
640    pub run_id: ReportUuid,
641
642    /// The profile name (e.g., "default", "ci").
643    pub profile_name: String,
644
645    /// The 0-indexed current stress run number.
646    ///
647    /// Also available as `arg2`.
648    pub stress_current: u32,
649
650    /// The total number of stress runs, if available (None for infinite or
651    /// duration-based runs).
652    pub stress_total: Option<u32>,
653
654    /// The total elapsed time since the overall stress run started, in
655    /// nanoseconds.
656    pub elapsed_nanos: u64,
657}
658
659/// Data associated with the `stress-sub-run-done` probe.
660///
661/// This data is JSON-encoded as `arg0`.
662#[derive(Clone, Debug, Serialize)]
663pub struct UsdtStressSubRunDone {
664    /// A unique identifier for this stress sub-run, of the form
665    /// `{run_id}:@stress-{stress_current}`.
666    ///
667    /// Also available as `arg1`.
668    pub stress_sub_run_id: String,
669
670    /// The nextest run ID, unique for each run.
671    pub run_id: ReportUuid,
672
673    /// The profile name (e.g., "default", "ci").
674    pub profile_name: String,
675
676    /// The 0-indexed current stress run number.
677    ///
678    /// Also available as `arg2`.
679    pub stress_current: u32,
680
681    /// The total number of stress runs, if available (None for infinite or
682    /// duration-based runs).
683    pub stress_total: Option<u32>,
684
685    /// The total elapsed time since the overall stress run started, in
686    /// nanoseconds.
687    pub elapsed_nanos: u64,
688
689    /// The duration of this sub-run in nanoseconds.
690    pub sub_run_duration_nanos: u64,
691
692    /// Total number of tests that were run in this sub-run.
693    pub total_tests: usize,
694
695    /// Number of tests that passed in this sub-run.
696    pub passed: usize,
697
698    /// Number of tests that failed in this sub-run.
699    pub failed: usize,
700
701    /// Number of tests that were skipped in this sub-run.
702    pub skipped: usize,
703}