2021-10-14 10:55:03 -04:00
|
|
|
// Copyright 2021 The Crashpad Authors. All rights reserved.
|
|
|
|
//
|
|
|
|
// 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 "client/ios_handler/in_process_handler.h"
|
|
|
|
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
|
|
|
#include "base/logging.h"
|
|
|
|
#include "client/ios_handler/in_process_intermediate_dump_handler.h"
|
2021-10-25 11:35:19 -04:00
|
|
|
#include "client/prune_crash_reports.h"
|
2021-10-14 10:55:03 -04:00
|
|
|
#include "client/settings.h"
|
|
|
|
#include "minidump/minidump_file_writer.h"
|
|
|
|
#include "util/file/directory_reader.h"
|
|
|
|
#include "util/file/filesystem.h"
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
|
|
|
// Creates directory at |path|.
|
2021-10-27 12:31:45 -04:00
|
|
|
bool CreateDirectory(const base::FilePath& path) {
|
2021-10-14 10:55:03 -04:00
|
|
|
if (mkdir(path.value().c_str(), 0755) == 0) {
|
2021-10-27 12:31:45 -04:00
|
|
|
return true;
|
2021-10-14 10:55:03 -04:00
|
|
|
}
|
|
|
|
if (errno != EEXIST) {
|
|
|
|
PLOG(ERROR) << "mkdir " << path.value();
|
2021-10-27 12:31:45 -04:00
|
|
|
return false;
|
2021-10-14 10:55:03 -04:00
|
|
|
}
|
2021-10-27 12:31:45 -04:00
|
|
|
return true;
|
2021-10-14 10:55:03 -04:00
|
|
|
}
|
|
|
|
|
2021-10-19 13:53:37 -04:00
|
|
|
// The file extension used to indicate a file is locked.
|
|
|
|
constexpr char kLockedExtension[] = ".locked";
|
|
|
|
|
|
|
|
// The seperator used to break the bundle id (e.g. com.chromium.ios) from the
|
|
|
|
// uuid in the intermediate dump file name.
|
|
|
|
constexpr char kBundleSeperator[] = "@";
|
|
|
|
|
2021-10-14 10:55:03 -04:00
|
|
|
} // namespace
|
|
|
|
|
|
|
|
namespace crashpad {
|
|
|
|
namespace internal {
|
|
|
|
|
|
|
|
InProcessHandler::InProcessHandler() = default;
|
|
|
|
|
|
|
|
InProcessHandler::~InProcessHandler() {
|
2021-11-11 06:32:46 -05:00
|
|
|
if (upload_thread_started_ && upload_thread_) {
|
|
|
|
upload_thread_->Stop();
|
|
|
|
}
|
|
|
|
prune_thread_->Stop();
|
2021-10-14 10:55:03 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool InProcessHandler::Initialize(
|
|
|
|
const base::FilePath& database,
|
|
|
|
const std::string& url,
|
2021-10-19 13:53:37 -04:00
|
|
|
const std::map<std::string, std::string>& annotations,
|
|
|
|
const IOSSystemDataCollector& system_data) {
|
2021-10-14 10:55:03 -04:00
|
|
|
INITIALIZATION_STATE_SET_INITIALIZING(initialized_);
|
|
|
|
annotations_ = annotations;
|
|
|
|
database_ = CrashReportDatabase::Initialize(database);
|
2021-10-27 12:31:45 -04:00
|
|
|
if (!database_) {
|
|
|
|
return false;
|
|
|
|
}
|
2021-10-19 13:53:37 -04:00
|
|
|
bundle_identifier_and_seperator_ =
|
|
|
|
system_data.BundleIdentifier() + kBundleSeperator;
|
2021-10-14 10:55:03 -04:00
|
|
|
|
|
|
|
if (!url.empty()) {
|
|
|
|
// TODO(scottmg): options.rate_limit should be removed when we have a
|
|
|
|
// configurable database setting to control upload limiting.
|
|
|
|
// See https://crashpad.chromium.org/bug/23.
|
|
|
|
CrashReportUploadThread::Options upload_thread_options;
|
|
|
|
upload_thread_options.rate_limit = false;
|
|
|
|
upload_thread_options.upload_gzip = true;
|
|
|
|
upload_thread_options.watch_pending_reports = true;
|
|
|
|
upload_thread_options.identify_client_via_url = true;
|
|
|
|
|
|
|
|
upload_thread_.reset(new CrashReportUploadThread(
|
|
|
|
database_.get(), url, upload_thread_options));
|
|
|
|
}
|
|
|
|
|
2021-10-27 12:31:45 -04:00
|
|
|
if (!CreateDirectory(database))
|
|
|
|
return false;
|
2021-10-14 10:55:03 -04:00
|
|
|
static constexpr char kPendingSerializediOSDump[] =
|
|
|
|
"pending-serialized-ios-dump";
|
|
|
|
base_dir_ = database.Append(kPendingSerializediOSDump);
|
2021-10-27 12:31:45 -04:00
|
|
|
if (!CreateDirectory(base_dir_))
|
|
|
|
return false;
|
2021-10-14 10:55:03 -04:00
|
|
|
|
2021-10-25 11:35:19 -04:00
|
|
|
prune_thread_.reset(new PruneIntermediateDumpsAndCrashReportsThread(
|
|
|
|
database_.get(),
|
|
|
|
PruneCondition::GetDefault(),
|
|
|
|
base_dir_,
|
|
|
|
bundle_identifier_and_seperator_,
|
|
|
|
system_data.IsExtension()));
|
|
|
|
prune_thread_->Start();
|
|
|
|
|
2021-10-14 10:55:03 -04:00
|
|
|
if (!OpenNewFile())
|
|
|
|
return false;
|
|
|
|
|
|
|
|
INITIALIZATION_STATE_SET_VALID(initialized_);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void InProcessHandler::DumpExceptionFromSignal(
|
|
|
|
const IOSSystemDataCollector& system_data,
|
|
|
|
siginfo_t* siginfo,
|
|
|
|
ucontext_t* context) {
|
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
{
|
2021-11-10 13:04:10 -05:00
|
|
|
ScopedReport report(writer_.get(), system_data, annotations_);
|
2021-10-14 10:55:03 -04:00
|
|
|
InProcessIntermediateDumpHandler::WriteExceptionFromSignal(
|
|
|
|
writer_.get(), system_data, siginfo, context);
|
|
|
|
}
|
|
|
|
PostReportCleanup();
|
|
|
|
}
|
|
|
|
|
|
|
|
void InProcessHandler::DumpExceptionFromMachException(
|
|
|
|
const IOSSystemDataCollector& system_data,
|
|
|
|
exception_behavior_t behavior,
|
|
|
|
thread_t thread,
|
|
|
|
exception_type_t exception,
|
|
|
|
const mach_exception_data_type_t* code,
|
|
|
|
mach_msg_type_number_t code_count,
|
|
|
|
thread_state_flavor_t flavor,
|
|
|
|
ConstThreadState old_state,
|
|
|
|
mach_msg_type_number_t old_state_count) {
|
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
{
|
2021-11-10 13:04:10 -05:00
|
|
|
ScopedReport report(writer_.get(), system_data, annotations_);
|
2021-10-14 10:55:03 -04:00
|
|
|
InProcessIntermediateDumpHandler::WriteExceptionFromMachException(
|
|
|
|
writer_.get(),
|
|
|
|
behavior,
|
|
|
|
thread,
|
|
|
|
exception,
|
|
|
|
code,
|
|
|
|
code_count,
|
|
|
|
flavor,
|
|
|
|
old_state,
|
|
|
|
old_state_count);
|
|
|
|
}
|
|
|
|
PostReportCleanup();
|
|
|
|
}
|
|
|
|
|
|
|
|
void InProcessHandler::DumpExceptionFromNSExceptionFrames(
|
|
|
|
const IOSSystemDataCollector& system_data,
|
|
|
|
const uint64_t* frames,
|
|
|
|
const size_t num_frames) {
|
|
|
|
{
|
2021-11-10 13:04:10 -05:00
|
|
|
ScopedReport report(
|
|
|
|
writer_.get(), system_data, annotations_, frames, num_frames);
|
2021-10-14 10:55:03 -04:00
|
|
|
InProcessIntermediateDumpHandler::WriteExceptionFromNSException(
|
|
|
|
writer_.get());
|
|
|
|
}
|
|
|
|
PostReportCleanup();
|
|
|
|
}
|
|
|
|
|
|
|
|
void InProcessHandler::ProcessIntermediateDumps(
|
2021-11-10 13:04:10 -05:00
|
|
|
const std::map<std::string, std::string>& annotations) {
|
2021-10-14 10:55:03 -04:00
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
|
|
|
|
for (auto& file : PendingFiles())
|
2021-11-10 13:04:10 -05:00
|
|
|
ProcessIntermediateDump(file, annotations);
|
2021-10-14 10:55:03 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void InProcessHandler::ProcessIntermediateDump(
|
|
|
|
const base::FilePath& file,
|
2021-11-10 13:04:10 -05:00
|
|
|
const std::map<std::string, std::string>& annotations) {
|
2021-10-14 10:55:03 -04:00
|
|
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
|
|
|
|
2021-11-10 13:04:10 -05:00
|
|
|
ProcessSnapshotIOSIntermediateDump process_snapshot;
|
2021-11-20 23:03:45 -05:00
|
|
|
if (process_snapshot.InitializeWithFilePath(file, annotations)) {
|
2021-11-10 13:04:10 -05:00
|
|
|
SaveSnapshot(process_snapshot);
|
|
|
|
}
|
2021-10-14 10:55:03 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
void InProcessHandler::StartProcessingPendingReports() {
|
|
|
|
if (!upload_thread_started_ && upload_thread_) {
|
|
|
|
upload_thread_->Start();
|
|
|
|
upload_thread_started_ = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void InProcessHandler::SaveSnapshot(
|
|
|
|
ProcessSnapshotIOSIntermediateDump& process_snapshot) {
|
|
|
|
std::unique_ptr<CrashReportDatabase::NewReport> new_report;
|
|
|
|
CrashReportDatabase::OperationStatus database_status =
|
|
|
|
database_->PrepareNewCrashReport(&new_report);
|
|
|
|
if (database_status != CrashReportDatabase::kNoError) {
|
|
|
|
Metrics::ExceptionCaptureResult(
|
|
|
|
Metrics::CaptureResult::kPrepareNewCrashReportFailed);
|
|
|
|
}
|
|
|
|
process_snapshot.SetReportID(new_report->ReportID());
|
|
|
|
|
2021-11-21 00:46:19 -05:00
|
|
|
UUID client_id;
|
|
|
|
Settings* const settings = database_->GetSettings();
|
|
|
|
if (settings && settings->GetClientID(&client_id)) {
|
|
|
|
process_snapshot.SetClientID(client_id);
|
|
|
|
}
|
|
|
|
|
2021-10-14 10:55:03 -04:00
|
|
|
MinidumpFileWriter minidump;
|
|
|
|
minidump.InitializeFromSnapshot(&process_snapshot);
|
|
|
|
if (!minidump.WriteEverything(new_report->Writer())) {
|
|
|
|
Metrics::ExceptionCaptureResult(
|
|
|
|
Metrics::CaptureResult::kMinidumpWriteFailed);
|
|
|
|
}
|
|
|
|
UUID uuid;
|
|
|
|
database_status =
|
|
|
|
database_->FinishedWritingCrashReport(std::move(new_report), &uuid);
|
|
|
|
if (database_status != CrashReportDatabase::kNoError) {
|
|
|
|
Metrics::ExceptionCaptureResult(
|
|
|
|
Metrics::CaptureResult::kFinishedWritingCrashReportFailed);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (upload_thread_) {
|
|
|
|
upload_thread_->ReportPending(uuid);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<base::FilePath> InProcessHandler::PendingFiles() {
|
|
|
|
DirectoryReader reader;
|
|
|
|
std::vector<base::FilePath> files;
|
|
|
|
if (!reader.Open(base_dir_)) {
|
|
|
|
return files;
|
|
|
|
}
|
|
|
|
base::FilePath file;
|
|
|
|
DirectoryReader::Result result;
|
2021-11-11 06:32:46 -05:00
|
|
|
|
|
|
|
// Because the intermediate dump directory is expected to be shared,
|
|
|
|
// mitigate any spamming by limiting this to |kMaxPendingFiles|.
|
|
|
|
constexpr size_t kMaxPendingFiles = 20;
|
|
|
|
|
|
|
|
// Track other application bundles separately, so they don't spam our
|
|
|
|
// intermediate dumps into never getting processed.
|
|
|
|
std::vector<base::FilePath> other_files;
|
|
|
|
|
2021-10-14 10:55:03 -04:00
|
|
|
while ((result = reader.NextFile(&file)) ==
|
|
|
|
DirectoryReader::Result::kSuccess) {
|
2021-10-19 13:53:37 -04:00
|
|
|
// Don't try to process files marked as 'locked' from a different bundle id.
|
2021-11-11 06:32:46 -05:00
|
|
|
bool bundle_match =
|
|
|
|
file.value().compare(0,
|
2021-10-19 13:53:37 -04:00
|
|
|
bundle_identifier_and_seperator_.size(),
|
2021-11-11 06:32:46 -05:00
|
|
|
bundle_identifier_and_seperator_) == 0;
|
|
|
|
if (!bundle_match && file.FinalExtension() == kLockedExtension) {
|
2021-10-19 13:53:37 -04:00
|
|
|
continue;
|
2021-10-14 10:55:03 -04:00
|
|
|
}
|
2021-10-19 13:53:37 -04:00
|
|
|
|
|
|
|
// Never process the current file.
|
|
|
|
file = base_dir_.Append(file);
|
|
|
|
if (file == current_file_)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Otherwise, include any other unlocked, or locked files matching
|
|
|
|
// |bundle_identifier_and_seperator_|.
|
2021-11-11 06:32:46 -05:00
|
|
|
if (bundle_match) {
|
|
|
|
files.push_back(file);
|
|
|
|
if (files.size() >= kMaxPendingFiles)
|
|
|
|
return files;
|
|
|
|
} else {
|
|
|
|
other_files.push_back(file);
|
|
|
|
}
|
2021-10-14 10:55:03 -04:00
|
|
|
}
|
2021-11-11 06:32:46 -05:00
|
|
|
|
|
|
|
auto end_iterator =
|
|
|
|
other_files.begin() +
|
|
|
|
std::min(kMaxPendingFiles - files.size(), other_files.size());
|
|
|
|
files.insert(files.end(), other_files.begin(), end_iterator);
|
2021-10-14 10:55:03 -04:00
|
|
|
return files;
|
|
|
|
}
|
|
|
|
|
|
|
|
InProcessHandler::ScopedAlternateWriter::ScopedAlternateWriter(
|
|
|
|
InProcessHandler* handler)
|
|
|
|
: handler_(handler) {}
|
|
|
|
|
|
|
|
bool InProcessHandler::ScopedAlternateWriter::Open() {
|
|
|
|
UUID uuid;
|
|
|
|
uuid.InitializeWithNew();
|
|
|
|
const std::string uuid_string = uuid.ToString();
|
|
|
|
return OpenAtPath(handler_->base_dir_.Append(uuid_string));
|
|
|
|
}
|
|
|
|
|
|
|
|
bool InProcessHandler::ScopedAlternateWriter::OpenAtPath(
|
|
|
|
const base::FilePath& path) {
|
|
|
|
path_ = path;
|
|
|
|
handler_->SetOpenNewFileAfterReport(false);
|
|
|
|
original_writer_ = handler_->GetWriter();
|
|
|
|
auto writer = std::make_unique<IOSIntermediateDumpWriter>();
|
|
|
|
if (!writer->Open(path_)) {
|
|
|
|
DLOG(ERROR) << "Unable to open alternate intermediate dump file: "
|
|
|
|
<< path_.value();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
handler_->SetWriter(std::move(writer));
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
InProcessHandler::ScopedAlternateWriter::~ScopedAlternateWriter() {
|
|
|
|
handler_->SetWriter(std::move(original_writer_));
|
|
|
|
handler_->SetOpenNewFileAfterReport(true);
|
|
|
|
}
|
|
|
|
|
|
|
|
InProcessHandler::ScopedReport::ScopedReport(
|
|
|
|
IOSIntermediateDumpWriter* writer,
|
|
|
|
const IOSSystemDataCollector& system_data,
|
2021-11-10 13:04:10 -05:00
|
|
|
const std::map<std::string, std::string>& annotations,
|
2021-10-14 10:55:03 -04:00
|
|
|
const uint64_t* frames,
|
|
|
|
const size_t num_frames)
|
2021-11-22 15:18:15 -05:00
|
|
|
: writer_(writer),
|
|
|
|
frames_(frames),
|
|
|
|
num_frames_(num_frames),
|
|
|
|
rootMap_(writer) {
|
2021-10-14 10:55:03 -04:00
|
|
|
InProcessIntermediateDumpHandler::WriteHeader(writer);
|
2021-11-10 13:04:10 -05:00
|
|
|
InProcessIntermediateDumpHandler::WriteProcessInfo(writer, annotations);
|
2021-10-14 10:55:03 -04:00
|
|
|
InProcessIntermediateDumpHandler::WriteSystemInfo(writer, system_data);
|
2021-11-22 15:18:15 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
InProcessHandler::ScopedReport::~ScopedReport() {
|
|
|
|
// Write threads and modules last (after the exception itself is written by
|
|
|
|
// DumpExceptionFrom*.)
|
|
|
|
InProcessIntermediateDumpHandler::WriteThreadInfo(
|
|
|
|
writer_, frames_, num_frames_);
|
|
|
|
InProcessIntermediateDumpHandler::WriteModuleInfo(writer_);
|
2021-10-14 10:55:03 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
bool InProcessHandler::OpenNewFile() {
|
2021-10-19 13:53:37 -04:00
|
|
|
if (!current_file_.empty()) {
|
|
|
|
// Remove .lock extension so this dump can be processed on next run by this
|
|
|
|
// client, or a client with a different bundle id that can access this dump.
|
|
|
|
base::FilePath new_path = current_file_.RemoveFinalExtension();
|
|
|
|
MoveFileOrDirectory(current_file_, new_path);
|
|
|
|
}
|
2021-10-14 10:55:03 -04:00
|
|
|
UUID uuid;
|
|
|
|
uuid.InitializeWithNew();
|
2021-10-19 13:53:37 -04:00
|
|
|
const std::string file_string =
|
|
|
|
bundle_identifier_and_seperator_ + uuid.ToString() + kLockedExtension;
|
|
|
|
current_file_ = base_dir_.Append(file_string);
|
2021-10-14 10:55:03 -04:00
|
|
|
writer_ = std::make_unique<IOSIntermediateDumpWriter>();
|
|
|
|
if (!writer_->Open(current_file_)) {
|
|
|
|
DLOG(ERROR) << "Unable to open intermediate dump file: "
|
|
|
|
<< current_file_.value();
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void InProcessHandler::PostReportCleanup() {
|
|
|
|
if (writer_) {
|
|
|
|
writer_->Close();
|
|
|
|
writer_.reset();
|
|
|
|
}
|
|
|
|
if (open_new_file_after_report_)
|
|
|
|
OpenNewFile();
|
|
|
|
}
|
|
|
|
|
|
|
|
} // namespace internal
|
|
|
|
} // namespace crashpad
|