Skip to main content

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