libprefetch: Start prefetch service based on build
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.
Bug: 362507272
Test: Prefetch record/replay
Change-Id: I90b861ba9381ddba6ab7dedb9930a735e55b0e5d
Signed-off-by: Akilesh Kailash <akailash@google.com>
diff --git a/init/libprefetch/prefetch/prefetch.rc b/init/libprefetch/prefetch/prefetch.rc
index 9f2cb7f..fb3fb3b 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 @@
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 0000000..c765e38
--- /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<Duration>,
+) -> 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 4c1e689..e534210 100644
--- a/init/libprefetch/prefetch/src/args.rs
+++ b/init/libprefetch/prefetch/src/args.rs
@@ -25,6 +25,8 @@
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 @@
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 8ac95fc..65084ee 100644
--- a/init/libprefetch/prefetch/src/args/args_argh.rs
+++ b/init/libprefetch/prefetch/src/args/args_argh.rs
@@ -40,6 +40,38 @@
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 @@
from_str_fn(parse_tracing_instance)
)]
pub tracing_instance: Option<String>,
+
+ #[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 4b56b13..6564c4b 100644
--- a/init/libprefetch/prefetch/src/lib.rs
+++ b/init/libprefetch/prefetch/src/lib.rs
@@ -20,6 +20,10 @@
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 @@
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::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 @@
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 @@
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 046e07e..eab826f 100644
--- a/init/libprefetch/prefetch/src/main.rs
+++ b/init/libprefetch/prefetch/src/main.rs
@@ -22,6 +22,8 @@
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 @@
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 {