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