xref: /aosp_15_r20/external/crosvm/cros_tracing/tests/trace_marker.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1 // Copyright 2023 The ChromiumOS Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 use std::env;
6 use std::env::current_exe;
7 use std::fs::File;
8 use std::io::BufRead;
9 use std::io::BufReader;
10 use std::process::Command;
11 
12 use cros_tracing::*;
13 
14 const TRACE_FILE: &str = "/sys/kernel/tracing/trace";
15 const TRACE_CONTEXT_INFO: &str = "/sys/kernel/tracing/options/context-info";
16 const TRACING_ON: &str = "/sys/kernel/tracing/tracing_on";
17 
setup()18 fn setup() {
19     // Make sure tracing is enabled.
20     std::fs::write(TRACING_ON, b"1").unwrap();
21     // Remove extra noise from trace file for easier parsing
22     std::fs::write(TRACE_CONTEXT_INFO, b"0").unwrap();
23     // Clear the trace backlog by writing an empty string, in case we have extra
24     // rogue messages in the trace buffer
25     std::fs::write(TRACE_FILE, b"").unwrap();
26 
27     init();
28 }
29 
cleanup()30 fn cleanup() {
31     // Stop tracing.
32     std::fs::write(TRACING_ON, b"0").unwrap();
33     // Reset trace file format back to how it was.
34     std::fs::write(TRACE_CONTEXT_INFO, b"1").unwrap();
35 }
36 
trace_simple_print()37 fn trace_simple_print() {
38     let reader = BufReader::new(File::open(TRACE_FILE).ok().unwrap());
39 
40     let message1 = "Simple print test one";
41     let message2 = "Simple print test two";
42 
43     trace_simple_print!("{message1}");
44     trace_simple_print!("{message2}");
45 
46     // Read contents of the file, skip the first two lines which are just a preamble.
47     let mut lines = reader.lines().map(|l| l.unwrap()).skip(2);
48 
49     // Check the printed lines are in order.
50     // We need to use contains instead of matching the full string because each time we
51     // print to trace_marker we will get unique data like PID and timestamps that we cannot
52     // rely on, but the contents of the message itself should always contain our string.
53     assert!(lines.next().unwrap().contains(message1));
54     assert!(lines.next().unwrap().contains(message2));
55 }
56 
push_descriptors()57 fn push_descriptors() {
58     let mut keep_rds = Vec::new();
59 
60     push_descriptors!(&mut keep_rds);
61 
62     // We cannot know the fd of the trace marker file beforehand but we can check if there
63     // is only one fd in the vector it means we can assume it's the trace_marker one.
64     assert_eq!(keep_rds.len(), 1);
65 }
66 
67 /// Executes the individual test `name` with root, in the same environment as the test suite,
68 /// if it does not already have root privileges. Sudo needs to be set up to run passwordless
69 /// or have cached credentials. The parent process spawns a child that runs with higher privileges
70 /// for that individual `name` test, and then waits for its completion.
71 ///
72 /// Returns `true` if the test suite already has root privilege, in which case it
73 /// proceeds to run the test without forking, otherwise it returns `false` to let the
74 /// test suite know that the test was run by the child process instead.
75 ///
76 /// # Arguments
77 ///
78 /// * `name` - Name of the individual test to execute as root
79 ///
80 /// # Examples
81 ///
82 /// ```
83 /// libtest_mimic::Trial::test("test_with_root", move || {
84 ///    if run_test_with_root("test_with_root") {
85 ///        // This part only executes with root
86 ///        function_to_test();
87 ///    }
88 ///    Ok(())
89 /// });
90 /// ```
run_test_with_root(name: &str) -> bool91 fn run_test_with_root(name: &str) -> bool {
92     // This test needs to run as root, so if we aren't root we need to re-execute ourselves
93     // with sudo.
94     let is_root = match env::var("USER") {
95         Ok(val) => val == "root",
96         Err(_) => false,
97     };
98 
99     if !is_root {
100         let can_sudo = Command::new("sudo")
101             .args(["--askpass", "true"])
102             .env("SUDO_ASKPASS", "false")
103             .output()
104             .unwrap();
105         if !can_sudo.status.success() {
106             panic!("This test needs to be run as root or with passwordless sudo.");
107         }
108 
109         let result = Command::new("sudo")
110             .args([
111                 "--preserve-env",
112                 current_exe().unwrap().to_str().unwrap(),
113                 "--nocapture",
114                 "--ignored",
115                 "--exact",
116                 name,
117             ])
118             .status()
119             .unwrap();
120 
121         if !result.success() {
122             panic!("Test {name} forked with root by the trace_marker suite failed.");
123         }
124         return false;
125     }
126     true
127 }
128 
main()129 fn main() {
130     let args = libtest_mimic::Arguments {
131         // Force single-threaded execution to make sure there is no race condition between
132         // data written to the trace_marker file. In case the tracefs environment is being
133         // used by other processes on the system, these tests might fail.
134         test_threads: Some(1),
135         ..libtest_mimic::Arguments::from_args()
136     };
137 
138     let tests = vec![
139         libtest_mimic::Trial::test("trace_simple_print", move || {
140             if run_test_with_root("trace_simple_print") {
141                 setup();
142                 trace_simple_print();
143                 cleanup();
144             }
145             Ok(())
146         }),
147         libtest_mimic::Trial::test("push_descriptors", move || {
148             if run_test_with_root("push_descriptors") {
149                 setup();
150                 push_descriptors();
151                 cleanup();
152             }
153             Ok(())
154         }),
155     ];
156     libtest_mimic::run(&args, tests).exit();
157 }
158