Skip to content

Commit 93a40cb

Browse files
committed
Enable dumping logs via streaming, cancellable via ctrl-c
1 parent 5b5b552 commit 93a40cb

File tree

3 files changed

+130
-17
lines changed

3 files changed

+130
-17
lines changed

src/adb.rs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1+
use crate::logs::LineBuffer;
12
use crate::{InstallerError, Result};
23
use adb_client::{ADBDeviceExt, ADBServer, ADBUSBDevice};
4+
use std::io::Write;
35
use std::net::{Ipv4Addr, SocketAddrV4};
46
use std::path::Path;
57

68
pub struct AdbManager {
7-
device: Box<dyn ADBDeviceExt>,
9+
device: Box<dyn ADBDeviceExt + Send>,
810
}
911

1012
impl AdbManager {
@@ -126,6 +128,18 @@ impl AdbManager {
126128
Ok(output_str.trim().to_string())
127129
}
128130

131+
pub fn shell_stream<T>(&mut self, command: &str, writer: T) -> Result<()>
132+
where
133+
T: Write,
134+
{
135+
let cmd_parts: Vec<&str> = command.split_whitespace().collect();
136+
let mut line_buffer = LineBuffer::new(writer);
137+
138+
self.device
139+
.shell_command(&cmd_parts, &mut line_buffer)
140+
.map_err(|e| InstallerError::Adb(format!("Failed to run shell command: {}", e)))
141+
}
142+
129143
pub async fn push_file(&mut self, local: &Path, remote: &str) -> Result<()> {
130144
let mut file = std::fs::File::open(local)
131145
.map_err(|e| InstallerError::Adb(format!("Failed to open file: {}", e)))?;

src/logs.rs

Lines changed: 113 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,130 @@
1+
use std::fs::File;
2+
use std::io::{self, Write};
13
use std::time::{SystemTime, UNIX_EPOCH};
24

3-
use tokio::fs;
5+
use tokio::signal::ctrl_c;
6+
use tokio::sync::watch::{self, Sender};
7+
use tokio::task::spawn_blocking;
48

5-
use crate::error::Result;
6-
use crate::AdbManager;
9+
use crate::{AdbManager, InstallerError};
710

8-
// TODO: Implement streaming
9-
pub async fn dump_logcat(stream: bool, remote_auth_url: Option<String>) -> Result<()> {
10-
let mut adb = AdbManager::connect(remote_auth_url).await?;
11+
// Taken from adb_client LogFilter
12+
pub struct LineBuffer<W: Write> {
13+
writer: W,
14+
buffer: Vec<u8>,
15+
}
16+
17+
impl<W: Write> LineBuffer<W> {
18+
pub fn new(writer: W) -> Self {
19+
LineBuffer {
20+
writer,
21+
buffer: Vec::new(),
22+
}
23+
}
24+
25+
fn should_write(&self, _line: &[u8]) -> bool {
26+
true
27+
}
28+
}
29+
30+
impl<W: Write> Write for LineBuffer<W> {
31+
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
32+
self.buffer.extend_from_slice(buf);
33+
34+
let buf_clone = self.buffer.clone();
35+
let mut lines = buf_clone.split_inclusive(|&byte| byte == b'\n').peekable();
36+
37+
while let Some(line) = lines.next() {
38+
if lines.peek().is_some() {
39+
if self.should_write(line) {
40+
self.writer.write_all(line)?;
41+
}
42+
} else {
43+
// This is the last (unfinished) element, we keep it for next round
44+
self.buffer = line.to_vec();
45+
break;
46+
}
47+
}
48+
49+
Ok(buf.len())
50+
}
1151

12-
let result = adb.shell("logcat -d").await?;
52+
fn flush(&mut self) -> io::Result<()> {
53+
self.writer.flush()
54+
}
55+
}
56+
57+
struct PrintFileWriter {
58+
file: File,
59+
line_count: usize,
60+
tx: Sender<usize>,
61+
}
62+
63+
impl Write for PrintFileWriter {
64+
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
65+
// This isn't correct if there needs to be a retry, but assume it just works
66+
let result = self.file.write(buf);
67+
if let Ok(string) = String::from_utf8(buf.into()) {
68+
println!("{string}");
69+
}
70+
self.line_count += 1;
71+
let _ = self.tx.send(self.line_count);
1372

14-
let line_count = result.split("\n").count();
73+
result
74+
}
1575

76+
fn flush(&mut self) -> io::Result<()> {
77+
todo!()
78+
}
79+
}
80+
81+
pub async fn dump_logcat_and_exit(stream: bool, remote_auth_url: Option<String>) {
1682
let timestamp = SystemTime::now()
1783
.duration_since(UNIX_EPOCH)
1884
.unwrap()
1985
.as_millis();
2086

2187
let filename = format!("penumbra_log_dump_{timestamp}.log");
2288

23-
fs::write(&filename, result).await?;
89+
let inner_filename = filename.clone();
90+
match tokio::spawn(async move {
91+
let mut adb = AdbManager::connect(remote_auth_url).await?;
92+
93+
let mut file = File::create(inner_filename)?;
94+
95+
if stream {
96+
let (tx, rx) = watch::channel(0);
97+
spawn_blocking(move || {
98+
let mut writer = PrintFileWriter {
99+
file,
100+
line_count: 0,
101+
tx,
102+
};
24103

25-
println!("Wrote {line_count} lines to {filename}");
104+
let _ = adb.shell_stream("logcat", &mut writer);
105+
});
26106

27-
Ok(())
107+
let _ = ctrl_c().await;
108+
let value = *rx.borrow();
109+
Ok::<usize, InstallerError>(value)
110+
} else {
111+
let result = adb.shell("logcat -d").await?;
112+
let line_count = result.split("\n").count();
113+
file.write_all(result.as_bytes())?;
114+
Ok(line_count)
115+
}
116+
})
117+
.await
118+
.unwrap()
119+
{
120+
Ok(line_count) => {
121+
println!("\n\nWrote {line_count} lines to {filename}");
122+
// Forcibly close process due to blocking call staying open
123+
std::process::exit(0);
124+
}
125+
Err(err) => {
126+
println!("Error: {err}");
127+
std::process::exit(1);
128+
}
129+
};
28130
}

src/main.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use std::path::PathBuf;
44
use tokio;
55

66
use penumbra_installer::{
7-
logs::dump_logcat, ConfigLoader, InstallationEngine, InstallerError, Result,
7+
logs::dump_logcat_and_exit, ConfigLoader, InstallationEngine, InstallerError, Result,
88
};
99

1010
#[derive(Parser)]
@@ -200,10 +200,7 @@ async fn main() -> Result<()> {
200200
Commands::DumpLogs {
201201
stream,
202202
remote_auth_url,
203-
} => match dump_logcat(stream, remote_auth_url).await {
204-
Ok(_) => {}
205-
Err(err) => println!("Dump error: {err}"),
206-
},
203+
} => dump_logcat_and_exit(stream, remote_auth_url).await,
207204
}
208205

209206
Ok(())

0 commit comments

Comments
 (0)