// Copyright 2023 The ChromiumOS Authors1// Use of this source code is governed by a BSD-style license that can be2// found in the LICENSE file.34use std::env;5use std::env::current_exe;6use std::fs::File;7use std::io::BufRead;8use std::io::BufReader;9use std::process::Command;1011use cros_tracing::*;1213const TRACE_FILE: &str = "/sys/kernel/tracing/trace";14const TRACE_CONTEXT_INFO: &str = "/sys/kernel/tracing/options/context-info";15const TRACING_ON: &str = "/sys/kernel/tracing/tracing_on";1617fn setup() {18// Make sure tracing is enabled.19std::fs::write(TRACING_ON, b"1").unwrap();20// Remove extra noise from trace file for easier parsing21std::fs::write(TRACE_CONTEXT_INFO, b"0").unwrap();22// Clear the trace backlog by writing an empty string, in case we have extra23// rogue messages in the trace buffer24std::fs::write(TRACE_FILE, b"").unwrap();2526init();27}2829fn cleanup() {30// Stop tracing.31std::fs::write(TRACING_ON, b"0").unwrap();32// Reset trace file format back to how it was.33std::fs::write(TRACE_CONTEXT_INFO, b"1").unwrap();34}3536fn trace_simple_print() {37let reader = BufReader::new(File::open(TRACE_FILE).ok().unwrap());3839let message1 = "Simple print test one";40let message2 = "Simple print test two";4142trace_simple_print!("{message1}");43trace_simple_print!("{message2}");4445// Read contents of the file, skip the first two lines which are just a preamble.46let mut lines = reader.lines().map(|l| l.unwrap()).skip(2);4748// Check the printed lines are in order.49// We need to use contains instead of matching the full string because each time we50// print to trace_marker we will get unique data like PID and timestamps that we cannot51// rely on, but the contents of the message itself should always contain our string.52assert!(lines.next().unwrap().contains(message1));53assert!(lines.next().unwrap().contains(message2));54}5556fn push_descriptors() {57let mut keep_rds = Vec::new();5859push_descriptors!(&mut keep_rds);6061// We cannot know the fd of the trace marker file beforehand but we can check if there62// is only one fd in the vector it means we can assume it's the trace_marker one.63assert_eq!(keep_rds.len(), 1);64}6566/// Executes the individual test `name` with root, in the same environment as the test suite,67/// if it does not already have root privileges. Sudo needs to be set up to run passwordless68/// or have cached credentials. The parent process spawns a child that runs with higher privileges69/// for that individual `name` test, and then waits for its completion.70///71/// Returns `true` if the test suite already has root privilege, in which case it72/// proceeds to run the test without forking, otherwise it returns `false` to let the73/// test suite know that the test was run by the child process instead.74///75/// # Arguments76///77/// * `name` - Name of the individual test to execute as root78///79/// # Examples80///81/// ```82/// libtest_mimic::Trial::test("test_with_root", move || {83/// if run_test_with_root("test_with_root") {84/// // This part only executes with root85/// function_to_test();86/// }87/// Ok(())88/// });89/// ```90fn run_test_with_root(name: &str) -> bool {91// This test needs to run as root, so if we aren't root we need to re-execute ourselves92// with sudo.93let is_root = match env::var("USER") {94Ok(val) => val == "root",95Err(_) => false,96};9798if !is_root {99let can_sudo = Command::new("sudo")100.args(["--askpass", "true"])101.env("SUDO_ASKPASS", "false")102.output()103.unwrap();104if !can_sudo.status.success() {105panic!("This test needs to be run as root or with passwordless sudo.");106}107108let result = Command::new("sudo")109.args([110"--preserve-env",111current_exe().unwrap().to_str().unwrap(),112"--nocapture",113"--ignored",114"--exact",115name,116])117.status()118.unwrap();119120if !result.success() {121panic!("Test {name} forked with root by the trace_marker suite failed.");122}123return false;124}125true126}127128fn main() {129let args = libtest_mimic::Arguments {130// Force single-threaded execution to make sure there is no race condition between131// data written to the trace_marker file. In case the tracefs environment is being132// used by other processes on the system, these tests might fail.133test_threads: Some(1),134..libtest_mimic::Arguments::from_args()135};136137let tests = vec![138libtest_mimic::Trial::test("trace_simple_print", move || {139if run_test_with_root("trace_simple_print") {140setup();141trace_simple_print();142cleanup();143}144Ok(())145}),146libtest_mimic::Trial::test("push_descriptors", move || {147if run_test_with_root("push_descriptors") {148setup();149push_descriptors();150cleanup();151}152Ok(())153}),154];155libtest_mimic::run(&args, tests).exit();156}157158159