| // Copyright 2020 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 "src/developer/forensics/exceptions/handler/crash_reporter.h" |
| |
| #include <lib/async/cpp/task.h> |
| #include <lib/syslog/cpp/macros.h> |
| #include <lib/zx/exception.h> |
| |
| #include "src/developer/forensics/exceptions/handler/component_lookup.h" |
| #include "src/developer/forensics/exceptions/handler/minidump.h" |
| #include "src/developer/forensics/exceptions/handler/report_builder.h" |
| #include "src/developer/forensics/utils/fit/timeout.h" |
| #include "src/lib/fsl/handles/object_info.h" |
| |
| namespace forensics { |
| namespace exceptions { |
| namespace handler { |
| namespace { |
| |
| // Either resets the exception immediately if the process only has one thread or with a 5s delay |
| // otherwise. |
| void ResetException(async_dispatcher_t* dispatcher, zx::exception exception, |
| const zx::process& process) { |
| if (!process.is_valid()) { |
| FX_LOGS(ERROR) << "Process for exception is invalid"; |
| exception.reset(); |
| return; |
| } |
| |
| size_t num_threads{0}; |
| if (const zx_status_t status = zx_object_get_info(process.get(), ZX_INFO_PROCESS_THREADS, nullptr, |
| 0u, nullptr, &num_threads); |
| status != ZX_OK) { |
| FX_PLOGS(ERROR, status) << "Failed to get thread info from process " << process.get(); |
| exception.reset(); |
| return; |
| } |
| |
| if (num_threads > 1) { |
| // If the process has multiple threads, delay resetting |exception| for 5 seconds. If one of the |
| // other threads is in an exception, releasing |exception| immediately may result in the process |
| // being terminated by the kernel before the minidump for the other thread is generated. |
| async::PostDelayedTask( |
| dispatcher, [exception = std::move(exception)]() mutable { exception.reset(); }, |
| zx::sec(5)); |
| } else { |
| exception.reset(); |
| } |
| } |
| |
| } // namespace |
| |
| using fuchsia::feedback::CrashReport; |
| using fuchsia::sys::internal::SourceIdentity; |
| |
| CrashReporter::CrashReporter(async_dispatcher_t* dispatcher, |
| std::shared_ptr<sys::ServiceDirectory> services, |
| zx::duration component_lookup_timeout) |
| : dispatcher_(dispatcher), |
| executor_(dispatcher_), |
| services_(services), |
| component_lookup_timeout_(component_lookup_timeout) {} |
| |
| void CrashReporter::Send(zx::exception exception, zx::process crashed_process, |
| zx::thread crashed_thread, SendCallback callback) { |
| CrashReportBuilder builder; |
| builder.SetProcess(crashed_process).SetThread(crashed_thread); |
| |
| if (exception.is_valid()) { |
| zx::vmo minidump = GenerateMinidump(exception); |
| ResetException(dispatcher_, std::move(exception), crashed_process); |
| |
| if (minidump.is_valid()) { |
| builder.SetMinidump(std::move(minidump)); |
| } else { |
| builder.SetProcessTerminated(); |
| } |
| } else { |
| builder.SetExceptionExpired(); |
| } |
| |
| auto file_crash_report = |
| GetComponentSourceIdentity(dispatcher_, services_, fit::Timeout(component_lookup_timeout_), |
| fsl::GetKoid(crashed_thread.get())) |
| .then([services = services_, builder = std::move(builder), |
| callback = std::move(callback)](::fit::result<SourceIdentity>& result) mutable { |
| SourceIdentity component_lookup; |
| if (result.is_ok()) { |
| component_lookup = result.take_value(); |
| } |
| |
| builder.SetComponentInfo(component_lookup); |
| |
| // We make a fire-and-forget request as we won't do anything with the result. |
| auto crash_reporter = services->Connect<fuchsia::feedback::CrashReporter>(); |
| crash_reporter->File(builder.Consume(), |
| [](fuchsia::feedback::CrashReporter_File_Result result) {}); |
| |
| callback(); |
| |
| return ::fit::ok(); |
| }); |
| |
| executor_.schedule_task(std::move(file_crash_report)); |
| } |
| |
| } // namespace handler |
| } // namespace exceptions |
| } // namespace forensics |