virtual_console_lib/
log.rs1use crate::terminal::Terminal;
6use anyhow::Error;
7use fuchsia_async::{self as fasync, OnSignals};
8use std::io::sink;
9use term_model::ansi::Processor;
10use term_model::event::EventListener;
11use zx::{self as zx, AsHandleRef};
12
13pub trait LogClient: 'static + Clone {
14 type Listener;
15
16 fn create_terminal(&self, id: u32, title: String) -> Result<Terminal<Self::Listener>, Error>;
17 fn request_update(&self, id: u32);
18}
19
20pub struct Log;
21
22impl Log {
23 pub fn start<T: LogClient>(
24 read_only_debuglog: zx::DebugLog,
25 client: &T,
26 id: u32,
27 ) -> Result<(), Error>
28 where
29 <T as LogClient>::Listener: EventListener,
30 {
31 let client = client.clone();
32 let terminal =
33 client.create_terminal(id, "debuglog".to_string()).expect("failed to create terminal");
34 let term = terminal.clone_term();
35
36 let proc_koid =
38 fuchsia_runtime::process_self().get_koid().expect("failed to get koid for process");
39
40 fasync::Task::local(async move {
41 let mut sink = sink();
42 let mut parser = Processor::new();
43
44 loop {
45 let on_signal = OnSignals::new(&read_only_debuglog, zx::Signals::LOG_READABLE);
46 on_signal.await.expect("failed to wait for log readable");
47
48 loop {
49 match read_only_debuglog.read() {
50 Ok(record) => {
51 if record.pid == proc_koid {
53 continue;
54 }
55
56 let mut term = term.borrow_mut();
57
58 let prefix = format!(
60 "\u{001b}[32m{:05}.{:03}\u{001b}[39m] \u{001b}[31m{:05}.\u{001b}[36m{:05}\u{001b}[39m> ",
61 record.timestamp.into_nanos() / 1_000_000_000,
62 (record.timestamp.into_nanos() / 1_000_000) % 1_000,
63 record.pid.raw_koid(),
64 record.tid.raw_koid(),
65 );
66 for byte in prefix.as_bytes() {
67 parser.advance(&mut *term, *byte, &mut sink);
68 }
69
70 let mut record_data = record.data();
72 if record_data.last() == Some(&b'\n') {
73 record_data = &record_data[..record_data.len() - 1];
74 }
75
76 for byte in record_data.iter() {
78 parser.advance(&mut *term, *byte, &mut sink);
79 }
80
81 for byte in "\r\n".as_bytes() {
83 parser.advance(&mut *term, *byte, &mut sink);
84 }
85
86 client.request_update(id);
88 }
89 Err(status) if status == zx::Status::SHOULD_WAIT => {
90 break;
91 }
92 Err(_) => {
93 let mut term = term.borrow_mut();
94 for byte in "\r\n<<LOG ERROR>>".as_bytes() {
95 parser.advance(&mut *term, *byte, &mut sink);
96 }
97
98 client.request_update(id);
100 break;
101 }
102 }
103 }
104 }
105 })
106 .detach();
107
108 Ok(())
109 }
110}
111
112#[cfg(test)]
113mod tests {
114 use super::*;
115 use crate::colors::ColorScheme;
116 use fuchsia_async as fasync;
117 use term_model::event::Event;
118
119 #[derive(Default)]
120 struct TestListener;
121
122 impl EventListener for TestListener {
123 fn send_event(&self, _event: Event) {}
124 }
125
126 #[derive(Default, Clone)]
127 struct TestLogClient;
128
129 impl LogClient for TestLogClient {
130 type Listener = TestListener;
131
132 fn create_terminal(
133 &self,
134 _id: u32,
135 title: String,
136 ) -> Result<Terminal<Self::Listener>, Error> {
137 Ok(Terminal::new(TestListener::default(), title, ColorScheme::default(), 1024, None))
138 }
139 fn request_update(&self, _id: u32) {}
140 }
141
142 #[fasync::run_singlethreaded(test)]
143 async fn can_start_log() -> Result<(), Error> {
144 let resource = zx::Resource::from(zx::Handle::invalid());
145 let debuglog = zx::DebugLog::create(&resource, zx::DebugLogOpts::empty()).unwrap();
146 let client = TestLogClient::default();
147 let _ = Log::start(debuglog, &client, 0)?;
148 Ok(())
149 }
150}