• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (C) 2024 The Android Open Source Project
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //      http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 //! A library to prefetch files on the file system to optimize startup times
16 //!
17 
18 mod args;
19 mod error;
20 mod format;
21 mod replay;
22 mod tracer;
23 #[cfg(target_os = "android")]
24 mod arch {
25     pub mod android;
26 }
27 
28 use std::fs::File;
29 use std::fs::OpenOptions;
30 use std::io;
31 use std::io::Write;
32 use std::os::unix::fs::PermissionsExt;
33 use std::string::ToString;
34 use std::thread;
35 use std::time::Duration;
36 
37 #[cfg(target_os = "android")]
38 use log::Level;
39 #[cfg(target_os = "linux")]
40 use log::LevelFilter;
41 
42 pub use args::args_from_env;
43 use args::OutputFormat;
44 pub use args::ReplayArgs;
45 pub use args::{DumpArgs, MainArgs, RecordArgs, SubCommands};
46 pub use error::Error;
47 pub use format::FileId;
48 pub use format::InodeInfo;
49 pub use format::Record;
50 pub use format::RecordsFile;
51 use log::info;
52 pub use replay::Replay;
53 pub use tracer::nanoseconds_since_boot;
54 
55 #[cfg(target_os = "android")]
56 pub use arch::android::*;
57 
58 /// Records prefetch data for the given configuration
record(args: &RecordArgs) -> Result<(), Error>59 pub fn record(args: &RecordArgs) -> Result<(), Error> {
60     #[cfg(target_os = "android")]
61     if !ensure_record_is_ready(&args.ready_path, &args.path, &args.build_fingerprint_path)? {
62         info!("Cannot perform record -- skipping");
63         return Ok(());
64     }
65 
66     info!("Starting record.");
67     let (mut tracer, exit_tx) = tracer::Tracer::create(
68         args.trace_buffer_size_kib,
69         args.tracing_subsystem.clone(),
70         args.tracing_instance.clone(),
71         args.setup_tracing,
72     )?;
73     let duration = Duration::from_secs(args.duration as u64);
74 
75     let thd = thread::spawn(move || {
76         if !duration.is_zero() {
77             info!("Record start - waiting for duration: {:?}", duration);
78             thread::sleep(duration);
79         } else {
80             #[cfg(target_os = "android")]
81             wait_for_record_stop();
82         }
83 
84         info!("Prefetch record exiting");
85         // We want to unwrap here on failure to send this signal. Otherwise
86         // tracer will continue generating huge records data.
87         exit_tx.send(()).unwrap();
88     });
89 
90     let mut rf = tracer.trace(args.int_path.as_ref())?;
91     thd.join()
92         .map_err(|_| Error::ThreadPool { error: "Failed to join timeout thread".to_string() })?;
93 
94     let mut out_file =
95         OpenOptions::new().write(true).create(true).truncate(true).open(&args.path).map_err(
96             |source| Error::Create { source, path: args.path.to_str().unwrap().to_owned() },
97         )?;
98 
99     std::fs::set_permissions(&args.path, std::fs::Permissions::from_mode(0o644))
100         .map_err(|source| Error::Create { source, path: args.path.to_str().unwrap().to_owned() })?;
101 
102     // Write the record file
103     out_file
104         .write_all(&rf.add_checksum_and_serialize()?)
105         .map_err(|source| Error::Write { path: args.path.to_str().unwrap().to_owned(), source })?;
106     out_file.sync_all()?;
107 
108     // Write build-finger-print file
109     #[cfg(target_os = "android")]
110     write_build_fingerprint(args)?;
111 
112     Ok(())
113 }
114 
115 /// Replays prefetch data for the given configuration
replay(args: &ReplayArgs) -> Result<(), Error>116 pub fn replay(args: &ReplayArgs) -> Result<(), Error> {
117     #[cfg(target_os = "android")]
118     if !can_perform_replay(&args.path, &args.build_fingerprint_path)? {
119         info!("Cannot perform replay -- exiting.");
120         return Ok(());
121     }
122 
123     info!("Starting replay.");
124     let replay = Replay::new(args)?;
125     replay.replay()
126 }
127 
128 /// Dumps prefetch data in the human readable form
dump(args: &DumpArgs) -> Result<(), Error>129 pub fn dump(args: &DumpArgs) -> Result<(), Error> {
130     let reader = File::open(&args.path)
131         .map_err(|source| Error::Open { source, path: args.path.to_str().unwrap().to_string() })?;
132     let rf: RecordsFile =
133         serde_cbor::from_reader(reader).map_err(|e| Error::Deserialize { error: e.to_string() })?;
134     match args.format {
135         OutputFormat::Json => println!(
136             "{:#}",
137             serde_json::to_string_pretty(&rf)
138                 .map_err(|e| Error::Serialize { error: e.to_string() })?
139         ),
140         OutputFormat::Csv => rf.serialize_records_to_csv(&mut io::stdout())?,
141     }
142     Ok(())
143 }
144 
145 /// An alias of android_logger::Level to use log level across android and linux.
146 #[cfg(target_os = "android")]
147 pub type LogLevel = Level;
148 
149 /// An alias of log::LevelFilter to use log level across android and linux.
150 #[cfg(not(target_os = "android"))]
151 pub type LogLevel = LevelFilter;
152 
153 /// Convenience logging initializer that is shared between the prefetch tool and c wrapper library
154 #[cfg(target_os = "android")]
init_logging(_level: LogLevel)155 pub fn init_logging(_level: LogLevel) {
156     android_logger::init_once(
157         android_logger::Config::default().with_max_level(log::LevelFilter::Info).format(
158             |f, record| {
159                 write!(
160                     f,
161                     "{} prefetch_rs: {}:{} {}: {}",
162                     nanoseconds_since_boot(),
163                     record.file().unwrap_or("unknown_file"),
164                     record.line().unwrap_or(0),
165                     record.level(),
166                     record.args()
167                 )
168             },
169         ),
170     )
171 }
172 
173 /// Convenience logging initializer that is shared between the prefetch tool and c wrapper library
174 #[cfg(target_os = "linux")]
init_logging(level: LogLevel)175 pub fn init_logging(level: LogLevel) {
176     let mut builder = env_logger::Builder::from_default_env();
177 
178     builder
179         .filter(None, level)
180         .format(|buf, record| {
181             writeln!(
182                 buf,
183                 "{} prefetch_rs: {}:{} {}: {}",
184                 nanoseconds_since_boot(),
185                 record.file().unwrap_or("unknown_file"),
186                 record.line().unwrap_or(0),
187                 record.level(),
188                 record.args()
189             )
190         })
191         .init();
192 }
193