mirror of
https://github.com/chromium/crashpad.git
synced 2025-01-14 17:30:09 +08:00
win,linux: implement attachments support
Implemented the AddAttachment(), InitializeAttachments(), CleanDatabase() functions on Windows. Added attachment=FILE_NAME option to the handler, and "attachments" argument for Windows and Linux to StartHandler function. On crash it will create the corresponding attachments in the database and copy content of the specified files to the database. Bug: b/157144387 Change-Id: Ia238de39028e07112a7b971b5b7d5e71a5864f53 Reviewed-on: https://chromium-review.googlesource.com/c/crashpad/crashpad/+/2248099 Commit-Queue: Joshua Peraza <jperaza@chromium.org> Reviewed-by: Joshua Peraza <jperaza@chromium.org>
This commit is contained in:
parent
7409adbff3
commit
4145699874
@ -33,7 +33,8 @@ std::vector<std::string> BuildHandlerArgvStrings(
|
|||||||
const base::FilePath& metrics_dir,
|
const base::FilePath& metrics_dir,
|
||||||
const std::string& url,
|
const std::string& url,
|
||||||
const std::map<std::string, std::string>& annotations,
|
const std::map<std::string, std::string>& annotations,
|
||||||
const std::vector<std::string>& arguments) {
|
const std::vector<std::string>& arguments,
|
||||||
|
const std::vector<base::FilePath>& attachments) {
|
||||||
std::vector<std::string> argv_strings(1, handler.value());
|
std::vector<std::string> argv_strings(1, handler.value());
|
||||||
|
|
||||||
for (const auto& argument : arguments) {
|
for (const auto& argument : arguments) {
|
||||||
@ -58,6 +59,11 @@ std::vector<std::string> BuildHandlerArgvStrings(
|
|||||||
FormatArgumentString("annotation", kv.first + '=' + kv.second));
|
FormatArgumentString("annotation", kv.first + '=' + kv.second));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const auto& attachment : attachments) {
|
||||||
|
argv_strings.push_back(
|
||||||
|
FormatArgumentString("attachment", attachment.value()));
|
||||||
|
}
|
||||||
|
|
||||||
return argv_strings;
|
return argv_strings;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -35,7 +35,8 @@ std::vector<std::string> BuildHandlerArgvStrings(
|
|||||||
const base::FilePath& metrics_dir,
|
const base::FilePath& metrics_dir,
|
||||||
const std::string& url,
|
const std::string& url,
|
||||||
const std::map<std::string, std::string>& annotations,
|
const std::map<std::string, std::string>& annotations,
|
||||||
const std::vector<std::string>& arguments);
|
const std::vector<std::string>& arguments,
|
||||||
|
const std::vector<base::FilePath>& attachments = {});
|
||||||
|
|
||||||
//! \brief Flattens a string vector into a const char* vector suitable for use
|
//! \brief Flattens a string vector into a const char* vector suitable for use
|
||||||
//! in an exec() call.
|
//! in an exec() call.
|
||||||
|
@ -126,7 +126,7 @@ class CrashReportDatabase {
|
|||||||
|
|
||||||
//! \brief Adds an attachment to the report.
|
//! \brief Adds an attachment to the report.
|
||||||
//!
|
//!
|
||||||
//! \note This function is not yet implemented on macOS or Windows.
|
//! \note This function is not yet implemented on macOS.
|
||||||
//!
|
//!
|
||||||
//! \param[in] name The key and name for the attachment, which will be
|
//! \param[in] name The key and name for the attachment, which will be
|
||||||
//! included in the http upload. The attachment will not appear in the
|
//! included in the http upload. The attachment will not appear in the
|
||||||
@ -170,7 +170,7 @@ class CrashReportDatabase {
|
|||||||
//! \brief Obtains a mapping of names to file readers for any attachments
|
//! \brief Obtains a mapping of names to file readers for any attachments
|
||||||
//! for the report.
|
//! for the report.
|
||||||
//!
|
//!
|
||||||
//! This is not implemented on macOS or Windows.
|
//! This is not implemented on macOS.
|
||||||
std::map<std::string, FileReader*> GetAttachments() const {
|
std::map<std::string, FileReader*> GetAttachments() const {
|
||||||
return attachment_map_;
|
return attachment_map_;
|
||||||
}
|
}
|
||||||
@ -396,8 +396,7 @@ class CrashReportDatabase {
|
|||||||
//! \brief Cleans the database of expired lockfiles, metadata without report
|
//! \brief Cleans the database of expired lockfiles, metadata without report
|
||||||
//! files, and report files without metadata.
|
//! files, and report files without metadata.
|
||||||
//!
|
//!
|
||||||
//! This method does nothing on the macOS and Windows implementations of the
|
//! This method does nothing on the macOS implementations of the database.
|
||||||
//! database.
|
|
||||||
//!
|
//!
|
||||||
//! \param[in] lockfile_ttl The number of seconds at which lockfiles or new
|
//! \param[in] lockfile_ttl The number of seconds at which lockfiles or new
|
||||||
//! report files are considered expired.
|
//! report files are considered expired.
|
||||||
|
@ -29,6 +29,8 @@
|
|||||||
#include "base/strings/stringprintf.h"
|
#include "base/strings/stringprintf.h"
|
||||||
#include "base/strings/utf_string_conversions.h"
|
#include "base/strings/utf_string_conversions.h"
|
||||||
#include "client/settings.h"
|
#include "client/settings.h"
|
||||||
|
#include "util/file/directory_reader.h"
|
||||||
|
#include "util/file/filesystem.h"
|
||||||
#include "util/misc/implicit_cast.h"
|
#include "util/misc/implicit_cast.h"
|
||||||
#include "util/misc/initialization_state_dcheck.h"
|
#include "util/misc/initialization_state_dcheck.h"
|
||||||
#include "util/misc/metrics.h"
|
#include "util/misc/metrics.h"
|
||||||
@ -47,6 +49,8 @@ constexpr wchar_t kCrashReportFileExtension[] = L"dmp";
|
|||||||
constexpr uint32_t kMetadataFileHeaderMagic = 'CPAD';
|
constexpr uint32_t kMetadataFileHeaderMagic = 'CPAD';
|
||||||
constexpr uint32_t kMetadataFileVersion = 1;
|
constexpr uint32_t kMetadataFileVersion = 1;
|
||||||
|
|
||||||
|
constexpr base::FilePath::CharType kAttachmentsDirectory[] = L"attachments";
|
||||||
|
|
||||||
using OperationStatus = CrashReportDatabase::OperationStatus;
|
using OperationStatus = CrashReportDatabase::OperationStatus;
|
||||||
|
|
||||||
// Helpers ---------------------------------------------------------------------
|
// Helpers ---------------------------------------------------------------------
|
||||||
@ -82,6 +86,11 @@ std::string ReadRestOfFileAsString(FileHandle file) {
|
|||||||
: std::string();
|
: std::string();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool UUIDFromReportPath(const base::FilePath& path, UUID* uuid) {
|
||||||
|
return uuid->InitializeFromString(
|
||||||
|
path.RemoveFinalExtension().BaseName().value());
|
||||||
|
}
|
||||||
|
|
||||||
// Helper structures, and conversions ------------------------------------------
|
// Helper structures, and conversions ------------------------------------------
|
||||||
|
|
||||||
// The format of the on disk metadata file is a MetadataFileHeader, followed by
|
// The format of the on disk metadata file is a MetadataFileHeader, followed by
|
||||||
@ -263,6 +272,12 @@ class Metadata {
|
|||||||
OperationStatus DeleteReport(const UUID& uuid,
|
OperationStatus DeleteReport(const UUID& uuid,
|
||||||
base::FilePath* report_path);
|
base::FilePath* report_path);
|
||||||
|
|
||||||
|
//! \brief Removes reports from the metadata database, that don't have
|
||||||
|
//! corresponding report files.
|
||||||
|
//!
|
||||||
|
//! \return number of metadata entries removed
|
||||||
|
int CleanDatabase();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Metadata(FileHandle handle, const base::FilePath& report_dir);
|
Metadata(FileHandle handle, const base::FilePath& report_dir);
|
||||||
|
|
||||||
@ -399,6 +414,20 @@ OperationStatus Metadata::DeleteReport(const UUID& uuid,
|
|||||||
return CrashReportDatabase::kNoError;
|
return CrashReportDatabase::kNoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int Metadata::CleanDatabase() {
|
||||||
|
int removed = 0;
|
||||||
|
for (auto report_iter = reports_.begin(); report_iter != reports_.end();) {
|
||||||
|
if (!IsRegularFile(report_iter->file_path)) {
|
||||||
|
report_iter = reports_.erase(report_iter);
|
||||||
|
++removed;
|
||||||
|
dirty_ = true;
|
||||||
|
} else {
|
||||||
|
++report_iter;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
|
||||||
Metadata::Metadata(FileHandle handle, const base::FilePath& report_dir)
|
Metadata::Metadata(FileHandle handle, const base::FilePath& report_dir)
|
||||||
: handle_(handle), report_dir_(report_dir), dirty_(false), reports_() {
|
: handle_(handle), report_dir_(report_dir), dirty_(false), reports_() {
|
||||||
}
|
}
|
||||||
@ -611,6 +640,13 @@ class CrashReportDatabaseWin : public CrashReportDatabase {
|
|||||||
Metrics::CrashSkippedReason reason) override;
|
Metrics::CrashSkippedReason reason) override;
|
||||||
OperationStatus DeleteReport(const UUID& uuid) override;
|
OperationStatus DeleteReport(const UUID& uuid) override;
|
||||||
OperationStatus RequestUpload(const UUID& uuid) override;
|
OperationStatus RequestUpload(const UUID& uuid) override;
|
||||||
|
int CleanDatabase(time_t lockfile_ttl) override;
|
||||||
|
|
||||||
|
// Build a filepath for the root attachments directory.
|
||||||
|
base::FilePath AttachmentsRootPath();
|
||||||
|
|
||||||
|
// Build a filepath for the directory for the report to hold attachments.
|
||||||
|
base::FilePath AttachmentsPath(const UUID& uuid);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// CrashReportDatabase:
|
// CrashReportDatabase:
|
||||||
@ -618,6 +654,13 @@ class CrashReportDatabaseWin : public CrashReportDatabase {
|
|||||||
bool successful,
|
bool successful,
|
||||||
const std::string& id) override;
|
const std::string& id) override;
|
||||||
|
|
||||||
|
// Cleans any attachments that have no associated report.
|
||||||
|
void CleanOrphanedAttachments();
|
||||||
|
|
||||||
|
// Attempt to remove any attachments associated with the given report UUID.
|
||||||
|
// There may not be any, so failing is not an error.
|
||||||
|
void RemoveAttachmentsByUUID(const UUID& uuid);
|
||||||
|
|
||||||
std::unique_ptr<Metadata> AcquireMetadata();
|
std::unique_ptr<Metadata> AcquireMetadata();
|
||||||
|
|
||||||
base::FilePath base_dir_;
|
base::FilePath base_dir_;
|
||||||
@ -627,14 +670,66 @@ class CrashReportDatabaseWin : public CrashReportDatabase {
|
|||||||
DISALLOW_COPY_AND_ASSIGN(CrashReportDatabaseWin);
|
DISALLOW_COPY_AND_ASSIGN(CrashReportDatabaseWin);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
base::FilePath CrashReportDatabaseWin::AttachmentsRootPath() {
|
||||||
|
return base_dir_.Append(kAttachmentsDirectory);
|
||||||
|
}
|
||||||
|
|
||||||
|
base::FilePath CrashReportDatabaseWin::AttachmentsPath(const UUID& uuid) {
|
||||||
|
const std::wstring uuid_string = uuid.ToString16();
|
||||||
|
return base_dir_.Append(kAttachmentsDirectory).Append(uuid_string);
|
||||||
|
}
|
||||||
|
|
||||||
FileWriter* CrashReportDatabase::NewReport::AddAttachment(
|
FileWriter* CrashReportDatabase::NewReport::AddAttachment(
|
||||||
const std::string& name) {
|
const std::string& name) {
|
||||||
// Attachments aren't implemented in the Windows database yet.
|
auto database_win = static_cast<CrashReportDatabaseWin*>(database_);
|
||||||
return nullptr;
|
base::FilePath attachments_root_dir = database_win->AttachmentsRootPath();
|
||||||
|
base::FilePath attachments_dir = database_win->AttachmentsPath(uuid_);
|
||||||
|
if (!LoggingCreateDirectory(
|
||||||
|
attachments_root_dir, FilePermissions::kOwnerOnly, true) ||
|
||||||
|
!LoggingCreateDirectory(
|
||||||
|
attachments_dir, FilePermissions::kOwnerOnly, true)) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
base::FilePath path = attachments_dir.Append(base::UTF8ToUTF16(name));
|
||||||
|
|
||||||
|
auto writer = std::make_unique<FileWriter>();
|
||||||
|
if (!writer->Open(
|
||||||
|
path, FileWriteMode::kCreateOrFail, FilePermissions::kOwnerOnly)) {
|
||||||
|
LOG(ERROR) << "could not open " << path.value().c_str();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
attachment_writers_.emplace_back(std::move(writer));
|
||||||
|
attachment_removers_.emplace_back(ScopedRemoveFile(path));
|
||||||
|
return attachment_writers_.back().get();
|
||||||
}
|
}
|
||||||
|
|
||||||
void CrashReportDatabase::UploadReport::InitializeAttachments() {
|
void CrashReportDatabase::UploadReport::InitializeAttachments() {
|
||||||
// Attachments aren't implemented in the Windows database yet.
|
base::FilePath attachments_dir =
|
||||||
|
static_cast<CrashReportDatabaseWin*>(database_)->AttachmentsPath(uuid);
|
||||||
|
if (!IsDirectory(attachments_dir, /*allow_symlinks=*/false)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DirectoryReader dir_reader;
|
||||||
|
if (!dir_reader.Open(attachments_dir)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
base::FilePath filename;
|
||||||
|
DirectoryReader::Result dir_result;
|
||||||
|
while ((dir_result = dir_reader.NextFile(&filename)) ==
|
||||||
|
DirectoryReader::Result::kSuccess) {
|
||||||
|
const base::FilePath filepath(attachments_dir.Append(filename));
|
||||||
|
std::unique_ptr<FileReader> file_reader(std::make_unique<FileReader>());
|
||||||
|
if (!file_reader->Open(filepath)) {
|
||||||
|
LOG(ERROR) << "attachment " << filepath.value().c_str()
|
||||||
|
<< " couldn't be opened, skipping";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
attachment_readers_.emplace_back(std::move(file_reader));
|
||||||
|
attachment_map_[base::UTF16ToUTF8(filename.value())] =
|
||||||
|
attachment_readers_.back().get();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CrashReportDatabaseWin::CrashReportDatabaseWin(const base::FilePath& path)
|
CrashReportDatabaseWin::CrashReportDatabaseWin(const base::FilePath& path)
|
||||||
@ -700,6 +795,14 @@ OperationStatus CrashReportDatabaseWin::FinishedWritingCrashReport(
|
|||||||
|
|
||||||
ignore_result(report->file_remover_.release());
|
ignore_result(report->file_remover_.release());
|
||||||
|
|
||||||
|
// Close all the attachments and disarm their removers too.
|
||||||
|
for (auto& writer : report->attachment_writers_) {
|
||||||
|
writer->Close();
|
||||||
|
}
|
||||||
|
for (auto& remover : report->attachment_removers_) {
|
||||||
|
ignore_result(remover.release());
|
||||||
|
}
|
||||||
|
|
||||||
*uuid = report->ReportID();
|
*uuid = report->ReportID();
|
||||||
|
|
||||||
Metrics::CrashReportPending(Metrics::PendingReportReason::kNewlyCreated);
|
Metrics::CrashReportPending(Metrics::PendingReportReason::kNewlyCreated);
|
||||||
@ -826,9 +929,33 @@ OperationStatus CrashReportDatabaseWin::DeleteReport(const UUID& uuid) {
|
|||||||
<< base::UTF16ToUTF8(report_path.value());
|
<< base::UTF16ToUTF8(report_path.value());
|
||||||
return kFileSystemError;
|
return kFileSystemError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
RemoveAttachmentsByUUID(uuid);
|
||||||
|
|
||||||
return kNoError;
|
return kNoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CrashReportDatabaseWin::RemoveAttachmentsByUUID(const UUID& uuid) {
|
||||||
|
base::FilePath attachments_dir = AttachmentsPath(uuid);
|
||||||
|
if (!IsDirectory(attachments_dir, /*allow_symlinks=*/false)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DirectoryReader reader;
|
||||||
|
if (!reader.Open(attachments_dir)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
base::FilePath filename;
|
||||||
|
DirectoryReader::Result result;
|
||||||
|
while ((result = reader.NextFile(&filename)) ==
|
||||||
|
DirectoryReader::Result::kSuccess) {
|
||||||
|
const base::FilePath filepath(attachments_dir.Append(filename));
|
||||||
|
LoggingRemoveFile(filepath);
|
||||||
|
}
|
||||||
|
|
||||||
|
LoggingRemoveDirectory(attachments_dir);
|
||||||
|
}
|
||||||
|
|
||||||
OperationStatus CrashReportDatabaseWin::SkipReportUpload(
|
OperationStatus CrashReportDatabaseWin::SkipReportUpload(
|
||||||
const UUID& uuid,
|
const UUID& uuid,
|
||||||
Metrics::CrashSkippedReason reason) {
|
Metrics::CrashSkippedReason reason) {
|
||||||
@ -897,6 +1024,86 @@ OperationStatus CrashReportDatabaseWin::RequestUpload(const UUID& uuid) {
|
|||||||
return kNoError;
|
return kNoError;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int CrashReportDatabaseWin::CleanDatabase(time_t lockfile_ttl) {
|
||||||
|
int removed = 0;
|
||||||
|
const base::FilePath dir_path(base_dir_.Append(kReportsDirectory));
|
||||||
|
DirectoryReader reader;
|
||||||
|
if (!reader.Open(dir_path)) {
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
base::FilePath filename;
|
||||||
|
DirectoryReader::Result result;
|
||||||
|
time_t now = time(nullptr);
|
||||||
|
|
||||||
|
std::unique_ptr<Metadata> metadata(AcquireMetadata());
|
||||||
|
|
||||||
|
// Remove old reports without metadata.
|
||||||
|
while ((result = reader.NextFile(&filename)) ==
|
||||||
|
DirectoryReader::Result::kSuccess) {
|
||||||
|
timespec filetime;
|
||||||
|
const base::FilePath report_path(dir_path.Append(filename));
|
||||||
|
if (!FileModificationTime(report_path, &filetime) ||
|
||||||
|
filetime.tv_sec > now - lockfile_ttl) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ReportDisk* report_disk;
|
||||||
|
UUID uuid;
|
||||||
|
bool is_uuid = UUIDFromReportPath(report_path, &uuid);
|
||||||
|
// ignore files whose base name is not uuid
|
||||||
|
if (!is_uuid) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
OperationStatus os = metadata->FindSingleReport(uuid, &report_disk);
|
||||||
|
|
||||||
|
if (os == OperationStatus::kReportNotFound) {
|
||||||
|
if (LoggingRemoveFile(report_path)) {
|
||||||
|
++removed;
|
||||||
|
RemoveAttachmentsByUUID(uuid);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove any metadata records without report files.
|
||||||
|
removed += metadata->CleanDatabase();
|
||||||
|
|
||||||
|
CleanOrphanedAttachments();
|
||||||
|
return removed;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CrashReportDatabaseWin::CleanOrphanedAttachments() {
|
||||||
|
base::FilePath root_attachments_dir(base_dir_.Append(kAttachmentsDirectory));
|
||||||
|
DirectoryReader reader;
|
||||||
|
if (!reader.Open(root_attachments_dir)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
base::FilePath filename;
|
||||||
|
DirectoryReader::Result result;
|
||||||
|
base::FilePath reports_dir = base_dir_.Append(kReportsDirectory);
|
||||||
|
while ((result = reader.NextFile(&filename)) ==
|
||||||
|
DirectoryReader::Result::kSuccess) {
|
||||||
|
const base::FilePath path(root_attachments_dir.Append(filename));
|
||||||
|
if (IsDirectory(path, false)) {
|
||||||
|
UUID uuid;
|
||||||
|
if (!uuid.InitializeFromString(filename.value())) {
|
||||||
|
LOG(ERROR) << "unexpected attachment dir name "
|
||||||
|
<< filename.value().c_str();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Remove attachments if corresponding report doen't exist.
|
||||||
|
base::FilePath report_path =
|
||||||
|
reports_dir.Append(uuid.ToString16() + kCrashReportFileExtension);
|
||||||
|
if (!IsRegularFile(report_path)) {
|
||||||
|
RemoveAttachmentsByUUID(uuid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
std::unique_ptr<CrashReportDatabase> CrashReportDatabase::Initialize(
|
std::unique_ptr<CrashReportDatabase> CrashReportDatabase::Initialize(
|
||||||
const base::FilePath& path) {
|
const base::FilePath& path) {
|
||||||
|
@ -107,6 +107,8 @@ class CrashpadClient {
|
|||||||
//! a background thread. Optionally, WaitForHandlerStart() can be used at
|
//! a background thread. Optionally, WaitForHandlerStart() can be used at
|
||||||
//! a suitable time to retreive the result of background startup. This
|
//! a suitable time to retreive the result of background startup. This
|
||||||
//! option is only used on Windows.
|
//! option is only used on Windows.
|
||||||
|
//! \param[in] attachments Vector that stores file paths that should be
|
||||||
|
//! captured with each report at the time of the crash.
|
||||||
//!
|
//!
|
||||||
//! \return `true` on success, `false` on failure with a message logged.
|
//! \return `true` on success, `false` on failure with a message logged.
|
||||||
bool StartHandler(const base::FilePath& handler,
|
bool StartHandler(const base::FilePath& handler,
|
||||||
@ -116,7 +118,8 @@ class CrashpadClient {
|
|||||||
const std::map<std::string, std::string>& annotations,
|
const std::map<std::string, std::string>& annotations,
|
||||||
const std::vector<std::string>& arguments,
|
const std::vector<std::string>& arguments,
|
||||||
bool restartable,
|
bool restartable,
|
||||||
bool asynchronous_start);
|
bool asynchronous_start,
|
||||||
|
const std::vector<base::FilePath>& attachments = {});
|
||||||
|
|
||||||
#if defined(OS_ANDROID) || defined(OS_LINUX) || DOXYGEN
|
#if defined(OS_ANDROID) || defined(OS_LINUX) || DOXYGEN
|
||||||
//! \brief Retrieve the socket and process ID for the handler.
|
//! \brief Retrieve the socket and process ID for the handler.
|
||||||
@ -340,7 +343,8 @@ class CrashpadClient {
|
|||||||
const base::FilePath& metrics_dir,
|
const base::FilePath& metrics_dir,
|
||||||
const std::string& url,
|
const std::string& url,
|
||||||
const std::map<std::string, std::string>& annotations,
|
const std::map<std::string, std::string>& annotations,
|
||||||
const std::vector<std::string>& arguments);
|
const std::vector<std::string>& arguments,
|
||||||
|
const std::vector<base::FilePath>& attachments = {});
|
||||||
|
|
||||||
//! \brief Starts a handler process with an initial client.
|
//! \brief Starts a handler process with an initial client.
|
||||||
//!
|
//!
|
||||||
|
@ -39,7 +39,9 @@ bool CrashpadClient::StartHandler(
|
|||||||
const std::map<std::string, std::string>& annotations,
|
const std::map<std::string, std::string>& annotations,
|
||||||
const std::vector<std::string>& arguments,
|
const std::vector<std::string>& arguments,
|
||||||
bool restartable,
|
bool restartable,
|
||||||
bool asynchronous_start) {
|
bool asynchronous_start,
|
||||||
|
const std::vector<base::FilePath>& attachments) {
|
||||||
|
DCHECK(attachments.empty()); // Attachments are not implemented on Fuchsia yet.
|
||||||
DCHECK_EQ(restartable, false); // Not used on Fuchsia.
|
DCHECK_EQ(restartable, false); // Not used on Fuchsia.
|
||||||
DCHECK_EQ(asynchronous_start, false); // Not used on Fuchsia.
|
DCHECK_EQ(asynchronous_start, false); // Not used on Fuchsia.
|
||||||
|
|
||||||
|
@ -372,7 +372,8 @@ bool CrashpadClient::StartHandler(
|
|||||||
const std::map<std::string, std::string>& annotations,
|
const std::map<std::string, std::string>& annotations,
|
||||||
const std::vector<std::string>& arguments,
|
const std::vector<std::string>& arguments,
|
||||||
bool restartable,
|
bool restartable,
|
||||||
bool asynchronous_start) {
|
bool asynchronous_start,
|
||||||
|
const std::vector<base::FilePath>& attachments) {
|
||||||
DCHECK(!asynchronous_start);
|
DCHECK(!asynchronous_start);
|
||||||
|
|
||||||
ScopedFileHandle client_sock, handler_sock;
|
ScopedFileHandle client_sock, handler_sock;
|
||||||
@ -382,7 +383,7 @@ bool CrashpadClient::StartHandler(
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<std::string> argv = BuildHandlerArgvStrings(
|
std::vector<std::string> argv = BuildHandlerArgvStrings(
|
||||||
handler, database, metrics_dir, url, annotations, arguments);
|
handler, database, metrics_dir, url, annotations, arguments, attachments);
|
||||||
|
|
||||||
argv.push_back(FormatArgumentInt("initial-client-fd", handler_sock.get()));
|
argv.push_back(FormatArgumentInt("initial-client-fd", handler_sock.get()));
|
||||||
argv.push_back("--shared-client-connection");
|
argv.push_back("--shared-client-connection");
|
||||||
@ -507,9 +508,10 @@ bool CrashpadClient::StartHandlerAtCrash(
|
|||||||
const base::FilePath& metrics_dir,
|
const base::FilePath& metrics_dir,
|
||||||
const std::string& url,
|
const std::string& url,
|
||||||
const std::map<std::string, std::string>& annotations,
|
const std::map<std::string, std::string>& annotations,
|
||||||
const std::vector<std::string>& arguments) {
|
const std::vector<std::string>& arguments,
|
||||||
|
const std::vector<base::FilePath>& attachments) {
|
||||||
std::vector<std::string> argv = BuildHandlerArgvStrings(
|
std::vector<std::string> argv = BuildHandlerArgvStrings(
|
||||||
handler, database, metrics_dir, url, annotations, arguments);
|
handler, database, metrics_dir, url, annotations, arguments, attachments);
|
||||||
|
|
||||||
auto signal_handler = LaunchAtCrashHandler::Get();
|
auto signal_handler = LaunchAtCrashHandler::Get();
|
||||||
return signal_handler->Initialize(&argv, nullptr, &unhandled_signals_);
|
return signal_handler->Initialize(&argv, nullptr, &unhandled_signals_);
|
||||||
|
@ -89,14 +89,16 @@ bool HandleCrashSuccessfully(int, siginfo_t*, ucontext_t*) {
|
|||||||
bool InstallHandler(CrashpadClient* client,
|
bool InstallHandler(CrashpadClient* client,
|
||||||
bool start_at_crash,
|
bool start_at_crash,
|
||||||
const base::FilePath& handler_path,
|
const base::FilePath& handler_path,
|
||||||
const base::FilePath& database_path) {
|
const base::FilePath& database_path,
|
||||||
|
const std::vector<base::FilePath>& attachments) {
|
||||||
return start_at_crash
|
return start_at_crash
|
||||||
? client->StartHandlerAtCrash(handler_path,
|
? client->StartHandlerAtCrash(handler_path,
|
||||||
database_path,
|
database_path,
|
||||||
base::FilePath(),
|
base::FilePath(),
|
||||||
"",
|
"",
|
||||||
std::map<std::string, std::string>(),
|
std::map<std::string, std::string>(),
|
||||||
std::vector<std::string>())
|
std::vector<std::string>(),
|
||||||
|
attachments)
|
||||||
: client->StartHandler(handler_path,
|
: client->StartHandler(handler_path,
|
||||||
database_path,
|
database_path,
|
||||||
base::FilePath(),
|
base::FilePath(),
|
||||||
@ -104,16 +106,28 @@ bool InstallHandler(CrashpadClient* client,
|
|||||||
std::map<std::string, std::string>(),
|
std::map<std::string, std::string>(),
|
||||||
std::vector<std::string>(),
|
std::vector<std::string>(),
|
||||||
false,
|
false,
|
||||||
false);
|
false,
|
||||||
|
attachments);
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr char kTestAnnotationName[] = "name_of_annotation";
|
constexpr char kTestAnnotationName[] = "name_of_annotation";
|
||||||
constexpr char kTestAnnotationValue[] = "value_of_annotation";
|
constexpr char kTestAnnotationValue[] = "value_of_annotation";
|
||||||
|
constexpr char kTestAttachmentName[] = "test_attachment";
|
||||||
|
constexpr char kTestAttachmentContent[] = "attachment_content";
|
||||||
|
|
||||||
#if defined(OS_ANDROID)
|
#if defined(OS_ANDROID)
|
||||||
constexpr char kTestAbortMessage[] = "test abort message";
|
constexpr char kTestAbortMessage[] = "test abort message";
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
void ValidateAttachment(const CrashReportDatabase::UploadReport* report) {
|
||||||
|
auto attachments = report->GetAttachments();
|
||||||
|
ASSERT_EQ(attachments.size(), 1u);
|
||||||
|
char buf[sizeof(kTestAttachmentContent)];
|
||||||
|
attachments.at(kTestAttachmentName)->Read(buf, sizeof(buf));
|
||||||
|
ASSERT_EQ(memcmp(kTestAttachmentContent, buf, sizeof(kTestAttachmentContent)),
|
||||||
|
0);
|
||||||
|
}
|
||||||
|
|
||||||
void ValidateDump(const CrashReportDatabase::UploadReport* report) {
|
void ValidateDump(const CrashReportDatabase::UploadReport* report) {
|
||||||
ProcessSnapshotMinidump minidump_snapshot;
|
ProcessSnapshotMinidump minidump_snapshot;
|
||||||
ASSERT_TRUE(minidump_snapshot.Initialize(report->Reader()));
|
ASSERT_TRUE(minidump_snapshot.Initialize(report->Reader()));
|
||||||
@ -129,6 +143,7 @@ void ValidateDump(const CrashReportDatabase::UploadReport* report) {
|
|||||||
EXPECT_EQ(kTestAbortMessage, abort_message->second);
|
EXPECT_EQ(kTestAbortMessage, abort_message->second);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
ValidateAttachment(report);
|
||||||
|
|
||||||
for (const ModuleSnapshot* module : minidump_snapshot.Modules()) {
|
for (const ModuleSnapshot* module : minidump_snapshot.Modules()) {
|
||||||
for (const AnnotationSnapshot& annotation : module->AnnotationObjects()) {
|
for (const AnnotationSnapshot& annotation : module->AnnotationObjects()) {
|
||||||
@ -169,11 +184,15 @@ CRASHPAD_CHILD_TEST_MAIN(StartHandlerForSelfTestChild) {
|
|||||||
static StringAnnotation<32> test_annotation(kTestAnnotationName);
|
static StringAnnotation<32> test_annotation(kTestAnnotationName);
|
||||||
test_annotation.Set(kTestAnnotationValue);
|
test_annotation.Set(kTestAnnotationValue);
|
||||||
|
|
||||||
|
const std::vector<base::FilePath> attachments = {
|
||||||
|
base::FilePath(kTestAttachmentName)};
|
||||||
|
|
||||||
crashpad::CrashpadClient client;
|
crashpad::CrashpadClient client;
|
||||||
if (!InstallHandler(&client,
|
if (!InstallHandler(&client,
|
||||||
options.start_handler_at_crash,
|
options.start_handler_at_crash,
|
||||||
handler_path,
|
handler_path,
|
||||||
base::FilePath(temp_dir))) {
|
base::FilePath(temp_dir),
|
||||||
|
attachments)) {
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -209,6 +228,16 @@ class StartHandlerForSelfInChildTest : public MultiprocessExec {
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
void MultiprocessParent() override {
|
void MultiprocessParent() override {
|
||||||
|
FileWriter writer;
|
||||||
|
base::FilePath test_attachment_path = base::FilePath(kTestAttachmentName);
|
||||||
|
bool is_created = writer.Open(test_attachment_path,
|
||||||
|
FileWriteMode::kCreateOrFail,
|
||||||
|
FilePermissions::kOwnerOnly);
|
||||||
|
ASSERT_TRUE(is_created);
|
||||||
|
ScopedRemoveFile attachment_remover(test_attachment_path);
|
||||||
|
writer.Write(kTestAttachmentContent, sizeof(kTestAttachmentContent));
|
||||||
|
writer.Close();
|
||||||
|
|
||||||
ScopedTempDir temp_dir;
|
ScopedTempDir temp_dir;
|
||||||
VMSize temp_dir_length = temp_dir.path().value().size();
|
VMSize temp_dir_length = temp_dir.path().value().size();
|
||||||
ASSERT_TRUE(LoggingWriteFile(
|
ASSERT_TRUE(LoggingWriteFile(
|
||||||
|
@ -446,7 +446,11 @@ bool CrashpadClient::StartHandler(
|
|||||||
const std::map<std::string, std::string>& annotations,
|
const std::map<std::string, std::string>& annotations,
|
||||||
const std::vector<std::string>& arguments,
|
const std::vector<std::string>& arguments,
|
||||||
bool restartable,
|
bool restartable,
|
||||||
bool asynchronous_start) {
|
bool asynchronous_start,
|
||||||
|
const std::vector<base::FilePath>& attachments) {
|
||||||
|
// Attachments are not implemented on MacOS yet.
|
||||||
|
DCHECK(attachments.empty());
|
||||||
|
|
||||||
// The “restartable” behavior can only be selected on OS X 10.10 and later. In
|
// The “restartable” behavior can only be selected on OS X 10.10 and later. In
|
||||||
// previous OS versions, if the initial client were to crash while attempting
|
// previous OS versions, if the initial client were to crash while attempting
|
||||||
// to restart the handler, it would become an unkillable process.
|
// to restart the handler, it would become an unkillable process.
|
||||||
|
@ -310,6 +310,7 @@ struct BackgroundHandlerStartThreadData {
|
|||||||
const std::string& url,
|
const std::string& url,
|
||||||
const std::map<std::string, std::string>& annotations,
|
const std::map<std::string, std::string>& annotations,
|
||||||
const std::vector<std::string>& arguments,
|
const std::vector<std::string>& arguments,
|
||||||
|
const std::vector<base::FilePath>& attachments,
|
||||||
const std::wstring& ipc_pipe,
|
const std::wstring& ipc_pipe,
|
||||||
ScopedFileHANDLE ipc_pipe_handle)
|
ScopedFileHANDLE ipc_pipe_handle)
|
||||||
: handler(handler),
|
: handler(handler),
|
||||||
@ -318,6 +319,7 @@ struct BackgroundHandlerStartThreadData {
|
|||||||
url(url),
|
url(url),
|
||||||
annotations(annotations),
|
annotations(annotations),
|
||||||
arguments(arguments),
|
arguments(arguments),
|
||||||
|
attachments(attachments),
|
||||||
ipc_pipe(ipc_pipe),
|
ipc_pipe(ipc_pipe),
|
||||||
ipc_pipe_handle(std::move(ipc_pipe_handle)) {}
|
ipc_pipe_handle(std::move(ipc_pipe_handle)) {}
|
||||||
|
|
||||||
@ -327,6 +329,7 @@ struct BackgroundHandlerStartThreadData {
|
|||||||
std::string url;
|
std::string url;
|
||||||
std::map<std::string, std::string> annotations;
|
std::map<std::string, std::string> annotations;
|
||||||
std::vector<std::string> arguments;
|
std::vector<std::string> arguments;
|
||||||
|
std::vector<base::FilePath> attachments;
|
||||||
std::wstring ipc_pipe;
|
std::wstring ipc_pipe;
|
||||||
ScopedFileHANDLE ipc_pipe_handle;
|
ScopedFileHANDLE ipc_pipe_handle;
|
||||||
};
|
};
|
||||||
@ -382,6 +385,11 @@ bool StartHandlerProcess(
|
|||||||
base::UTF8ToUTF16(kv.first + '=' + kv.second)),
|
base::UTF8ToUTF16(kv.first + '=' + kv.second)),
|
||||||
&command_line);
|
&command_line);
|
||||||
}
|
}
|
||||||
|
for (const base::FilePath& attachment : data->attachments) {
|
||||||
|
AppendCommandLineArgument(
|
||||||
|
FormatArgumentString("attachment", attachment.value()),
|
||||||
|
&command_line);
|
||||||
|
}
|
||||||
|
|
||||||
ScopedKernelHANDLE this_process(
|
ScopedKernelHANDLE this_process(
|
||||||
OpenProcess(kXPProcessAllAccess, true, GetCurrentProcessId()));
|
OpenProcess(kXPProcessAllAccess, true, GetCurrentProcessId()));
|
||||||
@ -599,7 +607,8 @@ bool CrashpadClient::StartHandler(
|
|||||||
const std::map<std::string, std::string>& annotations,
|
const std::map<std::string, std::string>& annotations,
|
||||||
const std::vector<std::string>& arguments,
|
const std::vector<std::string>& arguments,
|
||||||
bool restartable,
|
bool restartable,
|
||||||
bool asynchronous_start) {
|
bool asynchronous_start,
|
||||||
|
const std::vector<base::FilePath>& attachments) {
|
||||||
DCHECK(ipc_pipe_.empty());
|
DCHECK(ipc_pipe_.empty());
|
||||||
|
|
||||||
// Both the pipe and the signalling events have to be created on the main
|
// Both the pipe and the signalling events have to be created on the main
|
||||||
@ -629,6 +638,7 @@ bool CrashpadClient::StartHandler(
|
|||||||
url,
|
url,
|
||||||
annotations,
|
annotations,
|
||||||
arguments,
|
arguments,
|
||||||
|
attachments,
|
||||||
ipc_pipe_,
|
ipc_pipe_,
|
||||||
std::move(ipc_pipe_handle));
|
std::move(ipc_pipe_handle));
|
||||||
|
|
||||||
|
@ -101,6 +101,10 @@ void Usage(const base::FilePath& me) {
|
|||||||
"Crashpad's exception handler server.\n"
|
"Crashpad's exception handler server.\n"
|
||||||
"\n"
|
"\n"
|
||||||
" --annotation=KEY=VALUE set a process annotation in each crash report\n"
|
" --annotation=KEY=VALUE set a process annotation in each crash report\n"
|
||||||
|
#if defined(OS_WIN) || defined(OS_LINUX)
|
||||||
|
" --attachment=FILE_PATH attach specified file to each crash report\n"
|
||||||
|
" at the time of the crash\n"
|
||||||
|
#endif // OS_WIN || OS_LINUX
|
||||||
" --database=PATH store the crash report database at PATH\n"
|
" --database=PATH store the crash report database at PATH\n"
|
||||||
#if defined(OS_MACOSX)
|
#if defined(OS_MACOSX)
|
||||||
" --handshake-fd=FD establish communication with the client over FD\n"
|
" --handshake-fd=FD establish communication with the client over FD\n"
|
||||||
@ -211,6 +215,9 @@ struct Options {
|
|||||||
base::FilePath minidump_dir_for_tests;
|
base::FilePath minidump_dir_for_tests;
|
||||||
bool always_allow_feedback = false;
|
bool always_allow_feedback = false;
|
||||||
#endif // OS_CHROMEOS
|
#endif // OS_CHROMEOS
|
||||||
|
#if defined(OS_WIN) || defined (OS_LINUX)
|
||||||
|
std::vector<base::FilePath> attachments;
|
||||||
|
#endif // OS_WIN || OS_LINUX
|
||||||
};
|
};
|
||||||
|
|
||||||
// Splits |key_value| on '=' and inserts the resulting key and value into |map|.
|
// Splits |key_value| on '=' and inserts the resulting key and value into |map|.
|
||||||
@ -516,6 +523,9 @@ int HandlerMain(int argc,
|
|||||||
// Long options without short equivalents.
|
// Long options without short equivalents.
|
||||||
kOptionLastChar = 255,
|
kOptionLastChar = 255,
|
||||||
kOptionAnnotation,
|
kOptionAnnotation,
|
||||||
|
#if defined(OS_WIN) || defined(OS_LINUX)
|
||||||
|
kOptionAttachment,
|
||||||
|
#endif // OS_WIN || OS_LINUX
|
||||||
kOptionDatabase,
|
kOptionDatabase,
|
||||||
#if defined(OS_MACOSX)
|
#if defined(OS_MACOSX)
|
||||||
kOptionHandshakeFD,
|
kOptionHandshakeFD,
|
||||||
@ -568,6 +578,9 @@ int HandlerMain(int argc,
|
|||||||
|
|
||||||
static constexpr option long_options[] = {
|
static constexpr option long_options[] = {
|
||||||
{"annotation", required_argument, nullptr, kOptionAnnotation},
|
{"annotation", required_argument, nullptr, kOptionAnnotation},
|
||||||
|
#if defined(OS_WIN) || defined(OS_LINUX)
|
||||||
|
{"attachment", required_argument, nullptr, kOptionAttachment},
|
||||||
|
#endif // OS_WIN || OS_LINUX
|
||||||
{"database", required_argument, nullptr, kOptionDatabase},
|
{"database", required_argument, nullptr, kOptionDatabase},
|
||||||
#if defined(OS_MACOSX)
|
#if defined(OS_MACOSX)
|
||||||
{"handshake-fd", required_argument, nullptr, kOptionHandshakeFD},
|
{"handshake-fd", required_argument, nullptr, kOptionHandshakeFD},
|
||||||
@ -677,6 +690,13 @@ int HandlerMain(int argc,
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
#if defined(OS_WIN) || defined(OS_LINUX)
|
||||||
|
case kOptionAttachment: {
|
||||||
|
options.attachments.push_back(base::FilePath(
|
||||||
|
ToolSupport::CommandLineArgumentToFilePathStringType(optarg)));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#endif // OS_WIN || OS_LINUX
|
||||||
case kOptionDatabase: {
|
case kOptionDatabase: {
|
||||||
options.database = base::FilePath(
|
options.database = base::FilePath(
|
||||||
ToolSupport::CommandLineArgumentToFilePathStringType(optarg));
|
ToolSupport::CommandLineArgumentToFilePathStringType(optarg));
|
||||||
@ -988,6 +1008,9 @@ int HandlerMain(int argc,
|
|||||||
database.get(),
|
database.get(),
|
||||||
static_cast<CrashReportUploadThread*>(upload_thread.Get()),
|
static_cast<CrashReportUploadThread*>(upload_thread.Get()),
|
||||||
&options.annotations,
|
&options.annotations,
|
||||||
|
#if defined(OS_WIN) || defined(OS_LINUX)
|
||||||
|
&options.attachments,
|
||||||
|
#endif // OS_WIN || OS_LINUX
|
||||||
#if defined(OS_ANDROID)
|
#if defined(OS_ANDROID)
|
||||||
options.write_minidump_to_database,
|
options.write_minidump_to_database,
|
||||||
options.write_minidump_to_log,
|
options.write_minidump_to_log,
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include "minidump/minidump_file_writer.h"
|
#include "minidump/minidump_file_writer.h"
|
||||||
#include "snapshot/linux/process_snapshot_linux.h"
|
#include "snapshot/linux/process_snapshot_linux.h"
|
||||||
#include "snapshot/sanitized/process_snapshot_sanitized.h"
|
#include "snapshot/sanitized/process_snapshot_sanitized.h"
|
||||||
|
#include "util/file/file_helper.h"
|
||||||
#include "util/file/file_reader.h"
|
#include "util/file/file_reader.h"
|
||||||
#include "util/file/output_stream_file_writer.h"
|
#include "util/file/output_stream_file_writer.h"
|
||||||
#include "util/linux/direct_ptrace_connection.h"
|
#include "util/linux/direct_ptrace_connection.h"
|
||||||
@ -62,12 +63,14 @@ CrashReportExceptionHandler::CrashReportExceptionHandler(
|
|||||||
CrashReportDatabase* database,
|
CrashReportDatabase* database,
|
||||||
CrashReportUploadThread* upload_thread,
|
CrashReportUploadThread* upload_thread,
|
||||||
const std::map<std::string, std::string>* process_annotations,
|
const std::map<std::string, std::string>* process_annotations,
|
||||||
|
const std::vector<base::FilePath>* attachments,
|
||||||
bool write_minidump_to_database,
|
bool write_minidump_to_database,
|
||||||
bool write_minidump_to_log,
|
bool write_minidump_to_log,
|
||||||
const UserStreamDataSources* user_stream_data_sources)
|
const UserStreamDataSources* user_stream_data_sources)
|
||||||
: database_(database),
|
: database_(database),
|
||||||
upload_thread_(upload_thread),
|
upload_thread_(upload_thread),
|
||||||
process_annotations_(process_annotations),
|
process_annotations_(process_annotations),
|
||||||
|
attachments_(attachments),
|
||||||
write_minidump_to_database_(write_minidump_to_database),
|
write_minidump_to_database_(write_minidump_to_database),
|
||||||
write_minidump_to_log_(write_minidump_to_log),
|
write_minidump_to_log_(write_minidump_to_log),
|
||||||
user_stream_data_sources_(user_stream_data_sources) {
|
user_stream_data_sources_(user_stream_data_sources) {
|
||||||
@ -200,6 +203,25 @@ bool CrashReportExceptionHandler::WriteMinidumpToDatabase(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const auto& attachment : (*attachments_)) {
|
||||||
|
FileReader file_reader;
|
||||||
|
if (!file_reader.Open(attachment)) {
|
||||||
|
LOG(ERROR) << "attachment " << attachment.value().c_str()
|
||||||
|
<< " couldn't be opened, skipping";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
base::FilePath filename = attachment.BaseName();
|
||||||
|
FileWriter* file_writer = new_report->AddAttachment(filename.value());
|
||||||
|
if (file_writer == nullptr) {
|
||||||
|
LOG(ERROR) << "attachment " << filename.value().c_str()
|
||||||
|
<< " couldn't be created, skipping";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
CopyFileContent(&file_reader, file_writer);
|
||||||
|
}
|
||||||
|
|
||||||
UUID uuid;
|
UUID uuid;
|
||||||
database_status =
|
database_status =
|
||||||
database_->FinishedWritingCrashReport(std::move(new_report), &uuid);
|
database_->FinishedWritingCrashReport(std::move(new_report), &uuid);
|
||||||
|
@ -53,6 +53,8 @@ class CrashReportExceptionHandler : public ExceptionHandlerServer::Delegate {
|
|||||||
//! To interoperate with Breakpad servers, the recommended practice is to
|
//! To interoperate with Breakpad servers, the recommended practice is to
|
||||||
//! specify values for the `"prod"` and `"ver"` keys as process
|
//! specify values for the `"prod"` and `"ver"` keys as process
|
||||||
//! annotations.
|
//! annotations.
|
||||||
|
//! \param[in] attachments A vector of file paths that should be captured with
|
||||||
|
//! each report at the time of the crash.
|
||||||
//! \param[in] write_minidump_to_database Whether the minidump shall be
|
//! \param[in] write_minidump_to_database Whether the minidump shall be
|
||||||
//! written to database.
|
//! written to database.
|
||||||
//! \param[in] write_minidump_to_log Whether the minidump shall be written to
|
//! \param[in] write_minidump_to_log Whether the minidump shall be written to
|
||||||
@ -65,6 +67,7 @@ class CrashReportExceptionHandler : public ExceptionHandlerServer::Delegate {
|
|||||||
CrashReportDatabase* database,
|
CrashReportDatabase* database,
|
||||||
CrashReportUploadThread* upload_thread,
|
CrashReportUploadThread* upload_thread,
|
||||||
const std::map<std::string, std::string>* process_annotations,
|
const std::map<std::string, std::string>* process_annotations,
|
||||||
|
const std::vector<base::FilePath>* attachments,
|
||||||
bool write_minidump_to_database,
|
bool write_minidump_to_database,
|
||||||
bool write_minidump_to_log,
|
bool write_minidump_to_log,
|
||||||
const UserStreamDataSources* user_stream_data_sources);
|
const UserStreamDataSources* user_stream_data_sources);
|
||||||
@ -106,6 +109,7 @@ class CrashReportExceptionHandler : public ExceptionHandlerServer::Delegate {
|
|||||||
CrashReportDatabase* database_; // weak
|
CrashReportDatabase* database_; // weak
|
||||||
CrashReportUploadThread* upload_thread_; // weak
|
CrashReportUploadThread* upload_thread_; // weak
|
||||||
const std::map<std::string, std::string>* process_annotations_; // weak
|
const std::map<std::string, std::string>* process_annotations_; // weak
|
||||||
|
const std::vector<base::FilePath>* attachments_; // weak
|
||||||
bool write_minidump_to_database_;
|
bool write_minidump_to_database_;
|
||||||
bool write_minidump_to_log_;
|
bool write_minidump_to_log_;
|
||||||
const UserStreamDataSources* user_stream_data_sources_; // weak
|
const UserStreamDataSources* user_stream_data_sources_; // weak
|
||||||
|
@ -17,12 +17,14 @@
|
|||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
|
#include "base/strings/utf_string_conversions.h"
|
||||||
#include "client/crash_report_database.h"
|
#include "client/crash_report_database.h"
|
||||||
#include "client/settings.h"
|
#include "client/settings.h"
|
||||||
#include "handler/crash_report_upload_thread.h"
|
#include "handler/crash_report_upload_thread.h"
|
||||||
#include "minidump/minidump_file_writer.h"
|
#include "minidump/minidump_file_writer.h"
|
||||||
#include "minidump/minidump_user_extension_stream_data_source.h"
|
#include "minidump/minidump_user_extension_stream_data_source.h"
|
||||||
#include "snapshot/win/process_snapshot_win.h"
|
#include "snapshot/win/process_snapshot_win.h"
|
||||||
|
#include "util/file/file_helper.h"
|
||||||
#include "util/file/file_writer.h"
|
#include "util/file/file_writer.h"
|
||||||
#include "util/misc/metrics.h"
|
#include "util/misc/metrics.h"
|
||||||
#include "util/win/registration_protocol_win.h"
|
#include "util/win/registration_protocol_win.h"
|
||||||
@ -35,17 +37,17 @@ CrashReportExceptionHandler::CrashReportExceptionHandler(
|
|||||||
CrashReportDatabase* database,
|
CrashReportDatabase* database,
|
||||||
CrashReportUploadThread* upload_thread,
|
CrashReportUploadThread* upload_thread,
|
||||||
const std::map<std::string, std::string>* process_annotations,
|
const std::map<std::string, std::string>* process_annotations,
|
||||||
|
const std::vector<base::FilePath>* attachments,
|
||||||
const UserStreamDataSources* user_stream_data_sources)
|
const UserStreamDataSources* user_stream_data_sources)
|
||||||
: database_(database),
|
: database_(database),
|
||||||
upload_thread_(upload_thread),
|
upload_thread_(upload_thread),
|
||||||
process_annotations_(process_annotations),
|
process_annotations_(process_annotations),
|
||||||
|
attachments_(attachments),
|
||||||
user_stream_data_sources_(user_stream_data_sources) {}
|
user_stream_data_sources_(user_stream_data_sources) {}
|
||||||
|
|
||||||
CrashReportExceptionHandler::~CrashReportExceptionHandler() {
|
CrashReportExceptionHandler::~CrashReportExceptionHandler() {}
|
||||||
}
|
|
||||||
|
|
||||||
void CrashReportExceptionHandler::ExceptionHandlerServerStarted() {
|
void CrashReportExceptionHandler::ExceptionHandlerServerStarted() {}
|
||||||
}
|
|
||||||
|
|
||||||
unsigned int CrashReportExceptionHandler::ExceptionHandlerServerException(
|
unsigned int CrashReportExceptionHandler::ExceptionHandlerServerException(
|
||||||
HANDLE process,
|
HANDLE process,
|
||||||
@ -114,6 +116,26 @@ unsigned int CrashReportExceptionHandler::ExceptionHandlerServerException(
|
|||||||
return termination_code;
|
return termination_code;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const auto& attachment : (*attachments_)) {
|
||||||
|
FileReader file_reader;
|
||||||
|
if (!file_reader.Open(attachment)) {
|
||||||
|
LOG(ERROR) << "attachment " << attachment.value().c_str()
|
||||||
|
<< " couldn't be opened, skipping";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
base::FilePath filename = attachment.BaseName();
|
||||||
|
FileWriter* file_writer =
|
||||||
|
new_report->AddAttachment(base::UTF16ToUTF8(filename.value()));
|
||||||
|
if (file_writer == nullptr) {
|
||||||
|
LOG(ERROR) << "attachment " << filename.value().c_str()
|
||||||
|
<< " couldn't be created, skipping";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
CopyFileContent(&file_reader, file_writer);
|
||||||
|
}
|
||||||
|
|
||||||
UUID uuid;
|
UUID uuid;
|
||||||
database_status =
|
database_status =
|
||||||
database_->FinishedWritingCrashReport(std::move(new_report), &uuid);
|
database_->FinishedWritingCrashReport(std::move(new_report), &uuid);
|
||||||
|
@ -50,6 +50,8 @@ class CrashReportExceptionHandler final
|
|||||||
//! To interoperate with Breakpad servers, the recommended practice is to
|
//! To interoperate with Breakpad servers, the recommended practice is to
|
||||||
//! specify values for the `"prod"` and `"ver"` keys as process
|
//! specify values for the `"prod"` and `"ver"` keys as process
|
||||||
//! annotations.
|
//! annotations.
|
||||||
|
//! \param[in] attachments A vector of file paths that should be captured with
|
||||||
|
//! each report at the time of the crash.
|
||||||
//! \param[in] user_stream_data_sources Data sources to be used to extend
|
//! \param[in] user_stream_data_sources Data sources to be used to extend
|
||||||
//! crash reports. For each crash report that is written, the data sources
|
//! crash reports. For each crash report that is written, the data sources
|
||||||
//! are called in turn. These data sources may contribute additional
|
//! are called in turn. These data sources may contribute additional
|
||||||
@ -58,6 +60,7 @@ class CrashReportExceptionHandler final
|
|||||||
CrashReportDatabase* database,
|
CrashReportDatabase* database,
|
||||||
CrashReportUploadThread* upload_thread,
|
CrashReportUploadThread* upload_thread,
|
||||||
const std::map<std::string, std::string>* process_annotations,
|
const std::map<std::string, std::string>* process_annotations,
|
||||||
|
const std::vector<base::FilePath>* attachments,
|
||||||
const UserStreamDataSources* user_stream_data_sources);
|
const UserStreamDataSources* user_stream_data_sources);
|
||||||
|
|
||||||
~CrashReportExceptionHandler();
|
~CrashReportExceptionHandler();
|
||||||
@ -76,6 +79,7 @@ class CrashReportExceptionHandler final
|
|||||||
CrashReportDatabase* database_; // weak
|
CrashReportDatabase* database_; // weak
|
||||||
CrashReportUploadThread* upload_thread_; // weak
|
CrashReportUploadThread* upload_thread_; // weak
|
||||||
const std::map<std::string, std::string>* process_annotations_; // weak
|
const std::map<std::string, std::string>* process_annotations_; // weak
|
||||||
|
const std::vector<base::FilePath>* attachments_; // weak
|
||||||
const UserStreamDataSources* user_stream_data_sources_; // weak
|
const UserStreamDataSources* user_stream_data_sources_; // weak
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(CrashReportExceptionHandler);
|
DISALLOW_COPY_AND_ASSIGN(CrashReportExceptionHandler);
|
||||||
|
@ -149,6 +149,8 @@ static_library("util") {
|
|||||||
"file/delimited_file_reader.cc",
|
"file/delimited_file_reader.cc",
|
||||||
"file/delimited_file_reader.h",
|
"file/delimited_file_reader.h",
|
||||||
"file/directory_reader.h",
|
"file/directory_reader.h",
|
||||||
|
"file/file_helper.cc",
|
||||||
|
"file/file_helper.h",
|
||||||
"file/file_io.cc",
|
"file/file_io.cc",
|
||||||
"file/file_io.h",
|
"file/file_io.h",
|
||||||
"file/file_reader.cc",
|
"file/file_reader.cc",
|
||||||
|
34
util/file/file_helper.cc
Normal file
34
util/file/file_helper.cc
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
// Copyright 2020 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 "util/file/file_helper.h"
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
|
||||||
|
void CopyFileContent(FileReaderInterface* file_reader,
|
||||||
|
FileWriterInterface* file_writer) {
|
||||||
|
char buf[4096];
|
||||||
|
FileOperationResult read_result;
|
||||||
|
do {
|
||||||
|
read_result = file_reader->Read(buf, sizeof(buf));
|
||||||
|
if (read_result < 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (read_result > 0 && !file_writer->Write(buf, read_result)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (read_result > 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace crashpad
|
29
util/file/file_helper.h
Normal file
29
util/file/file_helper.h
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
// Copyright 2020 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.
|
||||||
|
|
||||||
|
#ifndef CRASHPAD_UTIL_FILE_FILE_HELPER_H_
|
||||||
|
#define CRASHPAD_UTIL_FILE_FILE_HELPER_H_
|
||||||
|
|
||||||
|
#include "util/file/file_reader.h"
|
||||||
|
#include "util/file/file_writer.h"
|
||||||
|
|
||||||
|
namespace crashpad {
|
||||||
|
|
||||||
|
//! \brief Copy the file content from file_reader to file_writer
|
||||||
|
void CopyFileContent(FileReaderInterface* file_reader,
|
||||||
|
FileWriterInterface* file_writer);
|
||||||
|
|
||||||
|
} // namespace crashpad
|
||||||
|
|
||||||
|
#endif // CRASHPAD_UTIL_FILE_FILE_HELPER_H_
|
@ -37,6 +37,8 @@
|
|||||||
'file/directory_reader.h',
|
'file/directory_reader.h',
|
||||||
'file/directory_reader_posix.cc',
|
'file/directory_reader_posix.cc',
|
||||||
'file/directory_reader_win.cc',
|
'file/directory_reader_win.cc',
|
||||||
|
'file/file_helper.cc',
|
||||||
|
'file/file_helper.h',
|
||||||
'file/file_io.cc',
|
'file/file_io.cc',
|
||||||
'file/file_io.h',
|
'file/file_io.h',
|
||||||
'file/file_io_posix.cc',
|
'file/file_io_posix.cc',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user