xref: /aosp_15_r20/external/crosvm/e2e_tests/fixture/src/utils.rs (revision bb4ee6a4ae7042d18b07a98463b9c8b875e44b39)
1*bb4ee6a4SAndroid Build Coastguard Worker // Copyright 2022 The ChromiumOS Authors
2*bb4ee6a4SAndroid Build Coastguard Worker // Use of this source code is governed by a BSD-style license that can be
3*bb4ee6a4SAndroid Build Coastguard Worker // found in the LICENSE file.
4*bb4ee6a4SAndroid Build Coastguard Worker 
5*bb4ee6a4SAndroid Build Coastguard Worker //! Provides utility functions used by multiple fixture files.
6*bb4ee6a4SAndroid Build Coastguard Worker 
7*bb4ee6a4SAndroid Build Coastguard Worker use std::env;
8*bb4ee6a4SAndroid Build Coastguard Worker use std::io::ErrorKind;
9*bb4ee6a4SAndroid Build Coastguard Worker #[cfg(any(target_os = "android", target_os = "linux"))]
10*bb4ee6a4SAndroid Build Coastguard Worker use std::os::unix::process::ExitStatusExt;
11*bb4ee6a4SAndroid Build Coastguard Worker use std::path::Path;
12*bb4ee6a4SAndroid Build Coastguard Worker use std::path::PathBuf;
13*bb4ee6a4SAndroid Build Coastguard Worker use std::process::Command;
14*bb4ee6a4SAndroid Build Coastguard Worker use std::process::ExitStatus;
15*bb4ee6a4SAndroid Build Coastguard Worker use std::process::Output;
16*bb4ee6a4SAndroid Build Coastguard Worker use std::sync::mpsc::sync_channel;
17*bb4ee6a4SAndroid Build Coastguard Worker use std::sync::mpsc::RecvTimeoutError;
18*bb4ee6a4SAndroid Build Coastguard Worker use std::thread;
19*bb4ee6a4SAndroid Build Coastguard Worker use std::time::Duration;
20*bb4ee6a4SAndroid Build Coastguard Worker use std::time::SystemTime;
21*bb4ee6a4SAndroid Build Coastguard Worker 
22*bb4ee6a4SAndroid Build Coastguard Worker use anyhow::bail;
23*bb4ee6a4SAndroid Build Coastguard Worker use anyhow::Result;
24*bb4ee6a4SAndroid Build Coastguard Worker use tempfile::NamedTempFile;
25*bb4ee6a4SAndroid Build Coastguard Worker 
26*bb4ee6a4SAndroid Build Coastguard Worker use crate::sys::binary_name;
27*bb4ee6a4SAndroid Build Coastguard Worker use crate::vhost_user::CmdType;
28*bb4ee6a4SAndroid Build Coastguard Worker use crate::vhost_user::Config as VuConfig;
29*bb4ee6a4SAndroid Build Coastguard Worker 
30*bb4ee6a4SAndroid Build Coastguard Worker pub const DEFAULT_BLOCK_SIZE: u64 = 1024 * 1024;
31*bb4ee6a4SAndroid Build Coastguard Worker 
32*bb4ee6a4SAndroid Build Coastguard Worker /// Returns the path to the crosvm binary to be tested.
33*bb4ee6a4SAndroid Build Coastguard Worker ///
34*bb4ee6a4SAndroid Build Coastguard Worker /// The crosvm binary is expected to be alongside to the integration tests
35*bb4ee6a4SAndroid Build Coastguard Worker /// binary. Alternatively in the parent directory (cargo will put the
36*bb4ee6a4SAndroid Build Coastguard Worker /// test binary in target/debug/deps/ but the crosvm binary in target/debug)
find_crosvm_binary() -> PathBuf37*bb4ee6a4SAndroid Build Coastguard Worker pub fn find_crosvm_binary() -> PathBuf {
38*bb4ee6a4SAndroid Build Coastguard Worker     let binary_name = binary_name();
39*bb4ee6a4SAndroid Build Coastguard Worker     let exe_dir = env::current_exe().unwrap().parent().unwrap().to_path_buf();
40*bb4ee6a4SAndroid Build Coastguard Worker     let first = exe_dir.join(binary_name);
41*bb4ee6a4SAndroid Build Coastguard Worker     if first.exists() {
42*bb4ee6a4SAndroid Build Coastguard Worker         return first;
43*bb4ee6a4SAndroid Build Coastguard Worker     }
44*bb4ee6a4SAndroid Build Coastguard Worker     let second = exe_dir.parent().unwrap().join(binary_name);
45*bb4ee6a4SAndroid Build Coastguard Worker     if second.exists() {
46*bb4ee6a4SAndroid Build Coastguard Worker         return second;
47*bb4ee6a4SAndroid Build Coastguard Worker     }
48*bb4ee6a4SAndroid Build Coastguard Worker     panic!(
49*bb4ee6a4SAndroid Build Coastguard Worker         "Cannot find {} in ./ or ../ alongside test binary.",
50*bb4ee6a4SAndroid Build Coastguard Worker         binary_name
51*bb4ee6a4SAndroid Build Coastguard Worker     );
52*bb4ee6a4SAndroid Build Coastguard Worker }
53*bb4ee6a4SAndroid Build Coastguard Worker 
54*bb4ee6a4SAndroid Build Coastguard Worker /// Run the provided closure in a separate thread and return it's result. If the closure does not
55*bb4ee6a4SAndroid Build Coastguard Worker /// finish before the timeout is reached, an Error is returned instead.
56*bb4ee6a4SAndroid Build Coastguard Worker ///
57*bb4ee6a4SAndroid Build Coastguard Worker /// WARNING: It is not possible to kill the closure if a timeout occurs. It is advised to panic
58*bb4ee6a4SAndroid Build Coastguard Worker /// when an error is returned.
run_with_timeout<F, U>(closure: F, timeout: Duration) -> Result<U> where F: FnOnce() -> U + Send + 'static, U: Send + 'static,59*bb4ee6a4SAndroid Build Coastguard Worker pub fn run_with_timeout<F, U>(closure: F, timeout: Duration) -> Result<U>
60*bb4ee6a4SAndroid Build Coastguard Worker where
61*bb4ee6a4SAndroid Build Coastguard Worker     F: FnOnce() -> U + Send + 'static,
62*bb4ee6a4SAndroid Build Coastguard Worker     U: Send + 'static,
63*bb4ee6a4SAndroid Build Coastguard Worker {
64*bb4ee6a4SAndroid Build Coastguard Worker     run_with_status_check(closure, timeout, || false)
65*bb4ee6a4SAndroid Build Coastguard Worker }
66*bb4ee6a4SAndroid Build Coastguard Worker 
67*bb4ee6a4SAndroid Build Coastguard Worker /// Run the provided closure in a separate thread and return it's result. If the closure does not
68*bb4ee6a4SAndroid Build Coastguard Worker /// finish, continue_fn is called periodically with interval while continue_fn return true. Once
69*bb4ee6a4SAndroid Build Coastguard Worker /// continue_fn return false, an Error is returned instead.
70*bb4ee6a4SAndroid Build Coastguard Worker ///
71*bb4ee6a4SAndroid Build Coastguard Worker /// WARNING: It is not possible to kill the closure if a timeout occurs. It is advised to panic
72*bb4ee6a4SAndroid Build Coastguard Worker /// when an error is returned.
run_with_status_check<F, U, C>( closure: F, interval: Duration, mut continue_fn: C, ) -> Result<U> where F: FnOnce() -> U + Send + 'static, U: Send + 'static, C: FnMut() -> bool,73*bb4ee6a4SAndroid Build Coastguard Worker pub fn run_with_status_check<F, U, C>(
74*bb4ee6a4SAndroid Build Coastguard Worker     closure: F,
75*bb4ee6a4SAndroid Build Coastguard Worker     interval: Duration,
76*bb4ee6a4SAndroid Build Coastguard Worker     mut continue_fn: C,
77*bb4ee6a4SAndroid Build Coastguard Worker ) -> Result<U>
78*bb4ee6a4SAndroid Build Coastguard Worker where
79*bb4ee6a4SAndroid Build Coastguard Worker     F: FnOnce() -> U + Send + 'static,
80*bb4ee6a4SAndroid Build Coastguard Worker     U: Send + 'static,
81*bb4ee6a4SAndroid Build Coastguard Worker     C: FnMut() -> bool,
82*bb4ee6a4SAndroid Build Coastguard Worker {
83*bb4ee6a4SAndroid Build Coastguard Worker     let (tx, rx) = sync_channel::<()>(1);
84*bb4ee6a4SAndroid Build Coastguard Worker     let handle = thread::spawn(move || {
85*bb4ee6a4SAndroid Build Coastguard Worker         let result = closure();
86*bb4ee6a4SAndroid Build Coastguard Worker         // Notify main thread the closure is done. Fail silently if it's not listening anymore.
87*bb4ee6a4SAndroid Build Coastguard Worker         let _ = tx.send(());
88*bb4ee6a4SAndroid Build Coastguard Worker         result
89*bb4ee6a4SAndroid Build Coastguard Worker     });
90*bb4ee6a4SAndroid Build Coastguard Worker     loop {
91*bb4ee6a4SAndroid Build Coastguard Worker         match rx.recv_timeout(interval) {
92*bb4ee6a4SAndroid Build Coastguard Worker             Ok(_) => {
93*bb4ee6a4SAndroid Build Coastguard Worker                 return Ok(handle.join().unwrap());
94*bb4ee6a4SAndroid Build Coastguard Worker             }
95*bb4ee6a4SAndroid Build Coastguard Worker             Err(RecvTimeoutError::Timeout) => {
96*bb4ee6a4SAndroid Build Coastguard Worker                 if !continue_fn() {
97*bb4ee6a4SAndroid Build Coastguard Worker                     bail!("closure timed out");
98*bb4ee6a4SAndroid Build Coastguard Worker                 }
99*bb4ee6a4SAndroid Build Coastguard Worker             }
100*bb4ee6a4SAndroid Build Coastguard Worker             Err(RecvTimeoutError::Disconnected) => bail!("closure panicked"),
101*bb4ee6a4SAndroid Build Coastguard Worker         }
102*bb4ee6a4SAndroid Build Coastguard Worker     }
103*bb4ee6a4SAndroid Build Coastguard Worker }
104*bb4ee6a4SAndroid Build Coastguard Worker 
105*bb4ee6a4SAndroid Build Coastguard Worker #[derive(Debug)]
106*bb4ee6a4SAndroid Build Coastguard Worker pub enum CommandError {
107*bb4ee6a4SAndroid Build Coastguard Worker     IoError(std::io::Error),
108*bb4ee6a4SAndroid Build Coastguard Worker     ErrorCode(i32),
109*bb4ee6a4SAndroid Build Coastguard Worker     Signal(i32),
110*bb4ee6a4SAndroid Build Coastguard Worker }
111*bb4ee6a4SAndroid Build Coastguard Worker 
112*bb4ee6a4SAndroid Build Coastguard Worker /// Extension trait for utilities on std::process::Command
113*bb4ee6a4SAndroid Build Coastguard Worker pub trait CommandExt {
114*bb4ee6a4SAndroid Build Coastguard Worker     /// Same as Command::output() but will treat non-success status of the Command as an
115*bb4ee6a4SAndroid Build Coastguard Worker     /// error.
output_checked(&mut self) -> std::result::Result<Output, CommandError>116*bb4ee6a4SAndroid Build Coastguard Worker     fn output_checked(&mut self) -> std::result::Result<Output, CommandError>;
117*bb4ee6a4SAndroid Build Coastguard Worker 
118*bb4ee6a4SAndroid Build Coastguard Worker     /// Print the command to be executed
log(&mut self) -> &mut Self119*bb4ee6a4SAndroid Build Coastguard Worker     fn log(&mut self) -> &mut Self;
120*bb4ee6a4SAndroid Build Coastguard Worker }
121*bb4ee6a4SAndroid Build Coastguard Worker 
122*bb4ee6a4SAndroid Build Coastguard Worker impl CommandExt for Command {
output_checked(&mut self) -> std::result::Result<Output, CommandError>123*bb4ee6a4SAndroid Build Coastguard Worker     fn output_checked(&mut self) -> std::result::Result<Output, CommandError> {
124*bb4ee6a4SAndroid Build Coastguard Worker         let output = self.output().map_err(CommandError::IoError)?;
125*bb4ee6a4SAndroid Build Coastguard Worker         if !output.status.success() {
126*bb4ee6a4SAndroid Build Coastguard Worker             if let Some(code) = output.status.code() {
127*bb4ee6a4SAndroid Build Coastguard Worker                 return Err(CommandError::ErrorCode(code));
128*bb4ee6a4SAndroid Build Coastguard Worker             } else {
129*bb4ee6a4SAndroid Build Coastguard Worker                 #[cfg(any(target_os = "android", target_os = "linux"))]
130*bb4ee6a4SAndroid Build Coastguard Worker                 if let Some(signal) = output.status.signal() {
131*bb4ee6a4SAndroid Build Coastguard Worker                     return Err(CommandError::Signal(signal));
132*bb4ee6a4SAndroid Build Coastguard Worker                 }
133*bb4ee6a4SAndroid Build Coastguard Worker                 panic!("No error code and no signal should never happen.");
134*bb4ee6a4SAndroid Build Coastguard Worker             }
135*bb4ee6a4SAndroid Build Coastguard Worker         }
136*bb4ee6a4SAndroid Build Coastguard Worker         Ok(output)
137*bb4ee6a4SAndroid Build Coastguard Worker     }
138*bb4ee6a4SAndroid Build Coastguard Worker 
log(&mut self) -> &mut Self139*bb4ee6a4SAndroid Build Coastguard Worker     fn log(&mut self) -> &mut Self {
140*bb4ee6a4SAndroid Build Coastguard Worker         println!("$ {:?}", self);
141*bb4ee6a4SAndroid Build Coastguard Worker         self
142*bb4ee6a4SAndroid Build Coastguard Worker     }
143*bb4ee6a4SAndroid Build Coastguard Worker }
144*bb4ee6a4SAndroid Build Coastguard Worker 
145*bb4ee6a4SAndroid Build Coastguard Worker /// Extension trait for utilities on std::process::Child
146*bb4ee6a4SAndroid Build Coastguard Worker pub trait ChildExt {
147*bb4ee6a4SAndroid Build Coastguard Worker     /// Same as Child.wait(), but will return with an error after the specified timeout.
wait_with_timeout(&mut self, timeout: Duration) -> std::io::Result<Option<ExitStatus>>148*bb4ee6a4SAndroid Build Coastguard Worker     fn wait_with_timeout(&mut self, timeout: Duration) -> std::io::Result<Option<ExitStatus>>;
149*bb4ee6a4SAndroid Build Coastguard Worker }
150*bb4ee6a4SAndroid Build Coastguard Worker 
151*bb4ee6a4SAndroid Build Coastguard Worker impl ChildExt for std::process::Child {
wait_with_timeout(&mut self, timeout: Duration) -> std::io::Result<Option<ExitStatus>>152*bb4ee6a4SAndroid Build Coastguard Worker     fn wait_with_timeout(&mut self, timeout: Duration) -> std::io::Result<Option<ExitStatus>> {
153*bb4ee6a4SAndroid Build Coastguard Worker         let start_time = SystemTime::now();
154*bb4ee6a4SAndroid Build Coastguard Worker         while SystemTime::now().duration_since(start_time).unwrap() < timeout {
155*bb4ee6a4SAndroid Build Coastguard Worker             if let Ok(status) = self.try_wait() {
156*bb4ee6a4SAndroid Build Coastguard Worker                 return Ok(status);
157*bb4ee6a4SAndroid Build Coastguard Worker             }
158*bb4ee6a4SAndroid Build Coastguard Worker             thread::sleep(Duration::from_millis(10));
159*bb4ee6a4SAndroid Build Coastguard Worker         }
160*bb4ee6a4SAndroid Build Coastguard Worker         Err(std::io::Error::new(
161*bb4ee6a4SAndroid Build Coastguard Worker             ErrorKind::TimedOut,
162*bb4ee6a4SAndroid Build Coastguard Worker             "Timeout while waiting for child",
163*bb4ee6a4SAndroid Build Coastguard Worker         ))
164*bb4ee6a4SAndroid Build Coastguard Worker     }
165*bb4ee6a4SAndroid Build Coastguard Worker }
166*bb4ee6a4SAndroid Build Coastguard Worker 
167*bb4ee6a4SAndroid Build Coastguard Worker /// Calls the `closure` until it returns a non-error Result.
168*bb4ee6a4SAndroid Build Coastguard Worker /// If it has been re-tried `retries` times, the last result is returned.
retry<F, T, E>(mut closure: F, retries: usize) -> Result<T, E> where F: FnMut() -> Result<T, E>, E: std::fmt::Debug,169*bb4ee6a4SAndroid Build Coastguard Worker pub fn retry<F, T, E>(mut closure: F, retries: usize) -> Result<T, E>
170*bb4ee6a4SAndroid Build Coastguard Worker where
171*bb4ee6a4SAndroid Build Coastguard Worker     F: FnMut() -> Result<T, E>,
172*bb4ee6a4SAndroid Build Coastguard Worker     E: std::fmt::Debug,
173*bb4ee6a4SAndroid Build Coastguard Worker {
174*bb4ee6a4SAndroid Build Coastguard Worker     let mut attempts_left = retries + 1;
175*bb4ee6a4SAndroid Build Coastguard Worker     loop {
176*bb4ee6a4SAndroid Build Coastguard Worker         let result = closure();
177*bb4ee6a4SAndroid Build Coastguard Worker         attempts_left -= 1;
178*bb4ee6a4SAndroid Build Coastguard Worker         if result.is_ok() || attempts_left == 0 {
179*bb4ee6a4SAndroid Build Coastguard Worker             break result;
180*bb4ee6a4SAndroid Build Coastguard Worker         } else {
181*bb4ee6a4SAndroid Build Coastguard Worker             println!("Attempt failed: {:?}", result.err());
182*bb4ee6a4SAndroid Build Coastguard Worker         }
183*bb4ee6a4SAndroid Build Coastguard Worker     }
184*bb4ee6a4SAndroid Build Coastguard Worker }
185*bb4ee6a4SAndroid Build Coastguard Worker 
186*bb4ee6a4SAndroid Build Coastguard Worker /// Prepare a temporary ext4 disk file.
prepare_disk_img() -> NamedTempFile187*bb4ee6a4SAndroid Build Coastguard Worker pub fn prepare_disk_img() -> NamedTempFile {
188*bb4ee6a4SAndroid Build Coastguard Worker     let mut disk = NamedTempFile::new().unwrap();
189*bb4ee6a4SAndroid Build Coastguard Worker     disk.as_file_mut().set_len(DEFAULT_BLOCK_SIZE).unwrap();
190*bb4ee6a4SAndroid Build Coastguard Worker 
191*bb4ee6a4SAndroid Build Coastguard Worker     // Add /sbin and /usr/sbin to PATH since some distributions put mkfs.ext4 in one of those
192*bb4ee6a4SAndroid Build Coastguard Worker     // directories but don't add them to non-root PATH.
193*bb4ee6a4SAndroid Build Coastguard Worker     let path = env::var("PATH").unwrap();
194*bb4ee6a4SAndroid Build Coastguard Worker     let path = [&path, "/sbin", "/usr/sbin"].join(":");
195*bb4ee6a4SAndroid Build Coastguard Worker 
196*bb4ee6a4SAndroid Build Coastguard Worker     // TODO(b/243127910): Use `mkfs.ext4 -d` to include test data.
197*bb4ee6a4SAndroid Build Coastguard Worker     Command::new("mkfs.ext4")
198*bb4ee6a4SAndroid Build Coastguard Worker         .arg(disk.path().to_str().unwrap())
199*bb4ee6a4SAndroid Build Coastguard Worker         .env("PATH", path)
200*bb4ee6a4SAndroid Build Coastguard Worker         .output()
201*bb4ee6a4SAndroid Build Coastguard Worker         .expect("failed to execute process");
202*bb4ee6a4SAndroid Build Coastguard Worker     disk
203*bb4ee6a4SAndroid Build Coastguard Worker }
204*bb4ee6a4SAndroid Build Coastguard Worker 
create_vu_block_config(cmd_type: CmdType, socket: &Path, disk: &Path) -> VuConfig205*bb4ee6a4SAndroid Build Coastguard Worker pub fn create_vu_block_config(cmd_type: CmdType, socket: &Path, disk: &Path) -> VuConfig {
206*bb4ee6a4SAndroid Build Coastguard Worker     let socket_path = socket.to_str().unwrap();
207*bb4ee6a4SAndroid Build Coastguard Worker     let disk_path = disk.to_str().unwrap();
208*bb4ee6a4SAndroid Build Coastguard Worker     println!("disk={disk_path}, socket={socket_path}");
209*bb4ee6a4SAndroid Build Coastguard Worker     match cmd_type {
210*bb4ee6a4SAndroid Build Coastguard Worker         CmdType::Device => VuConfig::new(cmd_type, "block").extra_args(vec![
211*bb4ee6a4SAndroid Build Coastguard Worker             "block".to_string(),
212*bb4ee6a4SAndroid Build Coastguard Worker             "--socket-path".to_string(),
213*bb4ee6a4SAndroid Build Coastguard Worker             socket_path.to_string(),
214*bb4ee6a4SAndroid Build Coastguard Worker             "--file".to_string(),
215*bb4ee6a4SAndroid Build Coastguard Worker             disk_path.to_string(),
216*bb4ee6a4SAndroid Build Coastguard Worker         ]),
217*bb4ee6a4SAndroid Build Coastguard Worker         CmdType::Devices => VuConfig::new(cmd_type, "block").extra_args(vec![
218*bb4ee6a4SAndroid Build Coastguard Worker             "--block".to_string(),
219*bb4ee6a4SAndroid Build Coastguard Worker             format!("vhost={},path={}", socket_path, disk_path),
220*bb4ee6a4SAndroid Build Coastguard Worker         ]),
221*bb4ee6a4SAndroid Build Coastguard Worker     }
222*bb4ee6a4SAndroid Build Coastguard Worker }
223*bb4ee6a4SAndroid Build Coastguard Worker 
create_vu_console_multiport_config( socket: &Path, file_path: Vec<(PathBuf, PathBuf)>, ) -> VuConfig224*bb4ee6a4SAndroid Build Coastguard Worker pub fn create_vu_console_multiport_config(
225*bb4ee6a4SAndroid Build Coastguard Worker     socket: &Path,
226*bb4ee6a4SAndroid Build Coastguard Worker     file_path: Vec<(PathBuf, PathBuf)>,
227*bb4ee6a4SAndroid Build Coastguard Worker ) -> VuConfig {
228*bb4ee6a4SAndroid Build Coastguard Worker     let socket_path = socket.to_str().unwrap();
229*bb4ee6a4SAndroid Build Coastguard Worker 
230*bb4ee6a4SAndroid Build Coastguard Worker     let mut args = vec![
231*bb4ee6a4SAndroid Build Coastguard Worker         "console".to_string(),
232*bb4ee6a4SAndroid Build Coastguard Worker         "--socket-path".to_string(),
233*bb4ee6a4SAndroid Build Coastguard Worker         socket_path.to_string(),
234*bb4ee6a4SAndroid Build Coastguard Worker     ];
235*bb4ee6a4SAndroid Build Coastguard Worker 
236*bb4ee6a4SAndroid Build Coastguard Worker     for (i, (output_file, input_file)) in file_path.iter().enumerate() {
237*bb4ee6a4SAndroid Build Coastguard Worker         args.push("--port".to_string());
238*bb4ee6a4SAndroid Build Coastguard Worker         match input_file.file_name().is_some() {
239*bb4ee6a4SAndroid Build Coastguard Worker             true => {
240*bb4ee6a4SAndroid Build Coastguard Worker                 args.push(format!(
241*bb4ee6a4SAndroid Build Coastguard Worker                     "type=file,hardware=virtio-console,name=port{},path={},input={}",
242*bb4ee6a4SAndroid Build Coastguard Worker                     i,
243*bb4ee6a4SAndroid Build Coastguard Worker                     output_file.to_str().unwrap(),
244*bb4ee6a4SAndroid Build Coastguard Worker                     input_file.to_str().unwrap(),
245*bb4ee6a4SAndroid Build Coastguard Worker                 ));
246*bb4ee6a4SAndroid Build Coastguard Worker             }
247*bb4ee6a4SAndroid Build Coastguard Worker             false => {
248*bb4ee6a4SAndroid Build Coastguard Worker                 args.push(format!(
249*bb4ee6a4SAndroid Build Coastguard Worker                     "type=file,hardware=virtio-console,name=port{},path={}",
250*bb4ee6a4SAndroid Build Coastguard Worker                     i,
251*bb4ee6a4SAndroid Build Coastguard Worker                     output_file.to_str().unwrap(),
252*bb4ee6a4SAndroid Build Coastguard Worker                 ));
253*bb4ee6a4SAndroid Build Coastguard Worker             }
254*bb4ee6a4SAndroid Build Coastguard Worker         };
255*bb4ee6a4SAndroid Build Coastguard Worker     }
256*bb4ee6a4SAndroid Build Coastguard Worker     VuConfig::new(CmdType::Device, "console").extra_args(args)
257*bb4ee6a4SAndroid Build Coastguard Worker }
258