Add metrics for tracking uploads

Three new metrics:
- counting upload success/failure;
- enum tracking the reason upload was skipped;
- enum describing how an upload got to the pending state.

R=mark@chromium.org, asvitkine@chromium.org
BUG=crashpad:100

Change-Id: I5e0cbc1ac3424e974f3a51560e5cdad484ffc038
Reviewed-on: https://chromium-review.googlesource.com/388855
Reviewed-by: Mark Mentovai <mark@chromium.org>
This commit is contained in:
Scott Graham 2016-09-26 15:06:19 -07:00
parent 0aeca5f123
commit ac6c01b575
8 changed files with 119 additions and 16 deletions

View File

@ -24,6 +24,7 @@
#include "base/files/file_path.h" #include "base/files/file_path.h"
#include "base/macros.h" #include "base/macros.h"
#include "util/file/file_io.h" #include "util/file/file_io.h"
#include "util/misc/metrics.h"
#include "util/misc/uuid.h" #include "util/misc/uuid.h"
namespace crashpad { namespace crashpad {
@ -326,9 +327,13 @@ class CrashReportDatabase {
//! crash generation is still enabled in the product. //! crash generation is still enabled in the product.
//! //!
//! \param[in] uuid The unique identifier for the crash report record. //! \param[in] uuid The unique identifier for the crash report record.
//! \param[in] reason The reason the report upload is being skipped for
//! metrics tracking purposes.
//! //!
//! \return The operation status code. //! \return The operation status code.
virtual OperationStatus SkipReportUpload(const UUID& uuid) = 0; virtual OperationStatus SkipReportUpload(
const UUID& uuid,
Metrics::CrashSkippedReason reason) = 0;
//! \brief Deletes a crash report file and its associated metadata. //! \brief Deletes a crash report file and its associated metadata.
//! //!

View File

@ -140,7 +140,8 @@ class CrashReportDatabaseMac : public CrashReportDatabase {
OperationStatus RecordUploadAttempt(const Report* report, OperationStatus RecordUploadAttempt(const Report* report,
bool successful, bool successful,
const std::string& id) override; const std::string& id) override;
OperationStatus SkipReportUpload(const UUID& uuid) override; OperationStatus SkipReportUpload(const UUID& uuid,
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;
@ -359,6 +360,7 @@ CrashReportDatabaseMac::FinishedWritingCrashReport(NewReport* report,
return kFileSystemError; return kFileSystemError;
} }
Metrics::CrashReportPending(Metrics::PendingReportReason::kNewlyCreated);
Metrics::CrashReportSize(report->handle); Metrics::CrashReportSize(report->handle);
return kNoError; return kNoError;
@ -451,6 +453,8 @@ CrashReportDatabaseMac::RecordUploadAttempt(const Report* report,
const std::string& id) { const std::string& id) {
INITIALIZATION_STATE_DCHECK_VALID(initialized_); INITIALIZATION_STATE_DCHECK_VALID(initialized_);
Metrics::CrashUploadAttempted(successful);
DCHECK(report); DCHECK(report);
DCHECK(successful || id.empty()); DCHECK(successful || id.empty());
@ -502,9 +506,12 @@ CrashReportDatabaseMac::RecordUploadAttempt(const Report* report,
} }
CrashReportDatabase::OperationStatus CrashReportDatabaseMac::SkipReportUpload( CrashReportDatabase::OperationStatus CrashReportDatabaseMac::SkipReportUpload(
const UUID& uuid) { const UUID& uuid,
Metrics::CrashSkippedReason reason) {
INITIALIZATION_STATE_DCHECK_VALID(initialized_); INITIALIZATION_STATE_DCHECK_VALID(initialized_);
Metrics::CrashUploadSkipped(reason);
base::FilePath report_path = LocateCrashReport(uuid); base::FilePath report_path = LocateCrashReport(uuid);
if (report_path.empty()) if (report_path.empty())
return kReportNotFound; return kReportNotFound;
@ -597,6 +604,8 @@ CrashReportDatabase::OperationStatus CrashReportDatabaseMac::RequestUpload(
return kFileSystemError; return kFileSystemError;
} }
Metrics::CrashReportPending(Metrics::PendingReportReason::kUserInitiated);
return kNoError; return kNoError;
} }

View File

@ -433,7 +433,8 @@ TEST_F(CrashReportDatabaseTest, GetCompletedAndNotUploadedReports) {
// Skip upload for one report. // Skip upload for one report.
EXPECT_EQ(CrashReportDatabase::kNoError, EXPECT_EQ(CrashReportDatabase::kNoError,
db()->SkipReportUpload(report_3_uuid)); db()->SkipReportUpload(
report_3_uuid, Metrics::CrashSkippedReason::kUploadsDisabled));
pending.clear(); pending.clear();
EXPECT_EQ(CrashReportDatabase::kNoError, db()->GetPendingReports(&pending)); EXPECT_EQ(CrashReportDatabase::kNoError, db()->GetPendingReports(&pending));
@ -608,7 +609,8 @@ TEST_F(CrashReportDatabaseTest, RequestUpload) {
// Skipped report gets back to pending state after RequestUpload is called. // Skipped report gets back to pending state after RequestUpload is called.
EXPECT_EQ(CrashReportDatabase::kNoError, EXPECT_EQ(CrashReportDatabase::kNoError,
db()->SkipReportUpload(report_1_uuid)); db()->SkipReportUpload(
report_1_uuid, Metrics::CrashSkippedReason::kUploadsDisabled));
std::vector<CrashReportDatabase::Report> pending_reports; std::vector<CrashReportDatabase::Report> pending_reports;
CrashReportDatabase::OperationStatus os = CrashReportDatabase::OperationStatus os =
@ -643,7 +645,8 @@ TEST_F(CrashReportDatabaseTest, RequestUpload) {
// Explicitly requested reports will not have upload_explicitly_requested bit // Explicitly requested reports will not have upload_explicitly_requested bit
// after getting skipped. // after getting skipped.
EXPECT_EQ(CrashReportDatabase::kNoError, EXPECT_EQ(CrashReportDatabase::kNoError,
db()->SkipReportUpload(report_1_uuid)); db()->SkipReportUpload(
report_1_uuid, Metrics::CrashSkippedReason::kUploadsDisabled));
CrashReportDatabase::Report report; CrashReportDatabase::Report report;
EXPECT_EQ(CrashReportDatabase::kNoError, EXPECT_EQ(CrashReportDatabase::kNoError,
db()->LookUpCrashReport(report_1_uuid, &report)); db()->LookUpCrashReport(report_1_uuid, &report));

View File

@ -589,7 +589,8 @@ class CrashReportDatabaseWin : public CrashReportDatabase {
OperationStatus RecordUploadAttempt(const Report* report, OperationStatus RecordUploadAttempt(const Report* report,
bool successful, bool successful,
const std::string& id) override; const std::string& id) override;
OperationStatus SkipReportUpload(const UUID& uuid) override; OperationStatus SkipReportUpload(const UUID& uuid,
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;
@ -679,6 +680,7 @@ OperationStatus CrashReportDatabaseWin::FinishedWritingCrashReport(
ReportState::kPending)); ReportState::kPending));
*uuid = scoped_report->uuid; *uuid = scoped_report->uuid;
Metrics::CrashReportPending(Metrics::PendingReportReason::kNewlyCreated);
Metrics::CrashReportSize(handle.get()); Metrics::CrashReportSize(handle.get());
return kNoError; return kNoError;
@ -774,6 +776,8 @@ OperationStatus CrashReportDatabaseWin::RecordUploadAttempt(
const std::string& id) { const std::string& id) {
INITIALIZATION_STATE_DCHECK_VALID(initialized_); INITIALIZATION_STATE_DCHECK_VALID(initialized_);
Metrics::CrashUploadAttempted(successful);
// Take ownership, allocated in GetReportForUploading. // Take ownership, allocated in GetReportForUploading.
std::unique_ptr<const Report> upload_report(report); std::unique_ptr<const Report> upload_report(report);
std::unique_ptr<Metadata> metadata(AcquireMetadata()); std::unique_ptr<Metadata> metadata(AcquireMetadata());
@ -826,9 +830,13 @@ OperationStatus CrashReportDatabaseWin::DeleteReport(const UUID& uuid) {
return kNoError; return kNoError;
} }
OperationStatus CrashReportDatabaseWin::SkipReportUpload(const UUID& uuid) { OperationStatus CrashReportDatabaseWin::SkipReportUpload(
const UUID& uuid,
Metrics::CrashSkippedReason reason) {
INITIALIZATION_STATE_DCHECK_VALID(initialized_); INITIALIZATION_STATE_DCHECK_VALID(initialized_);
Metrics::CrashUploadSkipped(reason);
std::unique_ptr<Metadata> metadata(AcquireMetadata()); std::unique_ptr<Metadata> metadata(AcquireMetadata());
if (!metadata) if (!metadata)
return kDatabaseError; return kDatabaseError;
@ -885,6 +893,8 @@ OperationStatus CrashReportDatabaseWin::RequestUpload(const UUID& uuid) {
report_disk->upload_explicitly_requested = true; report_disk->upload_explicitly_requested = true;
report_disk->state = ReportState::kPending; report_disk->state = ReportState::kPending;
Metrics::CrashReportPending(Metrics::PendingReportReason::kUserInitiated);
return kNoError; return kNoError;
} }

View File

@ -44,7 +44,8 @@ class MockDatabase : public CrashReportDatabase {
OperationStatus(const UUID&, const Report**)); OperationStatus(const UUID&, const Report**));
MOCK_METHOD3(RecordUploadAttempt, MOCK_METHOD3(RecordUploadAttempt,
OperationStatus(const Report*, bool, const std::string&)); OperationStatus(const Report*, bool, const std::string&));
MOCK_METHOD1(SkipReportUpload, OperationStatus(const UUID&)); MOCK_METHOD2(SkipReportUpload,
OperationStatus(const UUID&, Metrics::CrashSkippedReason));
MOCK_METHOD1(DeleteReport, OperationStatus(const UUID&)); MOCK_METHOD1(DeleteReport, OperationStatus(const UUID&));
MOCK_METHOD1(RequestUpload, OperationStatus(const UUID&)); MOCK_METHOD1(RequestUpload, OperationStatus(const UUID&));
}; };

View File

@ -28,6 +28,7 @@
#include "snapshot/minidump/process_snapshot_minidump.h" #include "snapshot/minidump/process_snapshot_minidump.h"
#include "snapshot/module_snapshot.h" #include "snapshot/module_snapshot.h"
#include "util/file/file_reader.h" #include "util/file/file_reader.h"
#include "util/misc/metrics.h"
#include "util/misc/uuid.h" #include "util/misc/uuid.h"
#include "util/net/http_body.h" #include "util/net/http_body.h"
#include "util/net/http_multipart_builder.h" #include "util/net/http_multipart_builder.h"
@ -196,7 +197,8 @@ void CrashReportUploadThread::ProcessPendingReport(
// Dont attempt an upload if theres no URL to upload to. Allow upload if // Dont attempt an upload if theres no URL to upload to. Allow upload if
// it has been explicitly requested by the user, otherwise, respect the // it has been explicitly requested by the user, otherwise, respect the
// upload-enabled state stored in the databases settings. // upload-enabled state stored in the databases settings.
database_->SkipReportUpload(report.uuid); database_->SkipReportUpload(report.uuid,
Metrics::CrashSkippedReason::kUploadsDisabled);
return; return;
} }
@ -217,7 +219,8 @@ void CrashReportUploadThread::ProcessPendingReport(
// attempt to upload the report. // attempt to upload the report.
const int kUploadAttemptIntervalSeconds = 60 * 60; // 1 hour const int kUploadAttemptIntervalSeconds = 60 * 60; // 1 hour
if (now - last_upload_attempt_time < kUploadAttemptIntervalSeconds) { if (now - last_upload_attempt_time < kUploadAttemptIntervalSeconds) {
database_->SkipReportUpload(report.uuid); database_->SkipReportUpload(
report.uuid, Metrics::CrashSkippedReason::kUploadThrottled);
return; return;
} }
} else { } else {
@ -228,7 +231,8 @@ void CrashReportUploadThread::ProcessPendingReport(
// accept it and dont attempt to upload the report. // accept it and dont attempt to upload the report.
const int kBackwardsClockTolerance = 60 * 60 * 24; // 1 day const int kBackwardsClockTolerance = 60 * 60 * 24; // 1 day
if (last_upload_attempt_time - now < kBackwardsClockTolerance) { if (last_upload_attempt_time - now < kBackwardsClockTolerance) {
database_->SkipReportUpload(report.uuid); database_->SkipReportUpload(
report.uuid, Metrics::CrashSkippedReason::kUnexpectedTime);
return; return;
} }
} }
@ -250,7 +254,8 @@ void CrashReportUploadThread::ProcessPendingReport(
case CrashReportDatabase::kDatabaseError: case CrashReportDatabase::kDatabaseError:
// In these cases, SkipReportUpload() might not work either, but its best // In these cases, SkipReportUpload() might not work either, but its best
// 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,
Metrics::CrashSkippedReason::kDatabaseError);
return; return;
case CrashReportDatabase::kCannotRequestUpload: case CrashReportDatabase::kCannotRequestUpload:
@ -274,7 +279,8 @@ void CrashReportUploadThread::ProcessPendingReport(
// TODO(mark): Deal with retries properly: dont call SkipReportUplaod() // TODO(mark): Deal with retries properly: dont call SkipReportUplaod()
// if the result was kRetry and the report hasnt already been retried // if the result was kRetry and the report hasnt already been retried
// too many times. // too many times.
database_->SkipReportUpload(report.uuid); database_->SkipReportUpload(report.uuid,
Metrics::CrashSkippedReason::kUploadFailed);
break; break;
} }
} }

View File

@ -35,11 +35,19 @@ enum class ExceptionProcessingState {
void ExceptionProcessing(ExceptionProcessingState state) { void ExceptionProcessing(ExceptionProcessingState state) {
UMA_HISTOGRAM_COUNTS("Crashpad.ExceptionEncountered", UMA_HISTOGRAM_COUNTS("Crashpad.ExceptionEncountered",
static_cast<int>(state)); static_cast<int32_t>(state));
} }
} // namespace } // namespace
// static
void Metrics::CrashReportPending(PendingReportReason reason) {
UMA_HISTOGRAM_ENUMERATION(
"Crashpad.CrashReportPending",
static_cast<int32_t>(reason),
static_cast<int32_t>(PendingReportReason::kMaxValue));
}
// static // static
void Metrics::CrashReportSize(FileHandle file) { void Metrics::CrashReportSize(FileHandle file) {
const FileOffset size = LoggingFileSizeByHandle(file); const FileOffset size = LoggingFileSizeByHandle(file);
@ -47,6 +55,20 @@ void Metrics::CrashReportSize(FileHandle file) {
"Crashpad.CrashReportSize", size, 0, 20 * 1024 * 1024, 50); "Crashpad.CrashReportSize", size, 0, 20 * 1024 * 1024, 50);
} }
// static
void Metrics::CrashUploadAttempted(bool successful) {
UMA_HISTOGRAM_COUNTS("Crashpad.CrashUpload.AttemptSuccessful",
static_cast<int32_t>(successful));
}
// static
void Metrics::CrashUploadSkipped(CrashSkippedReason reason) {
UMA_HISTOGRAM_ENUMERATION(
"Crashpad.CrashUpload.Skipped",
static_cast<int32_t>(reason),
static_cast<int32_t>(CrashSkippedReason::kMaxValue));
}
// static // static
void Metrics::ExceptionCaptureResult(CaptureResult result) { void Metrics::ExceptionCaptureResult(CaptureResult result) {
ExceptionProcessing(ExceptionProcessingState::kFinished); ExceptionProcessing(ExceptionProcessingState::kFinished);

View File

@ -30,12 +30,59 @@ namespace crashpad {
//! Chromium's base, they allow integration with its metrics system. //! Chromium's base, they allow integration with its metrics system.
class Metrics { class Metrics {
public: public:
//! \brief Values for CrashReportPending(). These are used as metrics
//! enumeration values, so new values should always be added at the end.
enum class PendingReportReason : int32_t {
//! \brief A report was newly created and is ready for upload.
kNewlyCreated = 0,
//! \brief The user manually requested the report be uploaded.
kUserInitiated = 1,
//! \brief The number of values in this enumeration; not a valid value.
kMaxValue
};
//! \brief Reports when a crash upload has entered the pending state.
static void CrashReportPending(PendingReportReason reason);
//! \brief Reports the size of a crash report file in bytes. Should be called //! \brief Reports the size of a crash report file in bytes. Should be called
//! when a new report is written to disk. //! when a new report is written to disk.
static void CrashReportSize(FileHandle file); static void CrashReportSize(FileHandle file);
//! \brief Reports on a crash upload attempt, and if it succeeded.
static void CrashUploadAttempted(bool successful);
//! \brief Values for CrashUploadSkipped(). These are used as metrics
//! enumeration values, so new values should always be added at the end.
enum class CrashSkippedReason : int32_t {
//! \brief Crash uploading is disabled.
kUploadsDisabled = 0,
//! \brief There was another upload too recently, so this one was throttled.
kUploadThrottled = 1,
//! \brief The report had an unexpected timestamp.
kUnexpectedTime = 2,
//! \brief The database reported an error, likely due to a filesystem
//! problem.
kDatabaseError = 3,
//! \brief The upload of the crash failed during communication with the
//! server.
kUploadFailed = 4,
//! \brief The number of values in this enumeration; not a valid value.
kMaxValue
};
//! \brief Reports when a report is moved to the completed state in the
//! database, without the report being uploadad.
static void CrashUploadSkipped(CrashSkippedReason reason);
//! \brief The result of capturing an exception. These are used as metrics //! \brief The result of capturing an exception. These are used as metrics
//! enumeration values so new values should always be added at the end. //! enumeration values, so new values should always be added at the end.
enum class CaptureResult : int32_t { enum class CaptureResult : int32_t {
//! \brief The exception capture succeeded normally. //! \brief The exception capture succeeded normally.
kSuccess = 0, kSuccess = 0,