driver_manager_firmware_crash/
firmware_crash_service.rs1use fidl::Peered;
6use fuchsia_component::server::{ServiceFs, ServiceObjLocal};
7use futures::StreamExt;
8use log::warn;
9use std::cell::RefCell;
10use std::collections::HashMap;
11use std::rc::{Rc, Weak};
12use zx::HandleBased;
13use {fidl_fuchsia_firmware_crash as ffc, fuchsia_async as fasync};
14
15pub struct FirmwareCrashService {
16 inner: Rc<RefCell<FirmwareCrashInner>>,
17 scope: fasync::Scope,
18}
19
20struct FirmwareCrashInner {
21 crash_count: HashMap<String, u32>,
22 crashes: Vec<ffc::Crash>,
23 watchers: Vec<Weak<RefCell<Watcher>>>,
24}
25
26pub struct Watcher {
27 parent: Weak<RefCell<FirmwareCrashInner>>,
28 crash_index: usize,
29 completer: Option<ffc::WatcherGetCrashResponder>,
30 event: Option<zx::EventPair>,
31}
32
33impl Default for FirmwareCrashService {
34 fn default() -> Self {
35 Self {
36 inner: Rc::new(RefCell::new(FirmwareCrashInner {
37 crash_count: HashMap::new(),
38 crashes: Vec::new(),
39 watchers: Vec::new(),
40 })),
41 scope: fasync::Scope::new_with_name("firmware_crash_service"),
42 }
43 }
44}
45
46impl FirmwareCrashService {
47 pub fn publish(self: &Rc<Self>, fs: &mut ServiceFs<ServiceObjLocal<'_, ()>>) {
48 let this = self.clone();
49 fs.dir("svc").add_fidl_service(move |stream: ffc::ReporterRequestStream| {
50 let this_clone1 = this.clone();
51 let this_clone2 = this.clone();
52 this_clone1.scope.spawn_local(async move {
53 if let Err(e) = this_clone2.serve_reporter(stream).await {
54 warn!("Failed to serve fuchsia.firmware.crash.Reporter: {}", e);
55 }
56 });
57 });
58
59 let this = self.clone();
60 fs.dir("svc").add_fidl_service(move |stream: ffc::WatcherRequestStream| {
61 let this_clone1 = this.clone();
62 let this_clone2 = this.clone();
63 this_clone1.scope.spawn_local(async move {
64 if let Err(e) = this_clone2.serve_watcher(stream).await {
65 warn!("Failed to serve fuchsia.firmware.crash.Watcher: {}", e);
66 }
67 });
68 });
69 }
70
71 async fn serve_reporter(
72 self: Rc<Self>,
73 mut stream: ffc::ReporterRequestStream,
74 ) -> Result<(), fidl::Error> {
75 while let Some(request) = stream.next().await {
76 match request? {
77 ffc::ReporterRequest::Report { mut payload, .. } => {
78 self.report(&mut payload);
79 }
80 ffc::ReporterRequest::_UnknownMethod { ordinal, .. } => {
81 warn!("fuchsia.firmware.crash/Reporter received unknown method: {}", ordinal);
82 }
83 }
84 }
85 Ok(())
86 }
87
88 fn report(&self, crash: &mut ffc::Crash) {
89 let watchers = {
90 let mut inner = self.inner.borrow_mut();
91
92 if let Some(subsystem) = &crash.subsystem_name {
93 let count = inner.crash_count.entry(subsystem.clone()).or_insert(0);
94 *count += 1;
95 crash.count = Some(*count);
96 }
97
98 inner.crashes.push(clone_crash(crash));
99
100 let mut active_watchers = Vec::new();
101 inner.watchers.retain(|w| {
102 if let Some(watcher) = w.upgrade() {
103 active_watchers.push(watcher);
104 true
105 } else {
106 false
107 }
108 });
109 active_watchers
110 };
111
112 for watcher in watchers {
113 watcher.borrow_mut().new_crash_available();
114 }
115 }
116
117 async fn serve_watcher(
118 self: Rc<Self>,
119 stream: ffc::WatcherRequestStream,
120 ) -> Result<(), fidl::Error> {
121 let watcher = Rc::new(RefCell::new(Watcher {
122 parent: Rc::downgrade(&self.inner),
123 crash_index: 0,
124 completer: None,
125 event: None,
126 }));
127
128 self.inner.borrow_mut().watchers.push(Rc::downgrade(&watcher));
129
130 let mut stream = stream;
131 while let Some(request) = stream.next().await {
132 match request? {
133 ffc::WatcherRequest::GetCrash { payload, responder } => {
134 watcher.borrow_mut().get_crash(payload, responder);
135 }
136 ffc::WatcherRequest::GetCrashEvent { responder } => {
137 watcher.borrow_mut().get_crash_event(responder);
138 }
139 ffc::WatcherRequest::_UnknownMethod { ordinal, .. } => {
140 warn!("fuchsia.firmware.crash/Watcher received unknown method: {}", ordinal);
141 }
142 }
143 }
144 Ok(())
145 }
146}
147
148impl Watcher {
149 fn new_crash_available(&mut self) {
150 let Some(parent) = self.parent.upgrade() else {
151 return;
152 };
153 let inner = parent.borrow();
154
155 if let Some(responder) = self.completer.take() {
156 let crash = clone_crash(&inner.crashes[self.crash_index]);
157 let _ = responder.send(Ok(crash));
158 self.crash_index += 1;
159 } else if let Some(event) = &self.event {
160 let _ = event.signal_peer(zx::Signals::NONE, zx::Signals::USER_0);
161 }
162 }
163
164 fn get_crash(
165 &mut self,
166 request: ffc::WatcherGetCrashRequest,
167 responder: ffc::WatcherGetCrashResponder,
168 ) {
169 if self.completer.is_some() {
170 let _ = responder.send(Err(ffc::Error::AlreadyPending));
171 return;
172 }
173
174 let Some(parent) = self.parent.upgrade() else {
175 return;
176 };
177
178 let inner = parent.borrow();
179 if inner.crashes.len() > self.crash_index {
180 let crash = clone_crash(&inner.crashes[self.crash_index]);
181 self.crash_index += 1;
182 if let Some(event) = &self.event
183 && self.crash_index == inner.crashes.len()
184 {
185 let _ = event.signal_peer(zx::Signals::USER_0, zx::Signals::NONE);
186 }
187 let _ = responder.send(Ok(crash));
188 return;
189 }
190
191 if request.wait_for_crash.unwrap_or(true) {
192 self.completer = Some(responder);
193 } else {
194 let _ = responder.send(Err(ffc::Error::NoCrashAvailable));
195 }
196 }
197
198 fn get_crash_event(&mut self, responder: ffc::WatcherGetCrashEventResponder) {
199 let (h1, h2) = zx::EventPair::create();
200 self.event = Some(h1);
201 let _ = responder.send(h2);
202 }
203}
204
205fn clone_crash(crash: &ffc::Crash) -> ffc::Crash {
206 ffc::Crash {
207 subsystem_name: crash.subsystem_name.clone(),
208 timestamp: crash.timestamp,
209 reason: crash.reason.clone(),
210 count: crash.count,
211 firmware_version: crash.firmware_version.clone(),
212 crash_dump: crash
213 .crash_dump
214 .as_ref()
215 .and_then(|vmo| vmo.duplicate_handle(zx::Rights::SAME_RIGHTS).ok()),
216 ..Default::default()
217 }
218}
219
220#[cfg(test)]
221mod tests {
222 use super::*;
223 use fidl::endpoints::create_proxy_and_stream;
224 use futures::FutureExt;
225
226 #[fasync::run_singlethreaded(test)]
227 async fn test_report_and_watch() {
228 let service = Rc::new(FirmwareCrashService::default());
229 let (reporter, reporter_stream) = create_proxy_and_stream::<ffc::ReporterMarker>();
230 let (watcher, watcher_stream) = create_proxy_and_stream::<ffc::WatcherMarker>();
231
232 let service_clone1 = service.clone();
233 let service_clone2 = service.clone();
234 service_clone1.scope.spawn_local(async move {
235 service_clone2.serve_reporter(reporter_stream).await.unwrap();
236 });
237
238 let service_clone1 = service.clone();
239 let service_clone2 = service.clone();
240 service_clone1.scope.spawn_local(async move {
241 service_clone2.serve_watcher(watcher_stream).await.unwrap();
242 });
243
244 let crash =
246 ffc::Crash { subsystem_name: Some("test-subsystem".to_string()), ..Default::default() };
247 reporter.report(crash).unwrap();
248
249 let result = watcher
251 .get_crash(&ffc::WatcherGetCrashRequest {
252 wait_for_crash: Some(false),
253 ..Default::default()
254 })
255 .await
256 .unwrap();
257 let received = result.unwrap();
258 assert_eq!(received.subsystem_name.unwrap(), "test-subsystem");
259 assert_eq!(received.count.unwrap(), 1);
260
261 let crash2 =
263 ffc::Crash { subsystem_name: Some("test-subsystem".to_string()), ..Default::default() };
264 reporter.report(crash2).unwrap();
265
266 let result = watcher
268 .get_crash(&ffc::WatcherGetCrashRequest {
269 wait_for_crash: Some(false),
270 ..Default::default()
271 })
272 .await
273 .unwrap();
274 let received2 = result.unwrap();
275 assert_eq!(received2.subsystem_name.unwrap(), "test-subsystem");
276 assert_eq!(received2.count.unwrap(), 2);
277 }
278
279 #[fasync::run_singlethreaded(test)]
280 async fn test_wait_for_crash() {
281 let service = Rc::new(FirmwareCrashService::default());
282 let (reporter, reporter_stream) = create_proxy_and_stream::<ffc::ReporterMarker>();
283 let (watcher, watcher_stream) = create_proxy_and_stream::<ffc::WatcherMarker>();
284
285 let service_clone1 = service.clone();
286 let service_clone2 = service.clone();
287 service_clone1.scope.spawn_local(async move {
288 service_clone2.serve_reporter(reporter_stream).await.unwrap();
289 });
290
291 let service_clone1 = service.clone();
292 let service_clone2 = service.clone();
293 service_clone1.scope.spawn_local(async move {
294 service_clone2.serve_watcher(watcher_stream).await.unwrap();
295 });
296
297 let get_fut = watcher.get_crash(&ffc::WatcherGetCrashRequest {
299 wait_for_crash: Some(true),
300 ..Default::default()
301 });
302
303 let crash =
305 ffc::Crash { subsystem_name: Some("test-subsystem".to_string()), ..Default::default() };
306 reporter.report(crash).unwrap();
307
308 let result = get_fut.await.unwrap();
310 let received = result.unwrap();
311 assert_eq!(received.subsystem_name.unwrap(), "test-subsystem");
312 }
313
314 #[fasync::run_singlethreaded(test)]
315 async fn test_get_crash_event() {
316 let service = Rc::new(FirmwareCrashService::default());
317 let (reporter, reporter_stream) = create_proxy_and_stream::<ffc::ReporterMarker>();
318 let (watcher, watcher_stream) = create_proxy_and_stream::<ffc::WatcherMarker>();
319
320 let service_clone1 = service.clone();
321 let service_clone2 = service.clone();
322 service_clone1.scope.spawn_local(async move {
323 service_clone2.serve_reporter(reporter_stream).await.unwrap();
324 });
325
326 let service_clone1 = service.clone();
327 let service_clone2 = service.clone();
328 service_clone1.scope.spawn_local(async move {
329 service_clone2.serve_watcher(watcher_stream).await.unwrap();
330 });
331
332 let event = watcher.get_crash_event().await.unwrap();
333
334 let crash =
336 ffc::Crash { subsystem_name: Some("test-subsystem".to_string()), ..Default::default() };
337 reporter.report(crash).unwrap();
338
339 fasync::OnSignals::new(&event, zx::Signals::USER_0).await.unwrap();
341
342 let _ = watcher
344 .get_crash(&ffc::WatcherGetCrashRequest {
345 wait_for_crash: Some(false),
346 ..Default::default()
347 })
348 .await
349 .unwrap()
350 .unwrap();
351
352 let timer =
354 fasync::Timer::new(zx::MonotonicInstant::after(zx::MonotonicDuration::from_millis(10)))
355 .fuse();
356 let signals = fasync::OnSignals::new(&event, zx::Signals::USER_0).fuse();
357 futures::pin_mut!(timer, signals);
358 futures::select! {
359 _ = signals => panic!("Signal should have been cleared"),
360 _ = timer => (),
361 }
362 }
363}