// Copyright 2017 The Crashpad Authors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. #include "snapshot/linux/process_snapshot_linux.h" #include #include "base/logging.h" #include "build/build_config.h" #include "util/linux/exception_information.h" namespace crashpad { ProcessSnapshotLinux::ProcessSnapshotLinux() = default; ProcessSnapshotLinux::~ProcessSnapshotLinux() = default; bool ProcessSnapshotLinux::Initialize(PtraceConnection* connection) { INITIALIZATION_STATE_SET_INITIALIZING(initialized_); if (gettimeofday(&snapshot_time_, nullptr) != 0) { PLOG(ERROR) << "gettimeofday"; return false; } if (!process_reader_.Initialize(connection) || !memory_range_.Initialize(process_reader_.Memory(), process_reader_.Is64Bit())) { return false; } client_id_.InitializeToZero(); system_.Initialize(&process_reader_, &snapshot_time_); InitializeModules(); GetCrashpadOptionsInternal((&options_)); InitializeThreads(); InitializeAnnotations(); INITIALIZATION_STATE_SET_VALID(initialized_); return true; } pid_t ProcessSnapshotLinux::FindThreadWithStackAddress( VMAddress stack_address) { INITIALIZATION_STATE_DCHECK_VALID(initialized_); for (const auto& thread : process_reader_.Threads()) { if (stack_address >= thread.stack_region_address && stack_address < thread.stack_region_address + thread.stack_region_size) { return thread.tid; } } return -1; } bool ProcessSnapshotLinux::InitializeException( LinuxVMAddress exception_info_address, pid_t exception_thread_id) { INITIALIZATION_STATE_DCHECK_VALID(initialized_); DCHECK(!exception_); ExceptionInformation info; if (!process_reader_.Memory()->Read( exception_info_address, sizeof(info), &info)) { LOG(ERROR) << "Couldn't read exception info"; return false; } if (exception_thread_id >= 0) { info.thread_id = exception_thread_id; } uint32_t* budget_remaining_pointer = options_.gather_indirectly_referenced_memory == TriState::kEnabled ? &options_.indirectly_referenced_memory_cap : nullptr; exception_.reset(new internal::ExceptionSnapshotLinux()); if (!exception_->Initialize(&process_reader_, info.siginfo_address, info.context_address, info.thread_id, budget_remaining_pointer)) { exception_.reset(); return false; } // The thread's existing snapshot will have captured the stack for the signal // handler. Replace it with a thread snapshot which captures the stack for the // exception context. for (const auto& reader_thread : process_reader_.Threads()) { if (reader_thread.tid == info.thread_id) { ProcessReaderLinux::Thread thread = reader_thread; thread.InitializeStackFromSP(&process_reader_, exception_->Context()->StackPointer()); auto exc_thread_snapshot = std::make_unique(); if (!exc_thread_snapshot->Initialize(&process_reader_, thread, nullptr)) { return false; } #ifdef CLIENT_STACKTRACES_ENABLED exc_thread_snapshot->TrimStackTrace( exception_->Context()->InstructionPointer()); #endif for (auto& thread_snapshot : threads_) { if (thread_snapshot->ThreadID() == static_cast(info.thread_id)) { thread_snapshot.reset(exc_thread_snapshot.release()); return true; } } LOG(ERROR) << "thread not found " << info.thread_id; return false; } } LOG(ERROR) << "thread not found " << info.thread_id; return false; } void ProcessSnapshotLinux::GetCrashpadOptions( CrashpadInfoClientOptions* options) { INITIALIZATION_STATE_DCHECK_VALID(initialized_); *options = options_; } void ProcessSnapshotLinux::GetCrashpadOptionsInternal( CrashpadInfoClientOptions* options) { CrashpadInfoClientOptions local_options; for (const auto& module : modules_) { CrashpadInfoClientOptions module_options; if (!module->GetCrashpadOptions(&module_options)) { continue; } if (local_options.crashpad_handler_behavior == TriState::kUnset) { local_options.crashpad_handler_behavior = module_options.crashpad_handler_behavior; } if (local_options.system_crash_reporter_forwarding == TriState::kUnset) { local_options.system_crash_reporter_forwarding = module_options.system_crash_reporter_forwarding; } if (local_options.gather_indirectly_referenced_memory == TriState::kUnset) { local_options.gather_indirectly_referenced_memory = module_options.gather_indirectly_referenced_memory; local_options.indirectly_referenced_memory_cap = module_options.indirectly_referenced_memory_cap; } // If non-default values have been found for all options, the loop can end // early. if (local_options.crashpad_handler_behavior != TriState::kUnset && local_options.system_crash_reporter_forwarding != TriState::kUnset && local_options.gather_indirectly_referenced_memory != TriState::kUnset) { break; } } *options = local_options; } crashpad::ProcessID ProcessSnapshotLinux::ProcessID() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return process_reader_.ProcessID(); } crashpad::ProcessID ProcessSnapshotLinux::ParentProcessID() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return process_reader_.ParentProcessID(); } void ProcessSnapshotLinux::SnapshotTime(timeval* snapshot_time) const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); *snapshot_time = snapshot_time_; } void ProcessSnapshotLinux::ProcessStartTime(timeval* start_time) const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); process_reader_.StartTime(start_time); } void ProcessSnapshotLinux::ProcessCPUTimes(timeval* user_time, timeval* system_time) const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); process_reader_.CPUTimes(user_time, system_time); } void ProcessSnapshotLinux::ReportID(UUID* report_id) const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); *report_id = report_id_; } void ProcessSnapshotLinux::ClientID(UUID* client_id) const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); *client_id = client_id_; } const std::map& ProcessSnapshotLinux::AnnotationsSimpleMap() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return annotations_simple_map_; } const SystemSnapshot* ProcessSnapshotLinux::System() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return &system_; } std::vector ProcessSnapshotLinux::Threads() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); std::vector threads; for (const auto& thread : threads_) { threads.push_back(thread.get()); } return threads; } std::vector ProcessSnapshotLinux::Modules() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); std::vector modules; for (const auto& module : modules_) { modules.push_back(module.get()); } return modules; } std::vector ProcessSnapshotLinux::UnloadedModules() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); // TODO(jperaza): Can this be implemented on Linux? return std::vector(); } const ExceptionSnapshot* ProcessSnapshotLinux::Exception() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return exception_.get(); } std::vector ProcessSnapshotLinux::MemoryMap() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); // TODO(jperaza): do this. return std::vector(); } std::vector ProcessSnapshotLinux::Handles() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return std::vector(); } std::vector ProcessSnapshotLinux::ExtraMemory() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return std::vector(); } const ProcessMemory* ProcessSnapshotLinux::Memory() const { INITIALIZATION_STATE_DCHECK_VALID(initialized_); return process_reader_.Memory(); } void ProcessSnapshotLinux::InitializeThreads() { const std::vector& process_reader_threads = process_reader_.Threads(); uint32_t* budget_remaining_pointer = options_.gather_indirectly_referenced_memory == TriState::kEnabled ? &options_.indirectly_referenced_memory_cap : nullptr; for (const ProcessReaderLinux::Thread& process_reader_thread : process_reader_threads) { auto thread = std::make_unique(); if (thread->Initialize(&process_reader_, process_reader_thread, budget_remaining_pointer)) { threads_.push_back(std::move(thread)); } } } void ProcessSnapshotLinux::InitializeModules() { for (const ProcessReaderLinux::Module& reader_module : process_reader_.Modules()) { auto module = std::make_unique(reader_module.name, reader_module.elf_reader, reader_module.type, &memory_range_, process_reader_.Memory()); if (module->Initialize()) { modules_.push_back(std::move(module)); } } } void ProcessSnapshotLinux::InitializeAnnotations() { #if BUILDFLAG(IS_ANDROID) const std::string& abort_message = process_reader_.AbortMessage(); if (!abort_message.empty()) { annotations_simple_map_["abort_message"] = abort_message; } #endif } } // namespace crashpad