driver_manager_firmware_crash/
firmware_crash_service.rs1use fuchsia_component::server::{ServiceFs, ServiceObjLocal};
6use futures::StreamExt;
7use log::warn;
8use std::cell::RefCell;
9use std::collections::HashMap;
10use std::rc::{Rc, Weak};
11use zx::HandleBased;
12use {fidl_fuchsia_firmware_crash as ffc, fuchsia_async as fasync};
13
14pub struct FirmwareCrashService {
15 inner: Rc<RefCell<FirmwareCrashInner>>,
16 scope: fasync::Scope,
17}
18
19struct FirmwareCrashInner {
20 crash_count: HashMap<String, u32>,
21 crashes: Vec<ffc::Crash>,
22 watchers: Vec<Weak<RefCell<Watcher>>>,
23}
24
25pub struct Watcher {
26 parent: Weak<RefCell<FirmwareCrashInner>>,
27 crash_index: usize,
28 completer: Option<ffc::WatcherGetCrashResponder>,
29}
30
31impl Default for FirmwareCrashService {
32 fn default() -> Self {
33 Self {
34 inner: Rc::new(RefCell::new(FirmwareCrashInner {
35 crash_count: HashMap::new(),
36 crashes: Vec::new(),
37 watchers: Vec::new(),
38 })),
39 scope: fasync::Scope::new_with_name("firmware_crash_service"),
40 }
41 }
42}
43
44impl FirmwareCrashService {
45 pub fn publish(self: &Rc<Self>, fs: &mut ServiceFs<ServiceObjLocal<'_, ()>>) {
46 let this = self.clone();
47 fs.dir("svc").add_fidl_service(move |stream: ffc::ReporterRequestStream| {
48 let this_clone1 = this.clone();
49 let this_clone2 = this.clone();
50 this_clone1.scope.spawn_local(async move {
51 if let Err(e) = this_clone2.serve_reporter(stream).await {
52 warn!("Failed to serve fuchsia.firmware.crash.Reporter: {}", e);
53 }
54 });
55 });
56
57 let this = self.clone();
58 fs.dir("svc").add_fidl_service(move |stream: ffc::WatcherRequestStream| {
59 let this_clone1 = this.clone();
60 let this_clone2 = this.clone();
61 this_clone1.scope.spawn_local(async move {
62 if let Err(e) = this_clone2.serve_watcher(stream).await {
63 warn!("Failed to serve fuchsia.firmware.crash.Watcher: {}", e);
64 }
65 });
66 });
67 }
68
69 async fn serve_reporter(
70 self: Rc<Self>,
71 mut stream: ffc::ReporterRequestStream,
72 ) -> Result<(), fidl::Error> {
73 while let Some(request) = stream.next().await {
74 match request? {
75 ffc::ReporterRequest::Report { mut payload, .. } => {
76 self.report(&mut payload);
77 }
78 ffc::ReporterRequest::_UnknownMethod { ordinal, .. } => {
79 warn!("fuchsia.firmware.crash/Reporter received unknown method: {}", ordinal);
80 }
81 }
82 }
83 Ok(())
84 }
85
86 fn report(&self, crash: &mut ffc::Crash) {
87 let watchers = {
88 let mut inner = self.inner.borrow_mut();
89
90 if let Some(subsystem) = &crash.subsystem_name {
91 let count = inner.crash_count.entry(subsystem.clone()).or_insert(0);
92 *count += 1;
93 crash.count = Some(*count);
94 }
95
96 inner.crashes.push(clone_crash(crash));
97
98 let mut active_watchers = Vec::new();
99 inner.watchers.retain(|w| {
100 if let Some(watcher) = w.upgrade() {
101 active_watchers.push(watcher);
102 true
103 } else {
104 false
105 }
106 });
107 active_watchers
108 };
109
110 for watcher in watchers {
111 watcher.borrow_mut().new_crash_available();
112 }
113 }
114
115 async fn serve_watcher(
116 self: Rc<Self>,
117 stream: ffc::WatcherRequestStream,
118 ) -> Result<(), fidl::Error> {
119 let watcher = Rc::new(RefCell::new(Watcher {
120 parent: Rc::downgrade(&self.inner),
121 crash_index: 0,
122 completer: None,
123 }));
124
125 self.inner.borrow_mut().watchers.push(Rc::downgrade(&watcher));
126
127 let mut stream = stream;
128 while let Some(request) = stream.next().await {
129 match request? {
130 ffc::WatcherRequest::GetCrash { responder } => {
131 watcher.borrow_mut().get_crash(responder);
132 }
133 ffc::WatcherRequest::_UnknownMethod { ordinal, .. } => {
134 warn!("fuchsia.firmware.crash/Watcher received unknown method: {}", ordinal);
135 }
136 }
137 }
138 Ok(())
139 }
140}
141
142impl Watcher {
143 fn new_crash_available(&mut self) {
144 let Some(parent) = self.parent.upgrade() else {
145 return;
146 };
147 let inner = parent.borrow();
148
149 if let Some(responder) = self.completer.take() {
150 let crash = clone_crash(&inner.crashes[self.crash_index]);
151 let _ = responder.send(Ok(crash));
152 self.crash_index += 1;
153 }
154 }
155
156 fn get_crash(&mut self, responder: ffc::WatcherGetCrashResponder) {
157 if self.completer.is_some() {
158 let _ = responder.send(Err(ffc::Error::AlreadyPending));
159 return;
160 }
161
162 let Some(parent) = self.parent.upgrade() else {
163 return;
164 };
165
166 let inner = parent.borrow();
167 if inner.crashes.len() > self.crash_index {
168 let crash = clone_crash(&inner.crashes[self.crash_index]);
169 self.crash_index += 1;
170 let _ = responder.send(Ok(crash));
171 return;
172 }
173
174 self.completer = Some(responder);
175 }
176}
177
178fn clone_crash(crash: &ffc::Crash) -> ffc::Crash {
179 ffc::Crash {
180 subsystem_name: crash.subsystem_name.clone(),
181 timestamp: crash.timestamp,
182 reason: crash.reason.clone(),
183 count: crash.count,
184 firmware_version: crash.firmware_version.clone(),
185 crash_dump: crash
186 .crash_dump
187 .as_ref()
188 .and_then(|vmo| vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).ok()),
189 ..Default::default()
190 }
191}
192
193#[cfg(test)]
194mod tests {
195 use super::*;
196 use fidl::endpoints::create_proxy_and_stream;
197
198 #[fasync::run_singlethreaded(test)]
199 async fn test_report_and_watch() {
200 let service = Rc::new(FirmwareCrashService::default());
201 let (reporter, reporter_stream) = create_proxy_and_stream::<ffc::ReporterMarker>();
202 let (watcher, watcher_stream) = create_proxy_and_stream::<ffc::WatcherMarker>();
203
204 let service_clone1 = service.clone();
205 let service_clone2 = service.clone();
206 service_clone1.scope.spawn_local(async move {
207 service_clone2.serve_reporter(reporter_stream).await.unwrap();
208 });
209
210 let service_clone1 = service.clone();
211 let service_clone2 = service.clone();
212 service_clone1.scope.spawn_local(async move {
213 service_clone2.serve_watcher(watcher_stream).await.unwrap();
214 });
215
216 let crash =
218 ffc::Crash { subsystem_name: Some("test-subsystem".to_string()), ..Default::default() };
219 reporter.report(crash).unwrap();
220
221 let result = watcher.get_crash().await.unwrap();
223 let received = result.unwrap();
224 assert_eq!(received.subsystem_name.unwrap(), "test-subsystem");
225 assert_eq!(received.count.unwrap(), 1);
226
227 let crash2 =
229 ffc::Crash { subsystem_name: Some("test-subsystem".to_string()), ..Default::default() };
230 reporter.report(crash2).unwrap();
231
232 let result = watcher.get_crash().await.unwrap();
234 let received2 = result.unwrap();
235 assert_eq!(received2.subsystem_name.unwrap(), "test-subsystem");
236 assert_eq!(received2.count.unwrap(), 2);
237 }
238
239 #[fasync::run_singlethreaded(test)]
240 async fn test_wait_for_crash() {
241 let service = Rc::new(FirmwareCrashService::default());
242 let (reporter, reporter_stream) = create_proxy_and_stream::<ffc::ReporterMarker>();
243 let (watcher, watcher_stream) = create_proxy_and_stream::<ffc::WatcherMarker>();
244
245 let service_clone1 = service.clone();
246 let service_clone2 = service.clone();
247 service_clone1.scope.spawn_local(async move {
248 service_clone2.serve_reporter(reporter_stream).await.unwrap();
249 });
250
251 let service_clone1 = service.clone();
252 let service_clone2 = service.clone();
253 service_clone1.scope.spawn_local(async move {
254 service_clone2.serve_watcher(watcher_stream).await.unwrap();
255 });
256
257 let get_fut = watcher.get_crash();
259
260 let crash =
262 ffc::Crash { subsystem_name: Some("test-subsystem".to_string()), ..Default::default() };
263 reporter.report(crash).unwrap();
264
265 let result = get_fut.await.unwrap();
267 let received = result.unwrap();
268 assert_eq!(received.subsystem_name.unwrap(), "test-subsystem");
269 }
270}