modem: Migrate dump_modem.sh to rust

Test: adb bugreport
Bug: 286755695
Flag: EXEMPT refactor
Change-Id: I69b147263d423594a51904970ce5af05eff3b03b
Signed-off-by: Mahesh Kallelil <kallelil@google.com>
diff --git a/modem/dump_modemlog/dump_modem.rs b/modem/dump_modemlog/dump_modem.rs
new file mode 100644
index 0000000..d9af7eb
--- /dev/null
+++ b/modem/dump_modemlog/dump_modem.rs
@@ -0,0 +1,109 @@
+// Copyright 2024 Google LLC
+
+//! The dump_modem binary is used to capture kernel/userspace logs in bugreport
+
+use std::fs;
+
+const MODEM_STAT: &str = "/data/vendor/modem_stat/debug.txt";
+const SSRDUMP_DIR: &str = "/data/vendor/ssrdump";
+const RFSD_ERR_LOG_DIR: &str = "/data/vendor/log/rfsd";
+const WAKEUP_EVENTS: &str = "/sys/devices/platform/cpif/wakeup_events";
+const CPIF_LOGBUFFER: &str = "/dev/logbuffer_cpif";
+const PCIE_EVENT_STATS: &str = "/sys/devices/platform/cpif/modem/pcie_event_stats";
+
+fn handle_io_error(file: &str, err: std::io::Error) {
+    match err.kind() {
+        std::io::ErrorKind::NotFound => println!("{file} not found!"),
+        std::io::ErrorKind::PermissionDenied => println!("Permission denied to access {file}"),
+        _ => println!("I/O error accessing {file}: {err}"),
+    }
+}
+
+fn print_file(file: &str) -> Result<(), std::io::Error> {
+    fs::metadata(file)?;
+
+    let data = fs::read_to_string(file)?;
+
+    if data.is_empty() {
+        println!("{file} is empty");
+    } else {
+        print!("{data}");
+    }
+
+    Ok(())
+}
+
+fn print_file_and_handle_error(file: &str) {
+    if let Err(err) = print_file(file) {
+        handle_io_error(file, err);
+    }
+}
+
+fn print_matching_files_in_dir(dir: &str, filename: &str) {
+    let Ok(entries) = fs::read_dir(dir) else {
+        return println!("Cannot open directory {dir}");
+    };
+
+    for entry in entries {
+        let Ok(entry) = entry else {
+            continue;
+        };
+        if entry.path().is_file() && entry.file_name().to_string_lossy().starts_with(filename) {
+            if let Some(path_str) = entry.path().to_str() {
+                println!("{}", path_str);
+                print_file_and_handle_error(path_str);
+            }
+        }
+    }
+}
+
+// Capture modem stat log if it exists
+fn modem_stat() {
+    println!("------ Modem Stat ------");
+    print_file_and_handle_error(MODEM_STAT);
+    println!();
+}
+
+// Capture crash signatures from all modem crashes
+fn modem_ssr_history() {
+    println!("------ Modem SSR history ------");
+    print_matching_files_in_dir(SSRDUMP_DIR, "crashinfo_modem");
+    println!();
+}
+
+// Capture rfsd error logs from all existing log files
+fn rfsd_error_log() {
+    println!("------ RFSD error log ------");
+    print_matching_files_in_dir(RFSD_ERR_LOG_DIR, "rfslog");
+    println!();
+}
+
+// Capture modem wakeup events if the sysfs attribute exists
+fn wakeup_events() {
+    println!("------ Wakeup event counts ------");
+    print_file_and_handle_error(WAKEUP_EVENTS);
+    println!();
+}
+
+// Capture kernel driver logbuffer if it exists
+fn cpif_logbuffer() {
+    println!("------ CPIF Logbuffer ------");
+    print_file_and_handle_error(CPIF_LOGBUFFER);
+    println!();
+}
+
+// Capture modem pcie stats if the sysfs attribute exists
+fn pcie_event_stats() {
+    println!("------ PCIe event stats ------");
+    print_file_and_handle_error(PCIE_EVENT_STATS);
+    println!();
+}
+
+fn main() {
+    modem_stat();
+    modem_ssr_history();
+    rfsd_error_log();
+    wakeup_events();
+    cpif_logbuffer();
+    pcie_event_stats();
+}