1use crate::constants::TEST_ROOT_REALM_NAME;
6use crate::error::DebugAgentError;
7use crate::run_events::SuiteEvents;
8use ftest_manager::LaunchError;
9use fuchsia_component::client::connect_to_protocol;
10use futures::channel::mpsc;
11use futures::future::RemoteHandle;
12use futures::prelude::*;
13use {
14 fidl_fuchsia_debugger as fdbg, fidl_fuchsia_test_manager as ftest_manager,
15 fuchsia_async as fasync,
16};
17
18pub(crate) struct ThreadBacktraceInfo {
19 pub thread: u64,
20 pub backtrace: String,
21}
22
23pub(crate) struct DebugAgent {
24 proxy: fdbg::DebugAgentProxy,
25 event_stream_processor_handle: Option<futures::future::RemoteHandle<()>>,
26 takeable_fatal_exception_recevier: Option<mpsc::Receiver<ThreadBacktraceInfo>>,
27}
28
29impl DebugAgent {
30 pub(crate) async fn new() -> Result<Self, DebugAgentError> {
33 Self::from_launcher_proxy(
34 connect_to_protocol::<fdbg::LauncherMarker>()
35 .map_err(|e| DebugAgentError::ConnectToLauncher(e))?,
36 )
37 .await
38 }
39
40 async fn from_launcher_proxy(
43 launcher_proxy: fdbg::LauncherProxy,
44 ) -> Result<Self, DebugAgentError> {
45 let (proxy, agent_server_end) = fidl::endpoints::create_proxy();
46
47 launcher_proxy
48 .launch(agent_server_end)
49 .await
50 .map_err(|e| DebugAgentError::LaunchLocal(e))?
51 .map_err(|zx_status| DebugAgentError::LaunchResponse(zx_status))?;
52
53 let _ = proxy
54 .attach_to(
55 TEST_ROOT_REALM_NAME,
56 fdbg::FilterType::MonikerSuffix,
57 &fdbg::FilterOptions { job_only: Some(true), ..Default::default() },
58 )
59 .await
60 .map_err(|e| DebugAgentError::AttachToTestsLocal(e))?
61 .map_err(|e| DebugAgentError::AttachToTestsResponse(e))?;
62
63 let (fatal_exception_sender, fatal_exception_receiver) = mpsc::channel(1024);
64 let event_stream_processor_handle =
65 Self::begin_process_event_stream(proxy.take_event_stream(), fatal_exception_sender);
66
67 Ok(Self {
68 proxy,
69 event_stream_processor_handle: Some(event_stream_processor_handle),
70 takeable_fatal_exception_recevier: Some(fatal_exception_receiver),
71 })
72 }
73
74 pub(crate) fn take_fatal_exception_receiver(&mut self) -> mpsc::Receiver<ThreadBacktraceInfo> {
77 self.takeable_fatal_exception_recevier.take().unwrap()
78 }
79
80 pub(crate) fn stop_sending_fatal_exceptions(&mut self) {
82 self.event_stream_processor_handle = None;
83 }
84
85 pub(crate) async fn report_all_backtraces(
88 &self,
89 mut event_sender: mpsc::Sender<Result<SuiteEvents, LaunchError>>,
90 ) {
91 let (client_end, mut server_end) = fidl::handle::fuchsia_handles::Socket::create_stream();
92 event_sender.send(Ok(SuiteEvents::suite_stderr(client_end).into())).await.unwrap();
93
94 let (iterator_proxy, iterator_server_end) = fidl::endpoints::create_proxy();
95 if let Err(_) = self
96 .proxy
97 .get_process_info(
98 &fdbg::GetProcessInfoOptions {
99 filter: Some(fdbg::Filter {
100 pattern: TEST_ROOT_REALM_NAME.to_string(),
101 type_: fdbg::FilterType::MonikerSuffix,
102 options: fdbg::FilterOptions { ..Default::default() },
103 }),
104 interest: Some(fdbg::ThreadDetailsInterest {
105 backtrace: Some(true),
106 ..Default::default()
107 }),
108 ..Default::default()
109 },
110 iterator_server_end,
111 )
112 .await
113 {
114 return;
116 }
117
118 loop {
119 match iterator_proxy.get_next().await {
120 Ok(result) => match result {
121 Ok(infos) => {
122 if infos.len() == 0 {
123 break;
124 }
125
126 for info in infos {
127 let _ =
128 server_end.write(format!("thread {:?}\n", info.thread).as_bytes());
129 DebugAgent::write_backtrace_info(
130 &ThreadBacktraceInfo {
131 thread: info.thread,
132 backtrace: info.details.backtrace.unwrap(),
133 },
134 &mut server_end,
135 )
136 .await;
137 }
138 }
139 Err(_e) => {
140 break;
141 }
142 },
143 Err(_e) => {
144 break;
145 }
146 }
147 }
148 }
149
150 fn begin_process_event_stream(
153 mut event_stream: fdbg::DebugAgentEventStream,
154 mut sender: mpsc::Sender<ThreadBacktraceInfo>,
155 ) -> RemoteHandle<()> {
156 let (event_stream_receiver, event_stream_receiver_handle) = async move {
157 loop {
158 match event_stream.next().await {
159 Some(Ok(fdbg::DebugAgentEvent::OnFatalException {
160 payload:
161 fdbg::DebugAgentOnFatalExceptionRequest {
162 thread: Some(thread),
163 backtrace: Some(backtrace),
164 ..
165 },
166 })) => {
167 sender.send(ThreadBacktraceInfo { thread, backtrace }).await.unwrap();
168 }
169 Some(_) => {}
170 None => break,
171 }
172 }
173 }
174 .remote_handle();
175
176 fasync::Task::spawn(event_stream_receiver).detach();
177
178 event_stream_receiver_handle
179 }
180
181 pub(crate) async fn write_backtrace_info(
182 backtrace_info: &ThreadBacktraceInfo,
183 socket_server_end: &mut fidl::Socket,
184 ) {
185 for line in backtrace_info.backtrace.split('\n') {
186 if line.starts_with("{{{reset:begin}}}") {
187 let _ = socket_server_end.write(line[..17].as_bytes());
188 let _ = socket_server_end.write(b"\n");
189 let _ = socket_server_end.write(line[17..].as_bytes());
190 } else {
191 let _ = socket_server_end.write(line.as_bytes());
192 }
193
194 let _ = socket_server_end.write(b"\n");
195 }
196 }
197}
198
199#[cfg(test)]
200mod test {
201 use crate::run_events::SuiteEventPayload;
202
203 use super::*;
204
205 const TEST_THREAD_1: u64 = 1234;
206 const TEST_THREAD_2: u64 = 5678;
207 const TEST_PROCESS_1: u64 = 4321;
208 const TEST_PROCESS_2: u64 = 8765;
209 const TEST_BACKTRACE_1: &str = "{{{reset:begin}}}test backtrace 1, with reset:begin prefix";
210 const TEST_BACKTRACE_2: &str = "test backtrace 2, no special prefix";
211 const TEST_MONIKER_1: &str = "test moniker 1";
212 const TEST_MONIKER_2: &str = "test moniker 2";
213 const TEST_STDERR_TEXT: &str = "thread 1234\n{{{reset:begin}}}\ntest backtrace 1, with \
214 reset:begin prefix\nthread 5678\ntest backtrace 2, no special prefix\n";
215 const TEST_STDERR_SIZE: usize = 120;
216
217 #[fuchsia::test]
218 async fn fatal_exceptions() {
219 let (mut under_test, mut debug_agent_service) = start_agent().await;
220
221 debug_agent_service.send_on_fatal_exception_event(TEST_THREAD_1, TEST_BACKTRACE_1);
223
224 let mut receiver = under_test.take_fatal_exception_receiver();
226 let thread_backtrace_info = receiver.next().await.expect("receive fatal exception");
227 assert_eq!(TEST_THREAD_1, thread_backtrace_info.thread);
228 assert_eq!(TEST_BACKTRACE_1, thread_backtrace_info.backtrace);
229
230 debug_agent_service.expect_nothing_more();
231 }
232
233 #[fuchsia::test]
234 async fn all_backtraces() {
235 let (under_test, mut debug_agent_service) = start_agent().await;
236
237 let (event_sender, mut event_receiver) =
238 mpsc::channel::<Result<SuiteEvents, LaunchError>>(16);
239
240 futures::future::join(under_test.report_all_backtraces(event_sender), async {
243 let iterator_server_end = debug_agent_service
244 .expect_get_process_info(&fdbg::GetProcessInfoOptions {
245 filter: Some(fdbg::Filter {
246 pattern: TEST_ROOT_REALM_NAME.to_string(),
247 type_: fdbg::FilterType::MonikerSuffix,
248 options: fdbg::FilterOptions { ..Default::default() },
249 }),
250 interest: Some(fdbg::ThreadDetailsInterest {
251 backtrace: Some(true),
252 ..Default::default()
253 }),
254 ..Default::default()
255 })
256 .await;
257 serve_process_info_iterator(iterator_server_end).await;
258 })
259 .await;
260
261 match event_receiver.try_next() {
263 Ok(Some(Ok(SuiteEvents {
264 timestamp: _,
265 payload: SuiteEventPayload::SuiteStderr(socket),
266 }))) => {
267 let mut buffer: [u8; 128] = [0; 128];
268 let size = socket.read(&mut buffer).expect("read from socket");
269 assert_eq!(TEST_STDERR_SIZE, size);
270 let stderr_string =
271 std::str::from_utf8(&buffer[0..TEST_STDERR_SIZE]).expect("convert [u8] to str");
272 assert_eq!(TEST_STDERR_TEXT, stderr_string);
273 }
274 Ok(Some(Ok(_))) => {
275 assert!(false, "Expected SuiteStderr event, got other event");
276 }
277 Ok(Some(Err(e))) => {
278 assert!(false, "Expected event, got error {:?}", e);
279 }
280 Ok(None) => {
281 assert!(false, "Expected event, got channel closed");
282 }
283 Err(e) => {
284 assert!(false, "Expected event, got channel error {:?}", e);
285 }
286 }
287 }
288
289 struct FakeLauncherService {
291 request_stream: fdbg::LauncherRequestStream,
292 }
293
294 impl FakeLauncherService {
295 fn new() -> (fdbg::LauncherProxy, Self) {
297 let (proxy, request_stream) =
298 fidl::endpoints::create_proxy_and_stream::<fdbg::LauncherMarker>();
299
300 (proxy, Self { request_stream })
301 }
302
303 async fn expect_launch_request(&mut self) -> FakeDebugAgentService {
305 match self.next_request().await {
306 fdbg::LauncherRequest::Launch { agent, responder } => {
307 responder.send(Ok(())).expect("send response to Launcher::Launch");
308 return FakeDebugAgentService::new(agent);
309 }
310 _ => panic!("Call to unexpected method."),
311 }
312 }
313
314 async fn expect_connection_closed(&mut self) {
315 if let Some(request) = self.request_stream.try_next().await.expect("get request") {
316 assert!(false, "Expected connection closed, got request {:?}", request);
317 }
318 }
319
320 fn expect_nothing_more(&mut self) {
322 match self.request_stream.try_next().now_or_never() {
323 Some(Ok(None)) => {
324 assert!(false, "Expected nothing more, got connection closed");
325 }
326 Some(Ok(v)) => {
327 assert!(false, "Expected nothing more, got request {:?}", v);
328 }
329 Some(Err(e)) => {
330 assert!(false, "Expected nothing more, got stream error {:?}", e);
331 }
332 None => {}
333 }
334 }
335
336 async fn next_request(&mut self) -> fdbg::LauncherRequest {
338 self.request_stream
339 .try_next()
340 .await
341 .expect("get request")
342 .expect("connection not closed")
343 }
344 }
345
346 struct FakeDebugAgentService {
348 request_stream: fdbg::DebugAgentRequestStream,
349 control_handle: fdbg::DebugAgentControlHandle,
350 }
351
352 impl FakeDebugAgentService {
353 fn new(server_end: fidl::endpoints::ServerEnd<fdbg::DebugAgentMarker>) -> Self {
355 let (request_stream, control_handle) = server_end.into_stream_and_control_handle();
356 FakeDebugAgentService { request_stream, control_handle }
357 }
358
359 async fn expect_attach_to(
361 &mut self,
362 expected_pattern: &str,
363 expected_type: fdbg::FilterType,
364 expected_options: fdbg::FilterOptions,
365 num_matches: u32,
366 ) {
367 match self.next_request().await {
368 fdbg::DebugAgentRequest::AttachTo { pattern, type_, options, responder } => {
369 responder.send(Ok(num_matches)).expect("send response to DebugAgent::AttachTo");
370 assert_eq!(expected_pattern, pattern);
371 assert_eq!(expected_type, type_);
372 assert_eq!(expected_options, options);
373 }
374 _ => panic!("Call to unexpected method."),
375 }
376 }
377
378 async fn expect_get_process_info(
381 &mut self,
382 expected_options: &fdbg::GetProcessInfoOptions,
383 ) -> fidl::endpoints::ServerEnd<fdbg::ProcessInfoIteratorMarker> {
384 match self.next_request().await {
385 fdbg::DebugAgentRequest::GetProcessInfo { options, iterator, responder } => {
386 responder.send(Ok(())).expect("send response to DebugAgent::GetProcessInfo");
387 assert_eq!(expected_options, &options);
388 iterator
389 }
390 _ => panic!("Call to unexpected method."),
391 }
392 }
393
394 fn send_on_fatal_exception_event(&mut self, thread: u64, backtrace: &str) {
396 self.control_handle
397 .send_on_fatal_exception(&fdbg::DebugAgentOnFatalExceptionRequest {
398 thread: Some(thread),
399 backtrace: Some(backtrace.to_string()),
400 ..Default::default()
401 })
402 .expect("send OnFatalException event");
403 }
404
405 fn expect_nothing_more(&mut self) {
407 match self.request_stream.try_next().now_or_never() {
408 Some(Ok(None)) => {
409 assert!(false, "Expected nothing more, got connection closed");
410 }
411 Some(Ok(v)) => {
412 assert!(false, "Expected nothing more, got request {:?}", v);
413 }
414 Some(Err(e)) => {
415 assert!(false, "Expected nothing more, got stream error {:?}", e);
416 }
417 None => {}
418 }
419 }
420
421 async fn next_request(&mut self) -> fdbg::DebugAgentRequest {
423 self.request_stream
424 .try_next()
425 .await
426 .expect("get request")
427 .expect("connection not closed")
428 }
429 }
430
431 async fn start_agent() -> (DebugAgent, FakeDebugAgentService) {
432 let (launcher_proxy, mut launcher_service) = FakeLauncherService::new();
434 launcher_service.expect_nothing_more();
435
436 let (under_test, mut debug_agent_service) =
438 futures::future::join(DebugAgent::from_launcher_proxy(launcher_proxy), async {
439 let mut debug_agent_service = launcher_service.expect_launch_request().await;
440 debug_agent_service
441 .expect_attach_to(
442 TEST_ROOT_REALM_NAME,
443 fdbg::FilterType::MonikerSuffix,
444 fdbg::FilterOptions { job_only: Some(true), ..Default::default() },
445 0,
446 )
447 .await;
448 debug_agent_service
449 })
450 .await;
451
452 let under_test = under_test.expect("create launcher from proxy");
453
454 launcher_service.expect_connection_closed().await;
456 debug_agent_service.expect_nothing_more();
457
458 (under_test, debug_agent_service)
459 }
460
461 async fn serve_process_info_iterator(
463 server_end: fidl::endpoints::ServerEnd<fdbg::ProcessInfoIteratorMarker>,
464 ) {
465 let mut request_stream = server_end.into_stream();
466
467 let request =
469 request_stream.try_next().await.expect("get request").expect("connection not closed");
470 match request {
471 fdbg::ProcessInfoIteratorRequest::GetNext { responder } => {
472 responder
473 .send(Ok(&vec![
474 fdbg::ProcessInfo {
475 process: TEST_PROCESS_1,
476 moniker: TEST_MONIKER_1.to_string(),
477 thread: TEST_THREAD_1,
478 details: fdbg::ThreadDetails {
479 backtrace: Some(TEST_BACKTRACE_1.to_string()),
480 ..Default::default()
481 },
482 },
483 fdbg::ProcessInfo {
484 process: TEST_PROCESS_2,
485 moniker: TEST_MONIKER_2.to_string(),
486 thread: TEST_THREAD_2,
487 details: fdbg::ThreadDetails {
488 backtrace: Some(TEST_BACKTRACE_2.to_string()),
489 ..Default::default()
490 },
491 },
492 ]))
493 .expect("send response");
494 }
495 }
496
497 let request =
499 request_stream.try_next().await.expect("get request").expect("connection not closed");
500 match request {
501 fdbg::ProcessInfoIteratorRequest::GetNext { responder } => {
502 responder.send(Ok(&vec![])).expect("send response");
503 }
504 }
505
506 if let Some(request) = request_stream.try_next().await.expect("get request") {
508 assert!(false, "Expected connection closed, got request {:?}", request);
509 }
510 }
511}