Add support for attachments to generic crash report database

This is the beginning of support for attachments at the process level
being stored alongside a report. Attachments will be uploaded by key as
part of the multipart http upload. There's no interface at the client
level yet to pass these through.

As this is intended for Fuchsia, this is not yet implemented for the
Mac/Windows database implementations.

Bug: crashpad:196
Change-Id: Ieaf580675164b631d313193f97375710979ba2a9
Reviewed-on: https://chromium-review.googlesource.com/1060419
Commit-Queue: Scott Graham <scottmg@chromium.org>
Reviewed-by: Joshua Peraza <jperaza@chromium.org>
This commit is contained in:
Scott Graham 2018-05-18 15:53:54 -07:00 committed by Commit Bot
parent 75b672be06
commit 1f3052c1cc
10 changed files with 380 additions and 15 deletions

View File

@ -14,6 +14,7 @@
#include "client/crash_report_database.h"
#include "base/logging.h"
#include "build/build_config.h"
namespace crashpad {
@ -29,13 +30,21 @@ CrashReportDatabase::Report::Report()
upload_explicitly_requested(false) {}
CrashReportDatabase::NewReport::NewReport()
: writer_(std::make_unique<FileWriter>()), uuid_(), file_remover_() {}
: writer_(std::make_unique<FileWriter>()),
file_remover_(),
attachment_writers_(),
attachment_removers_(),
uuid_(),
database_() {}
CrashReportDatabase::NewReport::~NewReport() = default;
bool CrashReportDatabase::NewReport::Initialize(
CrashReportDatabase* database,
const base::FilePath& directory,
const base::FilePath::StringType& extension) {
database_ = database;
if (!uuid_.InitializeWithNew()) {
return false;
}
@ -56,7 +65,11 @@ bool CrashReportDatabase::NewReport::Initialize(
}
CrashReportDatabase::UploadReport::UploadReport()
: Report(), reader_(std::make_unique<FileReader>()), database_(nullptr) {}
: Report(),
reader_(std::make_unique<FileReader>()),
database_(nullptr),
attachment_readers_(),
attachment_map_() {}
CrashReportDatabase::UploadReport::~UploadReport() {
if (database_) {
@ -67,6 +80,7 @@ CrashReportDatabase::UploadReport::~UploadReport() {
bool CrashReportDatabase::UploadReport::Initialize(const base::FilePath path,
CrashReportDatabase* db) {
database_ = db;
InitializeAttachments();
return reader_->Open(path);
}

View File

@ -17,6 +17,7 @@
#include <time.h>
#include <map>
#include <memory>
#include <string>
#include <vector>
@ -112,19 +113,35 @@ class CrashReportDatabase {
//! A unique identifier by which this report will always be known to the
//! database.
const UUID& ReportID() { return uuid_; }
const UUID& ReportID() const { return uuid_; }
//! \brief Adds an attachment to the report.
//!
//! \note This function is not yet implemented on macOS or Windows.
//!
//! \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
//! minidump report. \a name should only use characters from the set
//! `[a-zA-Z0-9._-]`.
//! \return A FileWriter that the caller should use to write the contents of
//! the attachment, or `nullptr` on failure with an error logged.
FileWriter* AddAttachment(const std::string& name);
private:
friend class CrashReportDatabaseGeneric;
friend class CrashReportDatabaseMac;
friend class CrashReportDatabaseWin;
bool Initialize(const base::FilePath& directory,
bool Initialize(CrashReportDatabase* database,
const base::FilePath& directory,
const base::FilePath::StringType& extension);
std::unique_ptr<FileWriter> writer_;
UUID uuid_;
ScopedRemoveFile file_remover_;
std::vector<std::unique_ptr<FileWriter>> attachment_writers_;
std::vector<ScopedRemoveFile> attachment_removers_;
UUID uuid_;
CrashReportDatabase* database_;
DISALLOW_COPY_AND_ASSIGN(NewReport);
};
@ -137,9 +154,17 @@ class CrashReportDatabase {
UploadReport();
virtual ~UploadReport();
// An open FileReader with which to read the report.
//! \brief An open FileReader with which to read the report.
FileReader* Reader() const { return reader_.get(); }
//! \brief Obtains a mapping of names to file readers for any attachments
//! for the report.
//!
//! This is not implemented on macOS or Windows.
std::map<std::string, FileReader*> GetAttachments() const {
return attachment_map_;
};
private:
friend class CrashReportDatabase;
friend class CrashReportDatabaseGeneric;
@ -147,9 +172,12 @@ class CrashReportDatabase {
friend class CrashReportDatabaseWin;
bool Initialize(const base::FilePath path, CrashReportDatabase* database);
void InitializeAttachments();
std::unique_ptr<FileReader> reader_;
CrashReportDatabase* database_;
std::vector<std::unique_ptr<FileReader>> attachment_readers_;
std::map<std::string, FileReader*> attachment_map_;
DISALLOW_COPY_AND_ASSIGN(UploadReport);
};

View File

@ -36,6 +36,20 @@ base::FilePath ReplaceFinalExtension(
return base::FilePath(path.RemoveFinalExtension().value() + extension);
}
UUID UUIDFromReportPath(const base::FilePath& path) {
UUID uuid;
uuid.InitializeFromString(path.RemoveFinalExtension().BaseName().value());
return uuid;
}
bool AttachmentNameIsOK(const std::string& name) {
for (const char c : name) {
if (c != '_' && c != '-' && c != '.' && !isalnum(c))
return false;
}
return true;
}
using OperationStatus = CrashReportDatabase::OperationStatus;
constexpr base::FilePath::CharType kSettings[] =
@ -53,6 +67,8 @@ constexpr base::FilePath::CharType kPendingDirectory[] =
FILE_PATH_LITERAL("pending");
constexpr base::FilePath::CharType kCompletedDirectory[] =
FILE_PATH_LITERAL("completed");
constexpr base::FilePath::CharType kAttachmentsDirectory[] =
FILE_PATH_LITERAL("attachments");
constexpr const base::FilePath::CharType* kReportDirectories[] = {
kNewDirectory,
@ -171,6 +187,9 @@ class CrashReportDatabaseGeneric : public CrashReportDatabase {
OperationStatus RequestUpload(const UUID& uuid) override;
int CleanDatabase(time_t lockfile_ttl) override;
// Build a filepath for the directory for the report to hold attachments.
base::FilePath AttachmentsPath(const UUID& uuid);
private:
struct LockfileUploadReport : public UploadReport {
ScopedLockFile lock_file;
@ -225,11 +244,18 @@ class CrashReportDatabaseGeneric : public CrashReportDatabase {
// Cleans lone metadata, reports, or expired locks in a particular state.
int CleanReportsInState(ReportState state, time_t lockfile_ttl);
// Cleans any attachments that have no associated report in any state.
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);
// Reads the metadata for a report from path and returns it in report.
static bool ReadMetadata(const base::FilePath& path, Report* report);
// Wraps ReadMetadata and removes the report from the database on failure.
static bool CleaningReadMetadata(const base::FilePath& path, Report* report);
bool CleaningReadMetadata(const base::FilePath& path, Report* report);
// Writes metadata for a new report to the filesystem at path.
static bool WriteNewMetadata(const base::FilePath& path);
@ -244,6 +270,59 @@ class CrashReportDatabaseGeneric : public CrashReportDatabase {
DISALLOW_COPY_AND_ASSIGN(CrashReportDatabaseGeneric);
};
FileWriter* CrashReportDatabase::NewReport::AddAttachment(
const std::string& name) {
if (!AttachmentNameIsOK(name)) {
LOG(ERROR) << "invalid name for attachment " << name;
return nullptr;
}
base::FilePath attachments_dir =
static_cast<CrashReportDatabaseGeneric*>(database_)->AttachmentsPath(
uuid_);
if (!LoggingCreateDirectory(
attachments_dir, FilePermissions::kOwnerOnly, true)) {
return nullptr;
}
base::FilePath path = attachments_dir.Append(name);
auto writer = std::make_unique<FileWriter>();
if (!writer->Open(
path, FileWriteMode::kCreateOrFail, FilePermissions::kOwnerOnly)) {
LOG(ERROR) << "could not open " << path.value();
return nullptr;
}
attachment_writers_.emplace_back(std::move(writer));
attachment_removers_.emplace_back(ScopedRemoveFile(path));
return attachment_writers_.back().get();
}
void CrashReportDatabase::UploadReport::InitializeAttachments() {
base::FilePath attachments_dir =
static_cast<CrashReportDatabaseGeneric*>(database_)->AttachmentsPath(
uuid);
DirectoryReader reader;
if (!reader.Open(attachments_dir)) {
return;
}
base::FilePath filename;
DirectoryReader::Result dir_result;
while ((dir_result = reader.NextFile(&filename)) ==
DirectoryReader::Result::kSuccess) {
const base::FilePath filepath(attachments_dir.Append(filename));
std::unique_ptr<FileReader> reader(std::make_unique<FileReader>());
if (!reader->Open(filepath)) {
LOG(ERROR) << "attachment " << filepath.value()
<< " couldn't be opened, skipping";
continue;
}
attachment_readers_.emplace_back(std::move(reader));
attachment_map_[filename.value()] = attachment_readers_.back().get();
}
}
CrashReportDatabaseGeneric::CrashReportDatabaseGeneric() = default;
CrashReportDatabaseGeneric::~CrashReportDatabaseGeneric() = default;
@ -260,13 +339,18 @@ bool CrashReportDatabaseGeneric::Initialize(const base::FilePath& path,
}
for (const base::FilePath::CharType* subdir : kReportDirectories) {
if (!LoggingCreateDirectory(base_dir_.Append(subdir),
FilePermissions::kOwnerOnly,
true)) {
if (!LoggingCreateDirectory(
base_dir_.Append(subdir), FilePermissions::kOwnerOnly, true)) {
return false;
}
}
if (!LoggingCreateDirectory(base_dir_.Append(kAttachmentsDirectory),
FilePermissions::kOwnerOnly,
true)) {
return false;
}
if (!settings_.Initialize(base_dir_.Append(kSettings))) {
return false;
}
@ -299,8 +383,8 @@ OperationStatus CrashReportDatabaseGeneric::PrepareNewCrashReport(
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
auto new_report = std::make_unique<NewReport>();
if (!new_report->Initialize(base_dir_.Append(kNewDirectory),
kCrashReportExtension)) {
if (!new_report->Initialize(
this, base_dir_.Append(kNewDirectory), kCrashReportExtension)) {
return kFileSystemError;
}
@ -332,6 +416,14 @@ OperationStatus CrashReportDatabaseGeneric::FinishedWritingCrashReport(
// We've moved the report to pending, so it no longer needs to be removed.
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();
Metrics::CrashReportPending(Metrics::PendingReportReason::kNewlyCreated);
@ -440,6 +532,8 @@ OperationStatus CrashReportDatabaseGeneric::DeleteReport(const UUID& uuid) {
return kDatabaseError;
}
RemoveAttachmentsByUUID(uuid);
return kNoError;
}
@ -505,6 +599,7 @@ int CrashReportDatabaseGeneric::CleanDatabase(time_t lockfile_ttl) {
removed += CleanReportsInState(kPending, lockfile_ttl);
removed += CleanReportsInState(kCompleted, lockfile_ttl);
CleanOrphanedAttachments();
return removed;
}
@ -569,6 +664,16 @@ base::FilePath CrashReportDatabaseGeneric::ReportPath(const UUID& uuid,
.Append(uuid_string + kCrashReportExtension);
}
base::FilePath CrashReportDatabaseGeneric::AttachmentsPath(const UUID& uuid) {
#if defined(OS_WIN)
const std::wstring uuid_string = uuid.ToString16();
#else
const std::string uuid_string = uuid.ToString();
#endif
return base_dir_.Append(kAttachmentsDirectory).Append(uuid_string);
}
OperationStatus CrashReportDatabaseGeneric::LocateAndLockReport(
const UUID& uuid,
ReportState desired_state,
@ -688,6 +793,7 @@ int CrashReportDatabaseGeneric::CleanReportsInState(ReportState state,
if (report_lock.ResetAcquire(filepath) && !IsRegularFile(metadata_path) &&
LoggingRemoveFile(filepath)) {
++removed;
RemoveAttachmentsByUUID(UUIDFromReportPath(filepath));
}
continue;
}
@ -700,6 +806,7 @@ int CrashReportDatabaseGeneric::CleanReportsInState(ReportState state,
if (report_lock.ResetAcquire(report_path) &&
!IsRegularFile(report_path) && LoggingRemoveFile(filepath)) {
++removed;
RemoveAttachmentsByUUID(UUIDFromReportPath(filepath));
}
continue;
}
@ -717,6 +824,7 @@ int CrashReportDatabaseGeneric::CleanReportsInState(ReportState state,
if (LoggingRemoveFile(filepath)) {
++removed;
RemoveAttachmentsByUUID(UUIDFromReportPath(filepath));
}
continue;
}
@ -725,6 +833,67 @@ int CrashReportDatabaseGeneric::CleanReportsInState(ReportState state,
return removed;
}
void CrashReportDatabaseGeneric::CleanOrphanedAttachments() {
base::FilePath root_attachments_dir(base_dir_.Append(kAttachmentsDirectory));
DirectoryReader reader;
if (!reader.Open(root_attachments_dir)) {
LOG(ERROR) << "no attachments dir";
return;
}
base::FilePath filename;
DirectoryReader::Result result;
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();
continue;
}
// Check to see if the report is being created in "new".
base::FilePath new_dir_path =
base_dir_.Append(kNewDirectory)
.Append(uuid.ToString() + kCrashReportExtension);
if (IsRegularFile(new_dir_path)) {
continue;
}
// Check to see if the report is in "pending" or "completed".
ScopedLockFile local_lock;
base::FilePath local_path;
OperationStatus os =
LocateAndLockReport(uuid, kSearchable, &local_path, &local_lock);
if (os != kReportNotFound) {
continue;
}
// Couldn't find a report, assume these attachments are orphaned.
RemoveAttachmentsByUUID(uuid);
}
}
}
void CrashReportDatabaseGeneric::RemoveAttachmentsByUUID(const UUID& uuid) {
base::FilePath attachments_dir = AttachmentsPath(uuid);
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);
}
// static
bool CrashReportDatabaseGeneric::ReadMetadata(const base::FilePath& path,
Report* report) {
@ -766,7 +935,6 @@ bool CrashReportDatabaseGeneric::ReadMetadata(const base::FilePath& path,
return true;
}
// static
bool CrashReportDatabaseGeneric::CleaningReadMetadata(
const base::FilePath& path,
Report* report) {
@ -776,6 +944,7 @@ bool CrashReportDatabaseGeneric::CleaningReadMetadata(
LoggingRemoveFile(path);
LoggingRemoveFile(ReplaceFinalExtension(path, kMetadataExtension));
RemoveAttachmentsByUUID(report->uuid);
return false;
}

View File

@ -245,6 +245,16 @@ class CrashReportDatabaseMac : public CrashReportDatabase {
DISALLOW_COPY_AND_ASSIGN(CrashReportDatabaseMac);
};
FileWriter* CrashReportDatabase::NewReport::AddAttachment(
const std::string& name) {
// Attachments aren't implemented in the Mac database yet.
return nullptr;
}
void CrashReportDatabase::UploadReport::InitializeAttachments() {
// Attachments aren't implemented in the Mac database yet.
}
CrashReportDatabaseMac::CrashReportDatabaseMac(const base::FilePath& path)
: CrashReportDatabase(),
base_dir_(path),
@ -311,7 +321,8 @@ CrashReportDatabaseMac::PrepareNewCrashReport(
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
std::unique_ptr<NewReport> report(new NewReport());
if (!report->Initialize(base_dir_.Append(kWriteDirectory),
if (!report->Initialize(this,
base_dir_.Append(kWriteDirectory),
std::string(".") + kCrashReportFileExtension)) {
return kFileSystemError;
}

View File

@ -20,6 +20,7 @@
#include "test/errors.h"
#include "test/file.h"
#include "test/filesystem.h"
#include "test/gtest_disabled.h"
#include "test/scoped_temp_dir.h"
#include "util/file/file_io.h"
#include "util/file/filesystem.h"
@ -669,6 +670,101 @@ TEST_F(CrashReportDatabaseTest, RequestUpload) {
CrashReportDatabase::kCannotRequestUpload);
}
TEST_F(CrashReportDatabaseTest, Attachments) {
#if defined(OS_MACOSX) || defined(OS_WIN)
// Attachments aren't supported on Mac and Windows yet.
DISABLED_TEST();
#else
std::unique_ptr<CrashReportDatabase::NewReport> new_report;
ASSERT_EQ(db()->PrepareNewCrashReport(&new_report),
CrashReportDatabase::kNoError);
FileWriter* attach_some_file = new_report->AddAttachment("some_file");
ASSERT_NE(attach_some_file, nullptr);
static constexpr char test_data[] = "test data";
attach_some_file->Write(test_data, sizeof(test_data));
FileWriter* failed_attach = new_report->AddAttachment("not/a valid fi!e");
EXPECT_EQ(failed_attach, nullptr);
UUID expect_uuid = new_report->ReportID();
UUID uuid;
ASSERT_EQ(db()->FinishedWritingCrashReport(std::move(new_report), &uuid),
CrashReportDatabase::kNoError);
EXPECT_EQ(uuid, expect_uuid);
CrashReportDatabase::Report report;
EXPECT_EQ(db()->LookUpCrashReport(uuid, &report),
CrashReportDatabase::kNoError);
ExpectPreparedCrashReport(report);
std::vector<CrashReportDatabase::Report> reports;
EXPECT_EQ(db()->GetPendingReports(&reports), CrashReportDatabase::kNoError);
ASSERT_EQ(reports.size(), 1u);
EXPECT_EQ(reports[0].uuid, report.uuid);
std::unique_ptr<const CrashReportDatabase::UploadReport> upload_report;
ASSERT_EQ(db()->GetReportForUploading(reports[0].uuid, &upload_report),
CrashReportDatabase::kNoError);
std::map<std::string, FileReader*> result_attachments =
upload_report->GetAttachments();
EXPECT_EQ(result_attachments.size(), 1u);
EXPECT_NE(result_attachments.find("some_file"), result_attachments.end());
char result_buffer[sizeof(test_data)];
result_attachments["some_file"]->Read(result_buffer, sizeof(result_buffer));
EXPECT_EQ(memcmp(test_data, result_buffer, sizeof(test_data)), 0);
#endif
}
TEST_F(CrashReportDatabaseTest, OrphanedAttachments) {
#if defined(OS_MACOSX) || defined(OS_WIN)
// Attachments aren't supported on Mac and Windows yet.
DISABLED_TEST();
#else
// TODO: This is using paths that are specific to the generic implementation
// and will need to be generalized for Mac and Windows.
std::unique_ptr<CrashReportDatabase::NewReport> new_report;
ASSERT_EQ(db()->PrepareNewCrashReport(&new_report),
CrashReportDatabase::kNoError);
FileWriter* file1 = new_report->AddAttachment("file1");
ASSERT_NE(file1, nullptr);
FileWriter* file2 = new_report->AddAttachment("file2");
ASSERT_NE(file2, nullptr);
UUID expect_uuid = new_report->ReportID();
UUID uuid;
ASSERT_EQ(db()->FinishedWritingCrashReport(std::move(new_report), &uuid),
CrashReportDatabase::kNoError);
EXPECT_EQ(uuid, expect_uuid);
CrashReportDatabase::Report report;
ASSERT_EQ(db()->LookUpCrashReport(uuid, &report),
CrashReportDatabase::kNoError);
ASSERT_TRUE(LoggingRemoveFile(report.file_path));
ASSERT_TRUE(LoggingRemoveFile(base::FilePath(
report.file_path.RemoveFinalExtension().value() + ".meta")));
ASSERT_EQ(db()->LookUpCrashReport(uuid, &report),
CrashReportDatabase::kReportNotFound);
base::FilePath report_attachments_dir(
path().Append("attachments").Append(uuid.ToString()));
base::FilePath file_path1(report_attachments_dir.Append("file1"));
base::FilePath file_path2(report_attachments_dir.Append("file2"));
EXPECT_TRUE(FileExists(file_path1));
EXPECT_TRUE(FileExists(file_path1));
EXPECT_EQ(db()->CleanDatabase(0), 0);
EXPECT_FALSE(FileExists(file_path1));
EXPECT_FALSE(FileExists(file_path2));
EXPECT_FALSE(FileExists(report_attachments_dir));
#endif
}
// This test uses knowledge of the database format to break it, so it only
// applies to the unfified database implementation.
#if !defined(OS_MACOSX) && !defined(OS_WIN)

View File

@ -615,6 +615,16 @@ class CrashReportDatabaseWin : public CrashReportDatabase {
DISALLOW_COPY_AND_ASSIGN(CrashReportDatabaseWin);
};
FileWriter* CrashReportDatabase::NewReport::AddAttachment(
const std::string& name) {
// Attachments aren't implemented in the Windows database yet.
return nullptr;
}
void CrashReportDatabase::UploadReport::InitializeAttachments() {
// Attachments aren't implemented in the Windows database yet.
}
CrashReportDatabaseWin::CrashReportDatabaseWin(const base::FilePath& path)
: CrashReportDatabase(), base_dir_(path), settings_(), initialized_() {}
@ -653,7 +663,8 @@ OperationStatus CrashReportDatabaseWin::PrepareNewCrashReport(
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
std::unique_ptr<NewReport> new_report(new NewReport());
if (!new_report->Initialize(base_dir_.Append(kReportsDirectory),
if (!new_report->Initialize(this,
base_dir_.Append(kReportsDirectory),
std::wstring(L".") + kCrashReportFileExtension)) {
return kFileSystemError;
}

View File

@ -284,6 +284,11 @@ CrashReportUploadThread::UploadResult CrashReportUploadThread::UploadReport(
}
}
for (const auto& it : report->GetAttachments()) {
http_multipart_builder.SetFileAttachment(
it.first, it.first, it.second, "application/octet-stream");
}
http_multipart_builder.SetFileAttachment(kMinidumpKey,
report->uuid.ToString() + ".dmp",
reader,

View File

@ -52,10 +52,12 @@ CrashReportExceptionHandler::CrashReportExceptionHandler(
CrashReportDatabase* database,
CrashReportUploadThread* upload_thread,
const std::map<std::string, std::string>* process_annotations,
const std::map<std::string, base::FilePath>* process_attachments,
const UserStreamDataSources* user_stream_data_sources)
: database_(database),
upload_thread_(upload_thread),
process_annotations_(process_annotations),
process_attachments_(process_attachments),
user_stream_data_sources_(user_stream_data_sources) {}
CrashReportExceptionHandler::~CrashReportExceptionHandler() {}
@ -144,6 +146,24 @@ bool CrashReportExceptionHandler::HandleExceptionHandles(zx_handle_t process,
return false;
}
if (process_attachments_) {
// Note that attachments are read at this point each time rather than once
// so that if the contents of the file has changed it will be re-read for
// each upload (e.g. in the case of a log file).
for (const auto& it : *process_attachments_) {
FileWriter* writer = new_report->AddAttachment(it.first);
if (writer) {
std::string contents;
if (!LoggingReadEntireFile(it.second, &contents)) {
// Not being able to read the file isn't considered fatal, and
// should not prevent the report from being processed.
continue;
}
writer->Write(contents.data(), contents.size());
}
}
}
UUID uuid;
database_status =
database_->FinishedWritingCrashReport(std::move(new_report), &uuid);

View File

@ -21,6 +21,7 @@
#include <map>
#include <string>
#include "base/files/file_path.h"
#include "base/macros.h"
#include "client/crash_report_database.h"
#include "handler/crash_report_upload_thread.h"
@ -47,6 +48,10 @@ class CrashReportExceptionHandler {
//! To interoperate with Breakpad servers, the recommended practice is to
//! specify values for the `"prod"` and `"ver"` keys as process
//! annotations.
//! \param[in] process_attachments A map of file name keys to file paths to be
//! included in the report. Each time a report is written, the file paths
//! will be read in their entirety and included in the report using the
//! file name key as the name in the http upload.
//! \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
//! are called in turn. These data sources may contribute additional
@ -55,6 +60,7 @@ class CrashReportExceptionHandler {
CrashReportDatabase* database,
CrashReportUploadThread* upload_thread,
const std::map<std::string, std::string>* process_annotations,
const std::map<std::string, base::FilePath>* process_attachments,
const UserStreamDataSources* user_stream_data_sources);
~CrashReportExceptionHandler();
@ -88,6 +94,7 @@ class CrashReportExceptionHandler {
CrashReportDatabase* database_; // weak
CrashReportUploadThread* upload_thread_; // weak
const std::map<std::string, std::string>* process_annotations_; // weak
const std::map<std::string, base::FilePath>* process_attachments_; // weak
const UserStreamDataSources* user_stream_data_sources_; // weak
DISALLOW_COPY_AND_ASSIGN(CrashReportExceptionHandler);

View File

@ -836,6 +836,10 @@ int HandlerMain(int argc,
database.get(),
static_cast<CrashReportUploadThread*>(upload_thread.Get()),
&options.annotations,
#if defined(OS_FUCHSIA)
// TODO(scottmg): Process level file attachments, and for all platforms.
nullptr,
#endif
user_stream_sources);
#if defined(OS_LINUX) || defined(OS_ANDROID)