diff --git a/init/libprefetch/prefetch/prefetch.rc b/init/libprefetch/prefetch/prefetch.rc index 9f2cb7f46..fb3fb3b6a 100644 --- a/init/libprefetch/prefetch/prefetch.rc +++ b/init/libprefetch/prefetch/prefetch.rc @@ -1,3 +1,16 @@ +on init && property:ro.prefetch_boot.enabled=true + start prefetch + +service prefetch /system/bin/prefetch start + class main + user root + group root system + disabled + oneshot + +on property:ro.prefetch_boot.record=true + start prefetch_record + service prefetch_record /system/bin/prefetch record --duration ${ro.prefetch_boot.duration_s:-0} class main user root @@ -5,6 +18,9 @@ service prefetch_record /system/bin/prefetch record --duration ${ro.prefetch_boo disabled oneshot +on property:ro.prefetch_boot.replay=true + start prefetch_replay + service prefetch_replay /system/bin/prefetch replay --io-depth ${ro.prefetch_boot.io_depth:-2} --max-fds ${ro.prefetch_boot.max_fds:-128} class main user root diff --git a/init/libprefetch/prefetch/src/arch/android.rs b/init/libprefetch/prefetch/src/arch/android.rs new file mode 100644 index 000000000..c765e38d7 --- /dev/null +++ b/init/libprefetch/prefetch/src/arch/android.rs @@ -0,0 +1,118 @@ +use crate::Error; +use crate::RecordArgs; +use crate::StartArgs; +use log::info; +use log::warn; +use std::fs::File; +use std::fs::OpenOptions; +use std::io::Write; +use std::time::Duration; + +use rustutils::system_properties::error::PropertyWatcherError; +use rustutils::system_properties::PropertyWatcher; + +const PREFETCH_RECORD_PROPERTY: &str = "ro.prefetch_boot.record"; +const PREFETCH_REPLAY_PROPERTY: &str = "ro.prefetch_boot.replay"; +const PREFETCH_RECORD_PROPERTY_STOP: &str = "ro.prefetch_boot.record_stop"; + +fn wait_for_property_true( + property_name: &str, + timeout: Option, +) -> Result<(), PropertyWatcherError> { + let mut prop = PropertyWatcher::new(property_name)?; + prop.wait_for_value("1", timeout)?; + Ok(()) +} + +/// Wait for record to stop +pub fn wait_for_record_stop() { + wait_for_property_true(PREFETCH_RECORD_PROPERTY_STOP, None).unwrap_or_else(|e| { + warn!("failed to wait for {} with error: {}", PREFETCH_RECORD_PROPERTY_STOP, e) + }); +} + +fn start_prefetch_service(property_name: &str) -> Result<(), Error> { + match rustutils::system_properties::write(property_name, "true") { + Ok(_) => {} + Err(_) => { + return Err(Error::Custom { error: "Failed to start prefetch service".to_string() }); + } + } + Ok(()) +} + +/// Start prefetch service +/// +/// 1: Check the presence of the file 'prefetch_ready'. If it doesn't +/// exist then the device is booting for the first time after wipe. +/// Thus, we would just create the file and exit as we do not want +/// to initiate the record after data wipe primiarly because boot +/// after data wipe is long and the I/O pattern during first boot may not actually match +/// with subsequent boot. +/// +/// 2: If the file 'prefetch_ready' is present: +/// +/// a: Compare the build-finger-print of the device with the one record format +/// is associated with by reading the file 'build_finger_print'. If they match, +/// start the prefetch_replay. +/// +/// b: If they don't match, then the device was updated through OTA. Hence, start +/// a fresh record and delete the build-finger-print file. This should also cover +/// the case of device rollback. +/// +/// c: If the build-finger-print file doesn't exist, then just restart the record +/// from scratch. +pub fn start_prefetch(args: &StartArgs) -> Result<(), Error> { + if !args.path.exists() { + match File::create(args.path.clone()) { + Ok(_) => {} + Err(_) => { + return Err(Error::Custom { error: "File Creation failed".to_string() }); + } + } + return Ok(()); + } + + if args.build_fingerprint_path.exists() { + let device_build_fingerprint = rustutils::system_properties::read("ro.build.fingerprint") + .map_err(|e| Error::Custom { + error: format!("Failed to read ro.build.fingerprint: {}", e), + })?; + let pack_build_fingerprint = std::fs::read_to_string(&args.build_fingerprint_path)?; + if pack_build_fingerprint.trim() == device_build_fingerprint.as_deref().unwrap_or_default() + { + info!("Start replay"); + start_prefetch_service(PREFETCH_REPLAY_PROPERTY)?; + } else { + info!("Start record"); + std::fs::remove_file(&args.build_fingerprint_path)?; + start_prefetch_service(PREFETCH_RECORD_PROPERTY)?; + } + } else { + info!("Start record"); + start_prefetch_service(PREFETCH_RECORD_PROPERTY)?; + } + Ok(()) +} + +/// Write build finger print to associate prefetch pack file +pub fn write_build_fingerprint(args: &RecordArgs) -> Result<(), Error> { + let mut build_fingerprint_file = OpenOptions::new() + .write(true) + .create(true) + .truncate(true) + .open(&args.build_fingerprint_path) + .map_err(|source| Error::Create { + source, + path: args.build_fingerprint_path.to_str().unwrap().to_owned(), + })?; + + let device_build_fingerprint = + rustutils::system_properties::read("ro.build.fingerprint").unwrap_or_default(); + let device_build_fingerprint = device_build_fingerprint.unwrap_or_default(); + + build_fingerprint_file.write_all(device_build_fingerprint.as_bytes())?; + build_fingerprint_file.sync_all()?; + + Ok(()) +} diff --git a/init/libprefetch/prefetch/src/args.rs b/init/libprefetch/prefetch/src/args.rs index 4c1e68919..e534210b5 100644 --- a/init/libprefetch/prefetch/src/args.rs +++ b/init/libprefetch/prefetch/src/args.rs @@ -25,6 +25,8 @@ use std::process::exit; pub use args_internal::OutputFormat; pub use args_internal::ReplayArgs; +#[cfg(target_os = "android")] +pub use args_internal::StartArgs; pub use args_internal::TracerType; pub use args_internal::{DumpArgs, MainArgs, RecordArgs, SubCommands}; use serde::Deserialize; @@ -66,6 +68,8 @@ fn verify_and_fix(args: &mut MainArgs) -> Result<(), Error> { SubCommands::Dump(arg) => { ensure_path_exists(&arg.path)?; } + #[cfg(target_os = "android")] + SubCommands::Start(_arg) => return Ok(()), } Ok(()) } diff --git a/init/libprefetch/prefetch/src/args/args_argh.rs b/init/libprefetch/prefetch/src/args/args_argh.rs index 8ac95fce7..65084eeaa 100644 --- a/init/libprefetch/prefetch/src/args/args_argh.rs +++ b/init/libprefetch/prefetch/src/args/args_argh.rs @@ -40,6 +40,38 @@ pub enum SubCommands { Replay(ReplayArgs), /// Dump prefetch data in human readable format Dump(DumpArgs), + /// Start prefetch service if possible + /// If the pack file is present, then prefetch replay is started + /// If the pack file is absent or if the build fingerprint + /// of the current pack file is different, then prefetch record is started. + #[cfg(target_os = "android")] + Start(StartArgs), +} + +#[cfg(target_os = "android")] +fn default_ready_path() -> PathBuf { + PathBuf::from("/metadata/prefetch/prefetch_ready") +} + +#[cfg(target_os = "android")] +fn default_build_finger_print_path() -> PathBuf { + PathBuf::from("/metadata/prefetch/build_finger_print") +} + +#[cfg(target_os = "android")] +#[derive(Eq, PartialEq, Debug, Default, FromArgs)] +/// Start prefetch service based on if pack file is present. +#[argh(subcommand, name = "start")] +pub struct StartArgs { + /// file path to check if prefetch_ready is present. + /// + /// A new file is created at the given path if it's not present. + #[argh(option, default = "default_ready_path()")] + pub path: PathBuf, + + /// file path where build fingerprint is stored + #[argh(option, default = "default_build_finger_print_path()")] + pub build_fingerprint_path: PathBuf, } impl Default for SubCommands { @@ -110,6 +142,11 @@ pub struct RecordArgs { from_str_fn(parse_tracing_instance) )] pub tracing_instance: Option, + + #[cfg(target_os = "android")] + /// store build_finger_print to tie the pack format + #[argh(option, default = "default_build_finger_print_path()")] + pub build_fingerprint_path: PathBuf, } /// Type of tracing subsystem to use. diff --git a/init/libprefetch/prefetch/src/lib.rs b/init/libprefetch/prefetch/src/lib.rs index 4b56b13ee..6564c4bc6 100644 --- a/init/libprefetch/prefetch/src/lib.rs +++ b/init/libprefetch/prefetch/src/lib.rs @@ -20,6 +20,10 @@ mod error; mod format; mod replay; mod tracer; +#[cfg(target_os = "android")] +mod arch { + pub mod android; +} use std::fs::File; use std::fs::OpenOptions; @@ -38,6 +42,8 @@ use log::LevelFilter; pub use args::args_from_env; use args::OutputFormat; pub use args::ReplayArgs; +#[cfg(target_os = "android")] +pub use args::StartArgs; pub use args::{DumpArgs, MainArgs, RecordArgs, SubCommands}; pub use error::Error; pub use format::FileId; @@ -45,29 +51,11 @@ pub use format::InodeInfo; pub use format::Record; pub use format::RecordsFile; use log::info; -#[cfg(target_os = "android")] -use log::warn; pub use replay::Replay; pub use tracer::nanoseconds_since_boot; #[cfg(target_os = "android")] -use rustutils::system_properties; -#[cfg(target_os = "android")] -use rustutils::system_properties::error::PropertyWatcherError; -#[cfg(target_os = "android")] -use rustutils::system_properties::PropertyWatcher; - -#[cfg(target_os = "android")] -fn wait_for_property_true(property_name: &str) -> Result<(), PropertyWatcherError> { - let mut prop = PropertyWatcher::new(property_name)?; - loop { - prop.wait(None)?; - if system_properties::read_bool(property_name, false)? { - break; - } - } - Ok(()) -} +pub use arch::android::*; /// Records prefetch data for the given configuration pub fn record(args: &RecordArgs) -> Result<(), Error> { @@ -85,11 +73,10 @@ pub fn record(args: &RecordArgs) -> Result<(), Error> { thread::sleep(duration); } else { #[cfg(target_os = "android")] - wait_for_property_true("sys.boot_completed").unwrap_or_else(|e| { - warn!("failed to wait for sys.boot_completed with error: {}", e) - }); + wait_for_record_stop(); } + info!("Prefetch record exiting"); // We want to unwrap here on failure to send this signal. Otherwise // tracer will continue generating huge records data. exit_tx.send(()).unwrap(); @@ -107,9 +94,16 @@ pub fn record(args: &RecordArgs) -> Result<(), Error> { std::fs::set_permissions(&args.path, std::fs::Permissions::from_mode(0o644)) .map_err(|source| Error::Create { source, path: args.path.to_str().unwrap().to_owned() })?; + // Write the record file out_file .write_all(&rf.add_checksum_and_serialize()?) .map_err(|source| Error::Write { path: args.path.to_str().unwrap().to_owned(), source })?; + out_file.sync_all()?; + + // Write build-finger-print file + #[cfg(target_os = "android")] + write_build_fingerprint(args)?; + Ok(()) } diff --git a/init/libprefetch/prefetch/src/main.rs b/init/libprefetch/prefetch/src/main.rs index 046e07eda..eab826f25 100644 --- a/init/libprefetch/prefetch/src/main.rs +++ b/init/libprefetch/prefetch/src/main.rs @@ -22,6 +22,8 @@ use prefetch_rs::dump; use prefetch_rs::init_logging; use prefetch_rs::record; use prefetch_rs::replay; +#[cfg(target_os = "android")] +use prefetch_rs::start_prefetch; use prefetch_rs::LogLevel; use prefetch_rs::MainArgs; use prefetch_rs::SubCommands; @@ -33,6 +35,8 @@ fn main() { SubCommands::Record(args) => record(args), SubCommands::Replay(args) => replay(args), SubCommands::Dump(args) => dump(args), + #[cfg(target_os = "android")] + SubCommands::Start(args) => start_prefetch(args), }; if let Err(err) = ret {