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