elf_runner/
crash_handler.rsuse crate::crash_info::{ComponentCrashInfo, CrashRecords};
use crate::error::ExceptionError;
use fuchsia_async as fasync;
use futures::TryStreamExt;
use moniker::Moniker;
use tracing::error;
use zx::{self as zx, AsHandleRef};
pub fn run_exceptions_server(
component_job: &zx::Job,
moniker: Moniker,
resolved_url: String,
crash_records: CrashRecords,
) -> Result<(), zx::Status> {
let mut task_exceptions_stream =
task_exceptions::ExceptionsStream::register_with_task(component_job)?;
fasync::Task::spawn(async move {
loop {
match task_exceptions_stream.try_next().await {
Ok(Some(exception_info)) => {
if let Err(error) = record_exception(
resolved_url.clone(),
moniker.clone(),
exception_info,
&crash_records,
)
.await
{
error!(url=%resolved_url, ?error, "failed to handle exception");
}
}
Ok(None) => break,
Err(error) => {
error!(
url=%resolved_url, ?error,
"failed to read message stream for fuchsia.sys2.CrashIntrospect",
);
break;
}
}
}
})
.detach();
Ok(())
}
async fn record_exception(
resolved_url: String,
moniker: Moniker,
exception_info: task_exceptions::ExceptionInfo,
crash_records: &CrashRecords,
) -> Result<(), ExceptionError> {
let thread_koid = exception_info.thread.get_koid().map_err(ExceptionError::GetThreadKoid)?;
crash_records.add_report(thread_koid, ComponentCrashInfo { url: resolved_url, moniker }).await;
exception_info
.exception_handle
.set_exception_state(&zx::sys::ZX_EXCEPTION_STATE_TRY_NEXT)
.map_err(ExceptionError::SetState)?;
Ok(())
}
#[cfg(test)]
mod tests {
use super::*;
use anyhow::{Context as _, Error};
use fuchsia_component::client as fclient;
use std::sync::Arc;
use zx::HandleBased;
use {fidl_fuchsia_io as fio, fidl_fuchsia_process as fprocess, fuchsia_runtime as fruntime};
#[fuchsia::test]
async fn crash_test() -> Result<(), Error> {
let crash_records = CrashRecords::new();
let url = "example://component#url".to_string();
let moniker = Moniker::try_from(vec!["a"]).unwrap();
let child_job =
fruntime::job_default().create_child_job().context("failed to create child job")?;
run_exceptions_server(&child_job, moniker.clone(), url.clone(), crash_records.clone())?;
let launcher_proxy = fclient::connect_to_protocol::<fprocess::LauncherMarker>()?;
let (ll_client_chan, ll_service_chan) = zx::Channel::create();
library_loader::start(
Arc::new(fuchsia_fs::directory::open_in_namespace(
"/pkg/lib",
fio::PERM_READABLE | fio::PERM_EXECUTABLE,
)?),
ll_service_chan,
);
let handle_infos = vec![fprocess::HandleInfo {
handle: ll_client_chan.into_handle(),
id: fruntime::HandleInfo::new(fruntime::HandleType::LdsvcLoader, 0).as_raw(),
}];
launcher_proxy.add_handles(handle_infos).context("failed to add loader service handle")?;
let executable_file_proxy = fuchsia_fs::file::open_in_namespace(
"/pkg/bin/panic_on_start",
fio::PERM_READABLE | fio::PERM_EXECUTABLE,
)?;
let vmo = executable_file_proxy
.get_backing_memory(fio::VmoFlags::READ | fio::VmoFlags::EXECUTE)
.await?
.map_err(zx::Status::from_raw)
.context("failed to get VMO of executable")?;
let child_job_dup = child_job.duplicate_handle(zx::Rights::SAME_RIGHTS)?;
let launch_info = fprocess::LaunchInfo {
executable: vmo,
job: child_job_dup,
name: "panic_on_start".to_string(),
};
let (status, process_start_data) = launcher_proxy
.create_without_starting(launch_info)
.await
.context("failed to launch process")?;
zx::Status::ok(status).context("error returned by process launcher")?;
let process_start_data = process_start_data.unwrap();
let thread_koid = process_start_data.thread.get_koid()?;
process_start_data.process.start(
&process_start_data.thread,
process_start_data.entry.try_into().unwrap(),
process_start_data.stack.try_into().unwrap(),
process_start_data.bootstrap.into_handle(),
process_start_data.vdso_base.try_into().unwrap(),
)?;
fasync::OnSignals::new(&process_start_data.process, zx::Signals::PROCESS_TERMINATED)
.await?;
let crash_info = crash_records
.take_report(&thread_koid)
.await
.expect("crash_records is missing crash information");
assert_eq!(ComponentCrashInfo { url, moniker }, crash_info);
Ok(())
}
}