// Copyright 2018 The Fuchsia Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include #include #include #include #include #include #include #include #include #include #include #include #include #include static bool GetChildKoids(const zx::job& job, zx_object_info_topic_t child_kind, fbl::unique_ptr* koids, size_t* num_koids) { size_t actual = 0; size_t available = 0; size_t count = 100; koids->reset(new zx_koid_t[count]); for (;;) { if (job.get_info(child_kind, koids->get(), count * sizeof(zx_koid_t), &actual, &available) != ZX_OK) { fprintf(stderr, "crashsvc: failed to get child koids\n"); koids->reset(); *num_koids = 0; return false; } if (actual == available) { break; } // Resize to the expected number next time with a bit of slop to try to // handle the race between here and the next request. count = available + 10; koids->reset(new zx_koid_t[count]); } // No need to actually downsize the output array since the size is separate. *num_koids = actual; return true; } static bool FindProcess(const zx::job& job, zx_koid_t process_koid, zx::process* out) { // Search this job for the process. zx::process process; if (job.get_child(process_koid, ZX_RIGHT_SAME_RIGHTS, &process) == ZX_OK) { *out = std::move(process); return true; } // Otherwise, enumerate and recurse into child jobs. fbl::unique_ptr child_koids; size_t num_koids; if (GetChildKoids(job, ZX_INFO_JOB_CHILDREN, &child_koids, &num_koids)) { for (size_t i = 0; i < num_koids; ++i) { zx::job child_job; if (job.get_child(child_koids[i], ZX_RIGHT_SAME_RIGHTS, &child_job) != ZX_OK) { continue; } if (FindProcess(child_job, process_koid, out)) { return true; } } } return false; } struct crash_ctx { zx::job root_job; zx::port exception_port; zx::channel svc_request; }; static void HandOffException(const crash_ctx& ctx, const zx_port_packet_t& packet) { zx::process process; if (!FindProcess(ctx.root_job, packet.exception.pid, &process)) { fprintf(stderr, "crashsvc: failed to find process for pid=%zu\n", packet.exception.pid); return; } zx::thread thread; if (process.get_child(packet.exception.tid, ZX_RIGHT_SAME_RIGHTS, &thread) != ZX_OK) { fprintf(stderr, "crashsvc: failed to find thread for tid=%zu\n", packet.exception.tid); return; } if (ctx.svc_request.is_valid()) { // Use the full system analyzer FIDL service, presumably crashpad_analyzer. zx::port port; if (ctx.exception_port.duplicate(ZX_RIGHT_SAME_RIGHTS, &port) != ZX_OK) { fprintf(stderr, "crashsvc: failed to duplicate exception port\n"); return; } // The resume_thread is only needed if the FIDL call fails. zx::thread resume_thread; thread.duplicate(ZX_RIGHT_SAME_RIGHTS, &resume_thread); zx_status_t analizer_status = ZX_ERR_INTERNAL; auto status = fuchsia_crash_AnalyzerHandleNativeException( ctx.svc_request.get(), process.release(), thread.release(), port.release(), &analizer_status); if ((status != ZX_OK) || (analizer_status != ZX_OK)) { fprintf(stderr, "crashsvc: analyzer failed, err (%d | %d)\n", status, analizer_status); if (resume_thread) { zx_task_resume_from_exception( resume_thread.get(), ctx.exception_port.get(), ZX_RESUME_TRY_NEXT); } } } else { // Use the zircon built-in analyzer. Does not return status so we presume // that upon failure it resumes the thread. inspector_print_debug_info_and_resume_thread( process.get(), thread.get(), ctx.exception_port.get()); } } int crash_svc(void* arg) { auto ctx = fbl::unique_ptr(reinterpret_cast(arg)); for (;;) { zx_port_packet_t packet; zx_status_t status = ctx->exception_port.wait(zx::time::infinite(), &packet); if (status != ZX_OK) { fprintf(stderr, "crashsvc: zx_port_wait failed %d\n", status); continue; } HandOffException(*ctx, packet); } } // Initialize the crash service, this supersedes the standalone service with the same // name that lived in zircon/system/core/crashsvc/crashsvc.cpp (/boot/bin/crashsvc) and // ad-hoc microservice in devmgr that delegated to svchost. See ZX-3199 for details. // // The job of this service is to handle exceptions that reached |root_job| and delegate // the crash analysis to one of two services: // // - built-in : using system/ulib/inspector // - appmgr hosted: via FIDL interface call (fuchsia_crash_Analyzer). // // Which one depends if |analyzer_svc| is a valid channel handle, which svchost sets // depending on "use_system". // void start_crashsvc(zx::job root_job, zx_handle_t analyzer_svc) { zx::port exception_port; zx::port::create(0, &exception_port); if (root_job.bind_exception_port(exception_port, 0, 0) != ZX_OK) { fprintf(stderr, "svchost: unable to bind to root job exception port\n"); return; } zx::channel ch0, ch1; if (analyzer_svc != ZX_HANDLE_INVALID) { zx::channel::create(0u, &ch0, &ch1); auto status = fdio_service_connect_at( analyzer_svc, fuchsia_crash_Analyzer_Name, ch0.release()); if (status != ZX_OK) { fprintf(stderr, "svchost: to connect to analyzer service\n"); return; } } auto ctx = new crash_ctx { std::move(root_job), std::move(exception_port), std::move(ch1) }; thrd_t t; if ((thrd_create_with_name(&t, crash_svc, ctx, "crash-svc")) == thrd_success) { thrd_detach(t); } else { delete ctx; } }