mirror of
https://github.com/chromium/crashpad.git
synced 2025-03-09 22:16:13 +00:00
Adding support for on-demand uploads.
In order to allow on-demand uploads for crash reports, adding a upload_explicitly_requested bit on 'pending' state and necessary support for it. BUG=chromium:620762 Change-Id: Ida38e483fe8d0e48eb5cbe95e8b8bfd96a2f8f00 Reviewed-on: https://chromium-review.googlesource.com/367328 Reviewed-by: Mark Mentovai <mark@chromium.org> Reviewed-by: Robert Sesek <rsesek@chromium.org>
This commit is contained in:
parent
660a5e69d6
commit
b35ee1fca1
@ -23,8 +23,8 @@ CrashReportDatabase::Report::Report()
|
|||||||
creation_time(0),
|
creation_time(0),
|
||||||
uploaded(false),
|
uploaded(false),
|
||||||
last_upload_attempt_time(0),
|
last_upload_attempt_time(0),
|
||||||
upload_attempts(0) {
|
upload_attempts(0),
|
||||||
}
|
upload_explicitly_requested(false) {}
|
||||||
|
|
||||||
CrashReportDatabase::CallErrorWritingCrashReport::CallErrorWritingCrashReport(
|
CrashReportDatabase::CallErrorWritingCrashReport::CallErrorWritingCrashReport(
|
||||||
CrashReportDatabase* database,
|
CrashReportDatabase* database,
|
||||||
|
@ -43,7 +43,8 @@ class Settings;
|
|||||||
//! the client then writes the report, and then calls
|
//! the client then writes the report, and then calls
|
||||||
//! FinishedWritingCrashReport() to make the report Pending.
|
//! FinishedWritingCrashReport() to make the report Pending.
|
||||||
//! 2. Pending: The report has been written but has not been locally
|
//! 2. Pending: The report has been written but has not been locally
|
||||||
//! processed.
|
//! processed, or it was has been brought back from 'Completed' state by
|
||||||
|
//! user request.
|
||||||
//! 3. Completed: The report has been locally processed, either by uploading
|
//! 3. Completed: The report has been locally processed, either by uploading
|
||||||
//! it to a collection server and calling RecordUploadAttempt(), or by
|
//! it to a collection server and calling RecordUploadAttempt(), or by
|
||||||
//! calling SkipReportUpload().
|
//! calling SkipReportUpload().
|
||||||
@ -88,6 +89,10 @@ class CrashReportDatabase {
|
|||||||
//! #last_upload_attempt_time will be set to the timestamp of the most
|
//! #last_upload_attempt_time will be set to the timestamp of the most
|
||||||
//! recent attempt.
|
//! recent attempt.
|
||||||
int upload_attempts;
|
int upload_attempts;
|
||||||
|
|
||||||
|
//! Whether this crash report was explicitly requested by user to be
|
||||||
|
//! uploaded. This can be true only if report is in the 'pending' state.
|
||||||
|
bool upload_explicitly_requested;
|
||||||
};
|
};
|
||||||
|
|
||||||
//! \brief A crash report that is in the process of being written.
|
//! \brief A crash report that is in the process of being written.
|
||||||
@ -161,6 +166,10 @@ class CrashReportDatabase {
|
|||||||
//! \brief The operation could not be completed because a concurrent
|
//! \brief The operation could not be completed because a concurrent
|
||||||
//! operation affecting the report is occurring.
|
//! operation affecting the report is occurring.
|
||||||
kBusyError,
|
kBusyError,
|
||||||
|
|
||||||
|
//! \brief The report cannot be uploaded by user request as it has already
|
||||||
|
//! been uploaded.
|
||||||
|
kCannotRequestUpload,
|
||||||
};
|
};
|
||||||
|
|
||||||
virtual ~CrashReportDatabase() {}
|
virtual ~CrashReportDatabase() {}
|
||||||
@ -328,6 +337,14 @@ class CrashReportDatabase {
|
|||||||
//! \return The operation status code.
|
//! \return The operation status code.
|
||||||
virtual OperationStatus DeleteReport(const UUID& uuid) = 0;
|
virtual OperationStatus DeleteReport(const UUID& uuid) = 0;
|
||||||
|
|
||||||
|
//! \brief Marks a crash report as explicitly requested to be uploaded by the
|
||||||
|
//! user and moves it to 'pending' state.
|
||||||
|
//!
|
||||||
|
//! \param[in] uuid The unique identifier for the crash report record.
|
||||||
|
//!
|
||||||
|
//! \return The operation status code.
|
||||||
|
virtual OperationStatus RequestUpload(const UUID& uuid) = 0;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
CrashReportDatabase() {}
|
CrashReportDatabase() {}
|
||||||
|
|
||||||
|
@ -60,6 +60,7 @@ const char kXattrCreationTime[] = "creation_time";
|
|||||||
const char kXattrIsUploaded[] = "uploaded";
|
const char kXattrIsUploaded[] = "uploaded";
|
||||||
const char kXattrLastUploadTime[] = "last_upload_time";
|
const char kXattrLastUploadTime[] = "last_upload_time";
|
||||||
const char kXattrUploadAttemptCount[] = "upload_count";
|
const char kXattrUploadAttemptCount[] = "upload_count";
|
||||||
|
const char kXattrIsUploadExplicitlyRequested[] = "upload_explicitly_requested";
|
||||||
|
|
||||||
const char kXattrDatabaseInitialized[] = "initialized";
|
const char kXattrDatabaseInitialized[] = "initialized";
|
||||||
|
|
||||||
@ -140,6 +141,7 @@ class CrashReportDatabaseMac : public CrashReportDatabase {
|
|||||||
const std::string& id) override;
|
const std::string& id) override;
|
||||||
OperationStatus SkipReportUpload(const UUID& uuid) override;
|
OperationStatus SkipReportUpload(const UUID& uuid) override;
|
||||||
OperationStatus DeleteReport(const UUID& uuid) override;
|
OperationStatus DeleteReport(const UUID& uuid) override;
|
||||||
|
OperationStatus RequestUpload(const UUID& uuid) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
//! \brief A private extension of the Report class that maintains bookkeeping
|
//! \brief A private extension of the Report class that maintains bookkeeping
|
||||||
@ -201,6 +203,18 @@ class CrashReportDatabaseMac : public CrashReportDatabase {
|
|||||||
//! \return The long name of the extended attribute.
|
//! \return The long name of the extended attribute.
|
||||||
std::string XattrName(const base::StringPiece& name);
|
std::string XattrName(const base::StringPiece& name);
|
||||||
|
|
||||||
|
//! \brief Marks a report with a given path as completed.
|
||||||
|
//!
|
||||||
|
//! Assumes that the report is locked.
|
||||||
|
//!
|
||||||
|
//! \param[in] report_path The path of the file to mark completed.
|
||||||
|
//! \param[out] out_path The path of the new file. This parameter is optional.
|
||||||
|
//!
|
||||||
|
//! \return The operation status code.
|
||||||
|
CrashReportDatabase::OperationStatus MarkReportCompletedLocked(
|
||||||
|
const base::FilePath& report_path,
|
||||||
|
base::FilePath* out_path);
|
||||||
|
|
||||||
base::FilePath base_dir_;
|
base::FilePath base_dir_;
|
||||||
Settings settings_;
|
Settings settings_;
|
||||||
bool xattr_new_names_;
|
bool xattr_new_names_;
|
||||||
@ -449,14 +463,10 @@ CrashReportDatabaseMac::RecordUploadAttempt(const Report* report,
|
|||||||
return kBusyError;
|
return kBusyError;
|
||||||
|
|
||||||
if (successful) {
|
if (successful) {
|
||||||
base::FilePath new_path =
|
CrashReportDatabase::OperationStatus os =
|
||||||
base_dir_.Append(kCompletedDirectory).Append(report_path.BaseName());
|
MarkReportCompletedLocked(report_path, &report_path);
|
||||||
if (rename(report_path.value().c_str(), new_path.value().c_str()) != 0) {
|
if (os != kNoError)
|
||||||
PLOG(ERROR) << "rename " << report_path.value() << " to "
|
return os;
|
||||||
<< new_path.value();
|
|
||||||
return kFileSystemError;
|
|
||||||
}
|
|
||||||
report_path = new_path;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!WriteXattrBool(report_path, XattrName(kXattrIsUploaded), successful)) {
|
if (!WriteXattrBool(report_path, XattrName(kXattrIsUploaded), successful)) {
|
||||||
@ -500,15 +510,7 @@ CrashReportDatabase::OperationStatus CrashReportDatabaseMac::SkipReportUpload(
|
|||||||
if (!lock.is_valid())
|
if (!lock.is_valid())
|
||||||
return kBusyError;
|
return kBusyError;
|
||||||
|
|
||||||
base::FilePath new_path =
|
return MarkReportCompletedLocked(report_path, nullptr);
|
||||||
base_dir_.Append(kCompletedDirectory).Append(report_path.BaseName());
|
|
||||||
if (rename(report_path.value().c_str(), new_path.value().c_str()) != 0) {
|
|
||||||
PLOG(ERROR) << "rename " << report_path.value() << " to "
|
|
||||||
<< new_path.value();
|
|
||||||
return kFileSystemError;
|
|
||||||
}
|
|
||||||
|
|
||||||
return kNoError;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
CrashReportDatabase::OperationStatus CrashReportDatabaseMac::DeleteReport(
|
CrashReportDatabase::OperationStatus CrashReportDatabaseMac::DeleteReport(
|
||||||
@ -556,6 +558,45 @@ base::FilePath CrashReportDatabaseMac::LocateCrashReport(const UUID& uuid) {
|
|||||||
return base::FilePath();
|
return base::FilePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CrashReportDatabase::OperationStatus CrashReportDatabaseMac::RequestUpload(
|
||||||
|
const UUID& uuid) {
|
||||||
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||||
|
|
||||||
|
base::FilePath report_path = LocateCrashReport(uuid);
|
||||||
|
if (report_path.empty())
|
||||||
|
return kReportNotFound;
|
||||||
|
|
||||||
|
base::ScopedFD lock(ObtainReportLock(report_path));
|
||||||
|
if (!lock.is_valid())
|
||||||
|
return kBusyError;
|
||||||
|
|
||||||
|
// If the crash report has already been uploaded, don't request new upload.
|
||||||
|
bool uploaded = false;
|
||||||
|
XattrStatus status =
|
||||||
|
ReadXattrBool(report_path, XattrName(kXattrIsUploaded), &uploaded);
|
||||||
|
if (status == XattrStatus::kOtherError)
|
||||||
|
return kDatabaseError;
|
||||||
|
if (uploaded)
|
||||||
|
return kCannotRequestUpload;
|
||||||
|
|
||||||
|
// Mark the crash report as having upload explicitly requested by the user,
|
||||||
|
// and move it to the pending state.
|
||||||
|
if (!WriteXattrBool(
|
||||||
|
report_path, XattrName(kXattrIsUploadExplicitlyRequested), true)) {
|
||||||
|
return kDatabaseError;
|
||||||
|
}
|
||||||
|
|
||||||
|
base::FilePath new_path =
|
||||||
|
base_dir_.Append(kUploadPendingDirectory).Append(report_path.BaseName());
|
||||||
|
if (rename(report_path.value().c_str(), new_path.value().c_str()) != 0) {
|
||||||
|
PLOG(ERROR) << "rename " << report_path.value() << " to "
|
||||||
|
<< new_path.value();
|
||||||
|
return kFileSystemError;
|
||||||
|
}
|
||||||
|
|
||||||
|
return kNoError;
|
||||||
|
}
|
||||||
|
|
||||||
// static
|
// static
|
||||||
base::ScopedFD CrashReportDatabaseMac::ObtainReportLock(
|
base::ScopedFD CrashReportDatabaseMac::ObtainReportLock(
|
||||||
const base::FilePath& path) {
|
const base::FilePath& path) {
|
||||||
@ -604,6 +645,14 @@ bool CrashReportDatabaseMac::ReadReportMetadataLocked(
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
report->upload_explicitly_requested = false;
|
||||||
|
if (ReadXattrBool(path,
|
||||||
|
XattrName(kXattrIsUploadExplicitlyRequested),
|
||||||
|
&report->upload_explicitly_requested) ==
|
||||||
|
XattrStatus::kOtherError) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -647,6 +696,28 @@ std::string CrashReportDatabaseMac::XattrName(const base::StringPiece& name) {
|
|||||||
return XattrNameInternal(name, xattr_new_names_);
|
return XattrNameInternal(name, xattr_new_names_);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CrashReportDatabase::OperationStatus
|
||||||
|
CrashReportDatabaseMac::MarkReportCompletedLocked(
|
||||||
|
const base::FilePath& report_path,
|
||||||
|
base::FilePath* out_path) {
|
||||||
|
if (RemoveXattr(report_path, XattrName(kXattrIsUploadExplicitlyRequested)) ==
|
||||||
|
XattrStatus::kOtherError) {
|
||||||
|
return kDatabaseError;
|
||||||
|
}
|
||||||
|
|
||||||
|
base::FilePath new_path =
|
||||||
|
base_dir_.Append(kCompletedDirectory).Append(report_path.BaseName());
|
||||||
|
if (rename(report_path.value().c_str(), new_path.value().c_str()) != 0) {
|
||||||
|
PLOG(ERROR) << "rename " << report_path.value() << " to "
|
||||||
|
<< new_path.value();
|
||||||
|
return kFileSystemError;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (out_path)
|
||||||
|
*out_path = new_path;
|
||||||
|
return kNoError;
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<CrashReportDatabase> InitializeInternal(
|
std::unique_ptr<CrashReportDatabase> InitializeInternal(
|
||||||
const base::FilePath& path,
|
const base::FilePath& path,
|
||||||
bool may_create) {
|
bool may_create) {
|
||||||
|
@ -94,6 +94,7 @@ class CrashReportDatabaseTest : public testing::Test {
|
|||||||
EXPECT_FALSE(report.uploaded);
|
EXPECT_FALSE(report.uploaded);
|
||||||
EXPECT_EQ(0, report.last_upload_attempt_time);
|
EXPECT_EQ(0, report.last_upload_attempt_time);
|
||||||
EXPECT_EQ(0, report.upload_attempts);
|
EXPECT_EQ(0, report.upload_attempts);
|
||||||
|
EXPECT_FALSE(report.upload_explicitly_requested);
|
||||||
}
|
}
|
||||||
|
|
||||||
void RelocateDatabase() {
|
void RelocateDatabase() {
|
||||||
@ -102,6 +103,16 @@ class CrashReportDatabaseTest : public testing::Test {
|
|||||||
SetUp();
|
SetUp();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
CrashReportDatabase::OperationStatus RequestUpload(const UUID& uuid) {
|
||||||
|
CrashReportDatabase::OperationStatus os = db()->RequestUpload(uuid);
|
||||||
|
|
||||||
|
CrashReportDatabase::Report report;
|
||||||
|
EXPECT_EQ(CrashReportDatabase::kNoError,
|
||||||
|
db_->LookUpCrashReport(uuid, &report));
|
||||||
|
|
||||||
|
return os;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
ScopedTempDir temp_dir_;
|
ScopedTempDir temp_dir_;
|
||||||
std::unique_ptr<CrashReportDatabase> db_;
|
std::unique_ptr<CrashReportDatabase> db_;
|
||||||
@ -220,6 +231,7 @@ TEST_F(CrashReportDatabaseTest, LookUpCrashReport) {
|
|||||||
EXPECT_FALSE(report.uploaded);
|
EXPECT_FALSE(report.uploaded);
|
||||||
EXPECT_EQ(0, report.last_upload_attempt_time);
|
EXPECT_EQ(0, report.last_upload_attempt_time);
|
||||||
EXPECT_EQ(0, report.upload_attempts);
|
EXPECT_EQ(0, report.upload_attempts);
|
||||||
|
EXPECT_FALSE(report.upload_explicitly_requested);
|
||||||
}
|
}
|
||||||
|
|
||||||
UploadReport(uuid, true, "test");
|
UploadReport(uuid, true, "test");
|
||||||
@ -234,6 +246,7 @@ TEST_F(CrashReportDatabaseTest, LookUpCrashReport) {
|
|||||||
EXPECT_TRUE(report.uploaded);
|
EXPECT_TRUE(report.uploaded);
|
||||||
EXPECT_NE(0, report.last_upload_attempt_time);
|
EXPECT_NE(0, report.last_upload_attempt_time);
|
||||||
EXPECT_EQ(1, report.upload_attempts);
|
EXPECT_EQ(1, report.upload_attempts);
|
||||||
|
EXPECT_FALSE(report.upload_explicitly_requested);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -585,6 +598,72 @@ TEST_F(CrashReportDatabaseTest, ReadEmptyDatabase) {
|
|||||||
CreateCrashReport(&report2);
|
CreateCrashReport(&report2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(CrashReportDatabaseTest, RequestUpload) {
|
||||||
|
std::vector<CrashReportDatabase::Report> reports(2);
|
||||||
|
CreateCrashReport(&reports[0]);
|
||||||
|
CreateCrashReport(&reports[1]);
|
||||||
|
|
||||||
|
const UUID& report_0_uuid = reports[0].uuid;
|
||||||
|
const UUID& report_1_uuid = reports[1].uuid;
|
||||||
|
|
||||||
|
// Skipped report gets back to pending state after RequestUpload is called.
|
||||||
|
EXPECT_EQ(CrashReportDatabase::kNoError,
|
||||||
|
db()->SkipReportUpload(report_1_uuid));
|
||||||
|
|
||||||
|
std::vector<CrashReportDatabase::Report> pending_reports;
|
||||||
|
CrashReportDatabase::OperationStatus os =
|
||||||
|
db()->GetPendingReports(&pending_reports);
|
||||||
|
EXPECT_EQ(CrashReportDatabase::kNoError, os);
|
||||||
|
ASSERT_EQ(1u, pending_reports.size());
|
||||||
|
EXPECT_EQ(pending_reports[0].uuid, report_0_uuid);
|
||||||
|
|
||||||
|
pending_reports.clear();
|
||||||
|
EXPECT_EQ(CrashReportDatabase::kNoError, RequestUpload(report_1_uuid));
|
||||||
|
os = db()->GetPendingReports(&pending_reports);
|
||||||
|
EXPECT_EQ(CrashReportDatabase::kNoError, os);
|
||||||
|
ASSERT_EQ(2u, pending_reports.size());
|
||||||
|
|
||||||
|
// Check individual reports.
|
||||||
|
const CrashReportDatabase::Report* expicitly_requested_report;
|
||||||
|
const CrashReportDatabase::Report* pending_report;
|
||||||
|
if (pending_reports[0].uuid == report_0_uuid) {
|
||||||
|
pending_report = &pending_reports[0];
|
||||||
|
expicitly_requested_report = &pending_reports[1];
|
||||||
|
} else {
|
||||||
|
pending_report = &pending_reports[1];
|
||||||
|
expicitly_requested_report = &pending_reports[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
EXPECT_EQ(report_0_uuid, pending_report->uuid);
|
||||||
|
EXPECT_FALSE(pending_report->upload_explicitly_requested);
|
||||||
|
|
||||||
|
EXPECT_EQ(report_1_uuid, expicitly_requested_report->uuid);
|
||||||
|
EXPECT_TRUE(expicitly_requested_report->upload_explicitly_requested);
|
||||||
|
|
||||||
|
// Explicitly requested reports will not have upload_explicitly_requested bit
|
||||||
|
// after getting skipped.
|
||||||
|
EXPECT_EQ(CrashReportDatabase::kNoError,
|
||||||
|
db()->SkipReportUpload(report_1_uuid));
|
||||||
|
CrashReportDatabase::Report report;
|
||||||
|
EXPECT_EQ(CrashReportDatabase::kNoError,
|
||||||
|
db()->LookUpCrashReport(report_1_uuid, &report));
|
||||||
|
EXPECT_FALSE(report.upload_explicitly_requested);
|
||||||
|
|
||||||
|
// Pending report gets correctly affected after RequestUpload is called.
|
||||||
|
pending_reports.clear();
|
||||||
|
EXPECT_EQ(CrashReportDatabase::kNoError, RequestUpload(report_0_uuid));
|
||||||
|
os = db()->GetPendingReports(&pending_reports);
|
||||||
|
EXPECT_EQ(CrashReportDatabase::kNoError, os);
|
||||||
|
EXPECT_EQ(1u, pending_reports.size());
|
||||||
|
EXPECT_EQ(pending_reports[0].uuid, report_0_uuid);
|
||||||
|
EXPECT_TRUE(pending_reports[0].upload_explicitly_requested);
|
||||||
|
|
||||||
|
// Already uploaded report cannot be requested for the new upload.
|
||||||
|
UploadReport(report_0_uuid, true, "1");
|
||||||
|
EXPECT_EQ(CrashReportDatabase::kCannotRequestUpload,
|
||||||
|
RequestUpload(report_0_uuid));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace test
|
} // namespace test
|
||||||
} // namespace crashpad
|
} // namespace crashpad
|
||||||
|
@ -103,6 +103,14 @@ enum class ReportState {
|
|||||||
kCompleted,
|
kCompleted,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum {
|
||||||
|
//! \brief Corresponds to uploaded bit of the report state.
|
||||||
|
kAttributeUploaded = 1 << 0,
|
||||||
|
|
||||||
|
//! \brief Corresponds to upload_explicity_requested bit of the report state.
|
||||||
|
kAttributeUploadExplicitlyRequested = 1 << 1,
|
||||||
|
};
|
||||||
|
|
||||||
struct MetadataFileReportRecord {
|
struct MetadataFileReportRecord {
|
||||||
// Note that this default constructor does no initialization. It is used only
|
// Note that this default constructor does no initialization. It is used only
|
||||||
// to create an array of records that are immediately initialized by reading
|
// to create an array of records that are immediately initialized by reading
|
||||||
@ -121,7 +129,7 @@ struct MetadataFileReportRecord {
|
|||||||
int64_t last_upload_attempt_time; // Holds a time_t.
|
int64_t last_upload_attempt_time; // Holds a time_t.
|
||||||
int32_t upload_attempts;
|
int32_t upload_attempts;
|
||||||
int32_t state; // A ReportState.
|
int32_t state; // A ReportState.
|
||||||
uint8_t uploaded; // Boolean, 0 or 1.
|
uint8_t attributes; // Bitfield of kAttribute*.
|
||||||
uint8_t padding[7];
|
uint8_t padding[7];
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -151,7 +159,10 @@ MetadataFileReportRecord::MetadataFileReportRecord(const ReportDisk& report,
|
|||||||
last_upload_attempt_time(report.last_upload_attempt_time),
|
last_upload_attempt_time(report.last_upload_attempt_time),
|
||||||
upload_attempts(report.upload_attempts),
|
upload_attempts(report.upload_attempts),
|
||||||
state(static_cast<uint32_t>(report.state)),
|
state(static_cast<uint32_t>(report.state)),
|
||||||
uploaded(report.uploaded) {
|
attributes((report.uploaded ? kAttributeUploaded : 0) |
|
||||||
|
(report.upload_explicitly_requested
|
||||||
|
? kAttributeUploadExplicitlyRequested
|
||||||
|
: 0)) {
|
||||||
memset(&padding, 0, sizeof(padding));
|
memset(&padding, 0, sizeof(padding));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -164,10 +175,12 @@ ReportDisk::ReportDisk(const MetadataFileReportRecord& record,
|
|||||||
base::UTF8ToUTF16(&string_table[record.file_path_index]));
|
base::UTF8ToUTF16(&string_table[record.file_path_index]));
|
||||||
id = &string_table[record.id_index];
|
id = &string_table[record.id_index];
|
||||||
creation_time = record.creation_time;
|
creation_time = record.creation_time;
|
||||||
uploaded = record.uploaded != 0;
|
|
||||||
last_upload_attempt_time = record.last_upload_attempt_time;
|
last_upload_attempt_time = record.last_upload_attempt_time;
|
||||||
upload_attempts = record.upload_attempts;
|
upload_attempts = record.upload_attempts;
|
||||||
state = static_cast<ReportState>(record.state);
|
state = static_cast<ReportState>(record.state);
|
||||||
|
uploaded = (record.attributes & kAttributeUploaded) != 0;
|
||||||
|
upload_explicitly_requested =
|
||||||
|
(record.attributes & kAttributeUploadExplicitlyRequested) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
ReportDisk::ReportDisk(const UUID& uuid,
|
ReportDisk::ReportDisk(const UUID& uuid,
|
||||||
@ -577,6 +590,7 @@ class CrashReportDatabaseWin : public CrashReportDatabase {
|
|||||||
const std::string& id) override;
|
const std::string& id) override;
|
||||||
OperationStatus SkipReportUpload(const UUID& uuid) override;
|
OperationStatus SkipReportUpload(const UUID& uuid) override;
|
||||||
OperationStatus DeleteReport(const UUID& uuid) override;
|
OperationStatus DeleteReport(const UUID& uuid) override;
|
||||||
|
OperationStatus RequestUpload(const UUID& uuid) override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::unique_ptr<Metadata> AcquireMetadata();
|
std::unique_ptr<Metadata> AcquireMetadata();
|
||||||
@ -773,8 +787,14 @@ OperationStatus CrashReportDatabaseWin::RecordUploadAttempt(
|
|||||||
report_disk->id = id;
|
report_disk->id = id;
|
||||||
report_disk->last_upload_attempt_time = now;
|
report_disk->last_upload_attempt_time = now;
|
||||||
report_disk->upload_attempts++;
|
report_disk->upload_attempts++;
|
||||||
report_disk->state =
|
if (successful) {
|
||||||
successful ? ReportState::kCompleted : ReportState::kPending;
|
report_disk->state = ReportState::kCompleted;
|
||||||
|
report_disk->upload_explicitly_requested = false;
|
||||||
|
} else {
|
||||||
|
report_disk->state = ReportState::kPending;
|
||||||
|
report_disk->upload_explicitly_requested =
|
||||||
|
report->upload_explicitly_requested;
|
||||||
|
}
|
||||||
|
|
||||||
if (!settings_.SetLastUploadAttemptTime(now))
|
if (!settings_.SetLastUploadAttemptTime(now))
|
||||||
return kDatabaseError;
|
return kDatabaseError;
|
||||||
@ -811,8 +831,10 @@ OperationStatus CrashReportDatabaseWin::SkipReportUpload(const UUID& uuid) {
|
|||||||
ReportDisk* report_disk;
|
ReportDisk* report_disk;
|
||||||
OperationStatus os = metadata->FindSingleReportAndMarkDirty(
|
OperationStatus os = metadata->FindSingleReportAndMarkDirty(
|
||||||
uuid, ReportState::kPending, &report_disk);
|
uuid, ReportState::kPending, &report_disk);
|
||||||
if (os == kNoError)
|
if (os == kNoError) {
|
||||||
report_disk->state = ReportState::kCompleted;
|
report_disk->state = ReportState::kCompleted;
|
||||||
|
report_disk->upload_explicitly_requested = false;
|
||||||
|
}
|
||||||
return os;
|
return os;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -831,6 +853,37 @@ std::unique_ptr<CrashReportDatabase> InitializeInternal(
|
|||||||
: std::unique_ptr<CrashReportDatabaseWin>();
|
: std::unique_ptr<CrashReportDatabaseWin>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OperationStatus CrashReportDatabaseWin::RequestUpload(const UUID& uuid) {
|
||||||
|
INITIALIZATION_STATE_DCHECK_VALID(initialized_);
|
||||||
|
|
||||||
|
std::unique_ptr<Metadata> metadata(AcquireMetadata());
|
||||||
|
if (!metadata)
|
||||||
|
return kDatabaseError;
|
||||||
|
|
||||||
|
ReportDisk* report_disk;
|
||||||
|
// TODO(gayane): Search for the report only once regardless of its state.
|
||||||
|
OperationStatus os = metadata->FindSingleReportAndMarkDirty(
|
||||||
|
uuid, ReportState::kCompleted, &report_disk);
|
||||||
|
if (os == kBusyError) {
|
||||||
|
os = metadata->FindSingleReportAndMarkDirty(
|
||||||
|
uuid, ReportState::kPending, &report_disk);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (os != kNoError)
|
||||||
|
return os;
|
||||||
|
|
||||||
|
// If the crash report has already been uploaded, don't request new upload.
|
||||||
|
if (report_disk->uploaded)
|
||||||
|
return kCannotRequestUpload;
|
||||||
|
|
||||||
|
// Mark the crash report as having upload explicitly requested by the user,
|
||||||
|
// and move it to the pending state.
|
||||||
|
report_disk->upload_explicitly_requested = true;
|
||||||
|
report_disk->state = ReportState::kPending;
|
||||||
|
|
||||||
|
return kNoError;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
// static
|
// static
|
||||||
|
@ -46,6 +46,7 @@ class MockDatabase : public CrashReportDatabase {
|
|||||||
OperationStatus(const Report*, bool, const std::string&));
|
OperationStatus(const Report*, bool, const std::string&));
|
||||||
MOCK_METHOD1(SkipReportUpload, OperationStatus(const UUID&));
|
MOCK_METHOD1(SkipReportUpload, OperationStatus(const UUID&));
|
||||||
MOCK_METHOD1(DeleteReport, OperationStatus(const UUID&));
|
MOCK_METHOD1(DeleteReport, OperationStatus(const UUID&));
|
||||||
|
MOCK_METHOD1(RequestUpload, OperationStatus(const UUID&));
|
||||||
};
|
};
|
||||||
|
|
||||||
time_t NDaysAgo(int num_days) {
|
time_t NDaysAgo(int num_days) {
|
||||||
|
@ -190,11 +190,12 @@ void CrashReportUploadThread::ProcessPendingReport(
|
|||||||
Settings* const settings = database_->GetSettings();
|
Settings* const settings = database_->GetSettings();
|
||||||
|
|
||||||
bool uploads_enabled;
|
bool uploads_enabled;
|
||||||
if (!settings->GetUploadsEnabled(&uploads_enabled) ||
|
if (url_.empty() ||
|
||||||
!uploads_enabled ||
|
(!report.upload_explicitly_requested &&
|
||||||
url_.empty()) {
|
(!settings->GetUploadsEnabled(&uploads_enabled) || !uploads_enabled))) {
|
||||||
// If the upload-enabled state can’t be determined, uploads are disabled, or
|
// Don’t attempt an upload if there’s no URL to upload to. Allow upload if
|
||||||
// there’s no URL to upload to, don’t attempt to upload the new report.
|
// it has been explicitly requested by the user, otherwise, respect the
|
||||||
|
// upload-enabled state stored in the database’s settings.
|
||||||
database_->SkipReportUpload(report.uuid);
|
database_->SkipReportUpload(report.uuid);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -251,6 +252,10 @@ void CrashReportUploadThread::ProcessPendingReport(
|
|||||||
// to at least try to get the report out of the way.
|
// to at least try to get the report out of the way.
|
||||||
database_->SkipReportUpload(report.uuid);
|
database_->SkipReportUpload(report.uuid);
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
case CrashReportDatabase::kCannotRequestUpload:
|
||||||
|
NOTREACHED();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
CallRecordUploadAttempt call_record_upload_attempt(database_, upload_report);
|
CallRecordUploadAttempt call_record_upload_attempt(database_, upload_report);
|
||||||
|
@ -148,4 +148,16 @@ bool WriteXattrTimeT(const base::FilePath& file,
|
|||||||
return WriteXattr(file, name, tmp);
|
return WriteXattr(file, name, tmp);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
XattrStatus RemoveXattr(const base::FilePath& file,
|
||||||
|
const base::StringPiece& name) {
|
||||||
|
int rv = removexattr(file.value().c_str(), name.data(), 0);
|
||||||
|
if (rv != 0) {
|
||||||
|
if (errno == ENOATTR)
|
||||||
|
return XattrStatus::kNoAttribute;
|
||||||
|
PLOG(ERROR) << "removexattr " << name << " on file " << file.value();
|
||||||
|
return XattrStatus::kOtherError;
|
||||||
|
}
|
||||||
|
return XattrStatus::kOK;
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace crashpad
|
} // namespace crashpad
|
||||||
|
@ -92,6 +92,15 @@ bool WriteXattrTimeT(const base::FilePath& file,
|
|||||||
const base::StringPiece& name,
|
const base::StringPiece& name,
|
||||||
time_t value);
|
time_t value);
|
||||||
|
|
||||||
|
//! \brief Removes an extended attribute from a file.
|
||||||
|
//!
|
||||||
|
//! \param[in] file The path to the file.
|
||||||
|
//! \param[in] name The name of the extended attribute to remove.
|
||||||
|
//!
|
||||||
|
//! \return XattrStatus
|
||||||
|
XattrStatus RemoveXattr(const base::FilePath& file,
|
||||||
|
const base::StringPiece& name);
|
||||||
|
|
||||||
} // namespace crashpad
|
} // namespace crashpad
|
||||||
|
|
||||||
#endif // CRASHPAD_UTIL_MAC_XATTR_H_
|
#endif // CRASHPAD_UTIL_MAC_XATTR_H_
|
||||||
|
@ -117,6 +117,20 @@ TEST_F(Xattr, WriteAndReadTimeT) {
|
|||||||
EXPECT_EQ(expected, actual);
|
EXPECT_EQ(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(Xattr, RemoveAndRead) {
|
||||||
|
const std::string value = "hello world";
|
||||||
|
EXPECT_TRUE(WriteXattr(path(), kKey, value));
|
||||||
|
|
||||||
|
std::string actual;
|
||||||
|
EXPECT_EQ(XattrStatus::kOK, ReadXattr(path(), kKey, &actual));
|
||||||
|
EXPECT_EQ(value, actual);
|
||||||
|
|
||||||
|
EXPECT_EQ(XattrStatus::kOK, RemoveXattr(path(), kKey));
|
||||||
|
EXPECT_EQ(XattrStatus::kNoAttribute, ReadXattr(path(), kKey, &actual));
|
||||||
|
|
||||||
|
EXPECT_EQ(XattrStatus::kNoAttribute, RemoveXattr(path(), kKey));
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace test
|
} // namespace test
|
||||||
} // namespace crashpad
|
} // namespace crashpad
|
||||||
|
Loading…
x
Reference in New Issue
Block a user