| // Copyright 2023 The ChromiumOS Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| use std::env; |
| use std::env::current_exe; |
| use std::fs::File; |
| use std::io::BufRead; |
| use std::io::BufReader; |
| use std::process::Command; |
| |
| use cros_tracing::*; |
| |
| const TRACE_FILE: &str = "/sys/kernel/tracing/trace"; |
| const TRACE_CONTEXT_INFO: &str = "/sys/kernel/tracing/options/context-info"; |
| const TRACING_ON: &str = "/sys/kernel/tracing/tracing_on"; |
| |
| fn setup() { |
| // Make sure tracing is enabled. |
| std::fs::write(TRACING_ON, b"1").unwrap(); |
| // Remove extra noise from trace file for easier parsing |
| std::fs::write(TRACE_CONTEXT_INFO, b"0").unwrap(); |
| // Clear the trace backlog by writing an empty string, in case we have extra |
| // rogue messages in the trace buffer |
| std::fs::write(TRACE_FILE, b"").unwrap(); |
| |
| init(); |
| } |
| |
| fn cleanup() { |
| // Stop tracing. |
| std::fs::write(TRACING_ON, b"0").unwrap(); |
| // Reset trace file format back to how it was. |
| std::fs::write(TRACE_CONTEXT_INFO, b"1").unwrap(); |
| } |
| |
| fn trace_simple_print() { |
| let reader = BufReader::new(File::open(TRACE_FILE).ok().unwrap()); |
| |
| let message1 = "Simple print test one"; |
| let message2 = "Simple print test two"; |
| |
| trace_simple_print!("{message1}"); |
| trace_simple_print!("{message2}"); |
| |
| // Read contents of the file, skip the first two lines which are just a preamble. |
| let mut lines = reader.lines().map(|l| l.unwrap()).skip(2); |
| |
| // Check the printed lines are in order. |
| // We need to use contains instead of matching the full string because each time we |
| // print to trace_marker we will get unique data like PID and timestamps that we cannot |
| // rely on, but the contents of the message itself should always contain our string. |
| assert!(lines.next().unwrap().contains(message1)); |
| assert!(lines.next().unwrap().contains(message2)); |
| } |
| |
| fn push_descriptors() { |
| let mut keep_rds = Vec::new(); |
| |
| push_descriptors!(&mut keep_rds); |
| |
| // We cannot know the fd of the trace marker file beforehand but we can check if there |
| // is only one fd in the vector it means we can assume it's the trace_marker one. |
| assert_eq!(keep_rds.len(), 1); |
| } |
| |
| /// Executes the individual test `name` with root, in the same environment as the test suite, |
| /// if it does not already have root privileges. Sudo needs to be set up to run passwordless |
| /// or have cached credentials. The parent process spawns a child that runs with higher privileges |
| /// for that individual `name` test, and then waits for its completion. |
| /// |
| /// Returns `true` if the test suite already has root privilege, in which case it |
| /// proceeds to run the test without forking, otherwise it returns `false` to let the |
| /// test suite know that the test was run by the child process instead. |
| /// |
| /// # Arguments |
| /// |
| /// * `name` - Name of the individual test to execute as root |
| /// |
| /// # Examples |
| /// |
| /// ``` |
| /// libtest_mimic::Trial::test("test_with_root", move || { |
| /// if run_test_with_root("test_with_root") { |
| /// // This part only executes with root |
| /// function_to_test(); |
| /// } |
| /// Ok(()) |
| /// }); |
| /// ``` |
| fn run_test_with_root(name: &str) -> bool { |
| // This test needs to run as root, so if we aren't root we need to re-execute ourselves |
| // with sudo. |
| let is_root = match env::var("USER") { |
| Ok(val) => val == "root", |
| Err(_) => false, |
| }; |
| |
| if !is_root { |
| let can_sudo = Command::new("sudo") |
| .args(["--askpass", "true"]) |
| .env("SUDO_ASKPASS", "false") |
| .output() |
| .unwrap(); |
| if !can_sudo.status.success() { |
| panic!("This test needs to be run as root or with passwordless sudo."); |
| } |
| |
| let result = Command::new("sudo") |
| .args([ |
| "--preserve-env", |
| current_exe().unwrap().to_str().unwrap(), |
| "--nocapture", |
| "--ignored", |
| "--exact", |
| name, |
| ]) |
| .status() |
| .unwrap(); |
| |
| if !result.success() { |
| panic!("Test {name} forked with root by the trace_marker suite failed."); |
| } |
| return false; |
| } |
| true |
| } |
| |
| fn main() { |
| let args = libtest_mimic::Arguments { |
| // Force single-threaded execution to make sure there is no race condition between |
| // data written to the trace_marker file. In case the tracefs environment is being |
| // used by other processes on the system, these tests might fail. |
| test_threads: Some(1), |
| ..libtest_mimic::Arguments::from_args() |
| }; |
| |
| let tests = vec![ |
| libtest_mimic::Trial::test("trace_simple_print", move || { |
| if run_test_with_root("trace_simple_print") { |
| setup(); |
| trace_simple_print(); |
| cleanup(); |
| } |
| Ok(()) |
| }), |
| libtest_mimic::Trial::test("push_descriptors", move || { |
| if run_test_with_root("push_descriptors") { |
| setup(); |
| push_descriptors(); |
| cleanup(); |
| } |
| Ok(()) |
| }), |
| ]; |
| libtest_mimic::run(&args, tests).exit(); |
| } |