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>
This commit is contained in:
parent
e400d09c83
commit
ef3a2c05fe
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}
|
service prefetch_record /system/bin/prefetch record --duration ${ro.prefetch_boot.duration_s:-0}
|
||||||
class main
|
class main
|
||||||
user root
|
user root
|
||||||
|
|
@ -5,6 +18,9 @@ service prefetch_record /system/bin/prefetch record --duration ${ro.prefetch_boo
|
||||||
disabled
|
disabled
|
||||||
oneshot
|
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}
|
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
|
class main
|
||||||
user root
|
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::OutputFormat;
|
||||||
pub use args_internal::ReplayArgs;
|
pub use args_internal::ReplayArgs;
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
pub use args_internal::StartArgs;
|
||||||
pub use args_internal::TracerType;
|
pub use args_internal::TracerType;
|
||||||
pub use args_internal::{DumpArgs, MainArgs, RecordArgs, SubCommands};
|
pub use args_internal::{DumpArgs, MainArgs, RecordArgs, SubCommands};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
@ -66,6 +68,8 @@ fn verify_and_fix(args: &mut MainArgs) -> Result<(), Error> {
|
||||||
SubCommands::Dump(arg) => {
|
SubCommands::Dump(arg) => {
|
||||||
ensure_path_exists(&arg.path)?;
|
ensure_path_exists(&arg.path)?;
|
||||||
}
|
}
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
SubCommands::Start(_arg) => return Ok(()),
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,38 @@ pub enum SubCommands {
|
||||||
Replay(ReplayArgs),
|
Replay(ReplayArgs),
|
||||||
/// Dump prefetch data in human readable format
|
/// Dump prefetch data in human readable format
|
||||||
Dump(DumpArgs),
|
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 {
|
impl Default for SubCommands {
|
||||||
|
|
@ -110,6 +142,11 @@ pub struct RecordArgs {
|
||||||
from_str_fn(parse_tracing_instance)
|
from_str_fn(parse_tracing_instance)
|
||||||
)]
|
)]
|
||||||
pub tracing_instance: Option<String>,
|
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.
|
/// Type of tracing subsystem to use.
|
||||||
|
|
|
||||||
|
|
@ -20,6 +20,10 @@ mod error;
|
||||||
mod format;
|
mod format;
|
||||||
mod replay;
|
mod replay;
|
||||||
mod tracer;
|
mod tracer;
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
mod arch {
|
||||||
|
pub mod android;
|
||||||
|
}
|
||||||
|
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::fs::OpenOptions;
|
use std::fs::OpenOptions;
|
||||||
|
|
@ -38,6 +42,8 @@ use log::LevelFilter;
|
||||||
pub use args::args_from_env;
|
pub use args::args_from_env;
|
||||||
use args::OutputFormat;
|
use args::OutputFormat;
|
||||||
pub use args::ReplayArgs;
|
pub use args::ReplayArgs;
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
pub use args::StartArgs;
|
||||||
pub use args::{DumpArgs, MainArgs, RecordArgs, SubCommands};
|
pub use args::{DumpArgs, MainArgs, RecordArgs, SubCommands};
|
||||||
pub use error::Error;
|
pub use error::Error;
|
||||||
pub use format::FileId;
|
pub use format::FileId;
|
||||||
|
|
@ -45,29 +51,11 @@ pub use format::InodeInfo;
|
||||||
pub use format::Record;
|
pub use format::Record;
|
||||||
pub use format::RecordsFile;
|
pub use format::RecordsFile;
|
||||||
use log::info;
|
use log::info;
|
||||||
#[cfg(target_os = "android")]
|
|
||||||
use log::warn;
|
|
||||||
pub use replay::Replay;
|
pub use replay::Replay;
|
||||||
pub use tracer::nanoseconds_since_boot;
|
pub use tracer::nanoseconds_since_boot;
|
||||||
|
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
use rustutils::system_properties;
|
pub use arch::android::*;
|
||||||
#[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(())
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Records prefetch data for the given configuration
|
/// Records prefetch data for the given configuration
|
||||||
pub fn record(args: &RecordArgs) -> Result<(), Error> {
|
pub fn record(args: &RecordArgs) -> Result<(), Error> {
|
||||||
|
|
@ -85,11 +73,10 @@ pub fn record(args: &RecordArgs) -> Result<(), Error> {
|
||||||
thread::sleep(duration);
|
thread::sleep(duration);
|
||||||
} else {
|
} else {
|
||||||
#[cfg(target_os = "android")]
|
#[cfg(target_os = "android")]
|
||||||
wait_for_property_true("sys.boot_completed").unwrap_or_else(|e| {
|
wait_for_record_stop();
|
||||||
warn!("failed to wait for sys.boot_completed with error: {}", e)
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
info!("Prefetch record exiting");
|
||||||
// We want to unwrap here on failure to send this signal. Otherwise
|
// We want to unwrap here on failure to send this signal. Otherwise
|
||||||
// tracer will continue generating huge records data.
|
// tracer will continue generating huge records data.
|
||||||
exit_tx.send(()).unwrap();
|
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))
|
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() })?;
|
.map_err(|source| Error::Create { source, path: args.path.to_str().unwrap().to_owned() })?;
|
||||||
|
|
||||||
|
// Write the record file
|
||||||
out_file
|
out_file
|
||||||
.write_all(&rf.add_checksum_and_serialize()?)
|
.write_all(&rf.add_checksum_and_serialize()?)
|
||||||
.map_err(|source| Error::Write { path: args.path.to_str().unwrap().to_owned(), source })?;
|
.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(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@ use prefetch_rs::dump;
|
||||||
use prefetch_rs::init_logging;
|
use prefetch_rs::init_logging;
|
||||||
use prefetch_rs::record;
|
use prefetch_rs::record;
|
||||||
use prefetch_rs::replay;
|
use prefetch_rs::replay;
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
use prefetch_rs::start_prefetch;
|
||||||
use prefetch_rs::LogLevel;
|
use prefetch_rs::LogLevel;
|
||||||
use prefetch_rs::MainArgs;
|
use prefetch_rs::MainArgs;
|
||||||
use prefetch_rs::SubCommands;
|
use prefetch_rs::SubCommands;
|
||||||
|
|
@ -33,6 +35,8 @@ fn main() {
|
||||||
SubCommands::Record(args) => record(args),
|
SubCommands::Record(args) => record(args),
|
||||||
SubCommands::Replay(args) => replay(args),
|
SubCommands::Replay(args) => replay(args),
|
||||||
SubCommands::Dump(args) => dump(args),
|
SubCommands::Dump(args) => dump(args),
|
||||||
|
#[cfg(target_os = "android")]
|
||||||
|
SubCommands::Start(args) => start_prefetch(args),
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Err(err) = ret {
|
if let Err(err) = ret {
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue