elf_runner/
crash_handler.rs1use crate::crash_info::{ComponentCrashInfo, CrashRecords};
6use crate::error::ExceptionError;
7use futures::TryStreamExt;
8use log::error;
9use moniker::Moniker;
10use vfs::ExecutionScope;
11
12pub fn run_exceptions_server(
16 scope: &ExecutionScope,
17 component_job: &zx::Job,
18 moniker: Moniker,
19 resolved_url: String,
20 crash_records: CrashRecords,
21) -> Result<(), zx::Status> {
22 let mut task_exceptions_stream =
23 task_exceptions::ExceptionsStream::register_with_task(component_job)?;
24 scope.spawn(async move {
25 loop {
26 match task_exceptions_stream.try_next().await {
27 Ok(Some(exception_info)) => {
28 if let Err(error) = record_exception(
29 resolved_url.clone(),
30 moniker.clone(),
31 exception_info,
32 &crash_records,
33 )
34 .await
35 {
36 error!(url:% = resolved_url, error:?; "failed to handle exception");
37 }
38 }
39 Ok(None) => break,
40 Err(error) => {
41 error!(
42 url:% = resolved_url, error:?;
43 "failed to read message stream for fuchsia.sys2.CrashIntrospect",
44 );
45 break;
46 }
47 }
48 }
49 });
50 Ok(())
51}
52
53async fn record_exception(
54 resolved_url: String,
55 moniker: Moniker,
56 exception_info: task_exceptions::ExceptionInfo,
57 crash_records: &CrashRecords,
58) -> Result<(), ExceptionError> {
59 let thread_koid = exception_info.thread.koid().map_err(ExceptionError::GetThreadKoid)?;
62 crash_records.add_report(thread_koid, ComponentCrashInfo { url: resolved_url, moniker }).await;
63
64 exception_info
67 .exception_handle
68 .set_exception_state(&zx::sys::ZX_EXCEPTION_STATE_TRY_NEXT)
69 .map_err(ExceptionError::SetState)?;
70
71 Ok(())
74}
75
76#[cfg(test)]
77mod tests {
78 use super::*;
79 use anyhow::{Context as _, Error};
80 use fuchsia_component::client as fclient;
81 use vfs::ExecutionScope;
82 use zx::HandleBased;
83 use {
84 fidl_fuchsia_io as fio, fidl_fuchsia_process as fprocess, fuchsia_async as fasync,
85 fuchsia_runtime as fruntime,
86 };
87
88 #[fuchsia::test]
89 async fn crash_test() -> Result<(), Error> {
90 let crash_records = CrashRecords::new();
91 let url = "example://component#url".to_string();
92 let moniker = Moniker::try_from(["a"]).unwrap();
93
94 let child_job =
95 fruntime::job_default().create_child_job().context("failed to create child job")?;
96
97 let scope = ExecutionScope::new();
98 run_exceptions_server(
99 &scope,
100 &child_job,
101 moniker.clone(),
102 url.clone(),
103 crash_records.clone(),
104 )?;
105
106 let launcher_proxy = fclient::connect_to_protocol::<fprocess::LauncherMarker>()?;
108
109 let (ll_client_chan, ll_service_chan) = zx::Channel::create();
111 library_loader::start(
112 fuchsia_fs::directory::open_in_namespace(
113 "/pkg/lib",
114 fio::PERM_READABLE | fio::PERM_EXECUTABLE,
115 )?,
116 ll_service_chan,
117 );
118 let handle_infos = vec![fprocess::HandleInfo {
119 handle: ll_client_chan.into_handle(),
120 id: fruntime::HandleInfo::new(fruntime::HandleType::LdsvcLoader, 0).as_raw(),
121 }];
122 launcher_proxy.add_handles(handle_infos).context("failed to add loader service handle")?;
123
124 let executable_file_proxy = fuchsia_fs::file::open_in_namespace(
126 "/pkg/bin/panic_on_start",
127 fio::PERM_READABLE | fio::PERM_EXECUTABLE,
128 )?;
129 let vmo = executable_file_proxy
130 .get_backing_memory(fio::VmoFlags::READ | fio::VmoFlags::EXECUTE)
131 .await?
132 .map_err(zx::Status::from_raw)
133 .context("failed to get VMO of executable")?;
134
135 let child_job_dup = child_job.duplicate_handle(zx::Rights::SAME_RIGHTS)?;
137 let launch_info = fprocess::LaunchInfo {
138 executable: vmo,
139 job: child_job_dup,
140 name: "panic_on_start".to_string(),
141 };
142 let (status, process_start_data) = launcher_proxy
143 .create_without_starting(launch_info)
144 .await
145 .context("failed to launch process")?;
146 zx::Status::ok(status).context("error returned by process launcher")?;
147 let process_start_data = process_start_data.unwrap();
148
149 let thread_koid = process_start_data.thread.koid()?;
152
153 process_start_data.process.start(
155 &process_start_data.thread,
156 process_start_data.entry.try_into().unwrap(),
158 process_start_data.stack.try_into().unwrap(),
159 process_start_data.bootstrap.into_handle(),
160 process_start_data.vdso_base.try_into().unwrap(),
161 )?;
162
163 fasync::OnSignals::new(&process_start_data.process, zx::Signals::PROCESS_TERMINATED)
165 .await?;
166 let crash_info = crash_records
167 .take_report(&thread_koid)
168 .await
169 .expect("crash_records is missing crash information");
170 assert_eq!(ComponentCrashInfo { url, moniker }, crash_info);
171 Ok(())
172 }
173}