Merge "libprefetch: Start prefetch service based on build" into main
This commit is contained in:
commit
3ffdc6206a
6 changed files with 195 additions and 22 deletions
|
|
@ -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
|
||||
|
|
|
|||
118
init/libprefetch/prefetch/src/arch/android.rs
Normal file
118
init/libprefetch/prefetch/src/arch/android.rs
Normal 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(())
|
||||
}
|
||||
|
|
@ -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(())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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.
|
||||
|
|
|
|||
|
|
@ -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(())
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue