Merge "libprefetch: Start prefetch service based on build" into main am: 3ffdc6206a am: 36252ab703

Original change: https://android-review.googlesource.com/c/platform/system/core/+/3368783

Change-Id: I91d8cfc9ee0a073d547e2faeedd1b835d320abf7
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
This commit is contained in:
Treehugger Robot 2024-11-23 07:34:56 +00:00 committed by Automerger Merge Worker
commit a13123fc59
6 changed files with 195 additions and 22 deletions

View file

@ -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

View file

@ -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(())
}

View file

@ -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(())
}

View file

@ -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<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.

View file

@ -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(())
}

View file

@ -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 {