mirror of
				https://bitbucket.org/chromiumembedded/cef
				synced 2025-06-05 21:39:12 +02:00 
			
		
		
		
	
		
			
				
	
	
		
			211 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			211 lines
		
	
	
		
			6.6 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| // Copyright 2016 The Chromium Embedded Framework Authors. All rights reserved.
 | ||
| // Use of this source code is governed by a BSD-style license that can be
 | ||
| // found in the LICENSE file.
 | ||
| 
 | ||
| #include "libcef/common/cef_crash_report_upload_thread.h"
 | ||
| 
 | ||
| #include "libcef/common/cef_crash_report_utils.h"
 | ||
| #include "third_party/crashpad/crashpad/client/settings.h"
 | ||
| 
 | ||
| using namespace crashpad;
 | ||
| 
 | ||
| CefCrashReportUploadThread::CefCrashReportUploadThread(
 | ||
|     CrashReportDatabase* database,
 | ||
|     const std::string& url,
 | ||
|     const Options& options,
 | ||
|     int max_uploads)
 | ||
|     : CrashReportUploadThread(database, url, options),
 | ||
|       max_uploads_(max_uploads) {}
 | ||
| 
 | ||
| CefCrashReportUploadThread::~CefCrashReportUploadThread() {}
 | ||
| 
 | ||
| void CefCrashReportUploadThread::ProcessPendingReports() {
 | ||
|   if (BackoffPending()) {
 | ||
|     // Try again later.
 | ||
|     return;
 | ||
|   }
 | ||
| 
 | ||
|   if (MaxUploadsEnabled()) {
 | ||
|     // Retrieve all completed reports.
 | ||
|     std::vector<CrashReportDatabase::Report> reports;
 | ||
|     if (database_->GetCompletedReports(&reports) !=
 | ||
|         CrashReportDatabase::kNoError) {
 | ||
|       // The database is sick. It might be prudent to stop trying to poke it
 | ||
|       // from this thread by abandoning the thread altogether. On the other
 | ||
|       // hand, if the problem is transient, it might be possible to talk to it
 | ||
|       // again on the next pass. For now, take the latter approach.
 | ||
|       return;
 | ||
|     }
 | ||
| 
 | ||
|     const time_t now = time(nullptr);
 | ||
|     const int kSeconds = 60 * 60 * 24;  // 24 hours
 | ||
| 
 | ||
|     // Count how many reports have completed in the last 24 hours.
 | ||
|     recent_upload_ct_ = 0;
 | ||
|     for (const CrashReportDatabase::Report& report : reports) {
 | ||
|       if (report.last_upload_attempt_time > now - kSeconds)
 | ||
|         recent_upload_ct_++;
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   // Continue with processing pending reports.
 | ||
|   CrashReportUploadThread::ProcessPendingReports();
 | ||
| }
 | ||
| 
 | ||
| void CefCrashReportUploadThread::ProcessPendingReport(
 | ||
|     const CrashReportDatabase::Report& report) {
 | ||
|   // Always allow upload if it's been explicitly requested by the user.
 | ||
|   if (!report.upload_explicitly_requested) {
 | ||
|     if (!UploadsEnabled()) {
 | ||
|       // Don’t attempt an upload if there’s no URL or if uploads have been
 | ||
|       // disabled in the database’s settings.
 | ||
|       database_->SkipReportUpload(
 | ||
|           report.uuid, Metrics::CrashSkippedReason::kUploadsDisabled);
 | ||
|       return;
 | ||
|     }
 | ||
| 
 | ||
|     if (MaxUploadsExceeded()) {
 | ||
|       // Don't send uploads if the rate limit has been exceeded.
 | ||
|       database_->SkipReportUpload(
 | ||
|           report.uuid, Metrics::CrashSkippedReason::kUploadThrottled);
 | ||
|       return;
 | ||
|     }
 | ||
|   }
 | ||
| 
 | ||
|   if (BackoffPending()) {
 | ||
|     // Try again later.
 | ||
|     return;
 | ||
|   }
 | ||
| 
 | ||
|   std::unique_ptr<const CrashReportDatabase::UploadReport> upload_report;
 | ||
|   CrashReportDatabase::OperationStatus status =
 | ||
|       database_->GetReportForUploading(report.uuid, &upload_report);
 | ||
|   switch (status) {
 | ||
|     case CrashReportDatabase::kNoError:
 | ||
|       break;
 | ||
| 
 | ||
|     case CrashReportDatabase::kBusyError:
 | ||
|       return;
 | ||
| 
 | ||
|     case CrashReportDatabase::kReportNotFound:
 | ||
|     case CrashReportDatabase::kFileSystemError:
 | ||
|     case CrashReportDatabase::kDatabaseError:
 | ||
|       // In these cases, SkipReportUpload() might not work either, but it’s best
 | ||
|       // to at least try to get the report out of the way.
 | ||
|       database_->SkipReportUpload(report.uuid,
 | ||
|                                   Metrics::CrashSkippedReason::kDatabaseError);
 | ||
|       return;
 | ||
| 
 | ||
|     case CrashReportDatabase::kCannotRequestUpload:
 | ||
|       NOTREACHED();
 | ||
|       return;
 | ||
|   }
 | ||
| 
 | ||
|   std::string response_body;
 | ||
|   UploadResult upload_result =
 | ||
|       UploadReport(upload_report.get(), &response_body);
 | ||
|   switch (upload_result) {
 | ||
|     case UploadResult::kSuccess:
 | ||
|       // The upload completed successfully.
 | ||
|       database_->RecordUploadComplete(std::move(upload_report), response_body);
 | ||
|       if (MaxUploadsEnabled())
 | ||
|         recent_upload_ct_++;
 | ||
|       ResetBackoff();
 | ||
|       break;
 | ||
|     case UploadResult::kPermanentFailure:
 | ||
|       // The upload should never be retried.
 | ||
|       database_->SkipReportUpload(report.uuid,
 | ||
|                                   Metrics::CrashSkippedReason::kUploadFailed);
 | ||
|       break;
 | ||
|     case UploadResult::kRetry:
 | ||
|       // The upload will be retried after a reasonable backoff delay. Since we
 | ||
|       // didn't successfully upload it we won't count it against the rate limit.
 | ||
|       IncreaseBackoff();
 | ||
|       break;
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| CrashReportUploadThread::ParameterMap
 | ||
| CefCrashReportUploadThread::FilterParameters(const ParameterMap& parameters) {
 | ||
|   return crash_report_utils::FilterParameters(parameters);
 | ||
| }
 | ||
| 
 | ||
| bool CefCrashReportUploadThread::UploadsEnabled() const {
 | ||
|   Settings* const settings = database_->GetSettings();
 | ||
|   bool uploads_enabled;
 | ||
|   return !url_.empty() && settings->GetUploadsEnabled(&uploads_enabled) &&
 | ||
|          uploads_enabled;
 | ||
| }
 | ||
| 
 | ||
| bool CefCrashReportUploadThread::MaxUploadsEnabled() const {
 | ||
|   return options_.rate_limit && max_uploads_ > 0;
 | ||
| }
 | ||
| 
 | ||
| bool CefCrashReportUploadThread::MaxUploadsExceeded() const {
 | ||
|   return MaxUploadsEnabled() && recent_upload_ct_ >= max_uploads_;
 | ||
| }
 | ||
| 
 | ||
| bool CefCrashReportUploadThread::BackoffPending() const {
 | ||
|   if (!options_.rate_limit)
 | ||
|     return false;
 | ||
| 
 | ||
|   Settings* const settings = database_->GetSettings();
 | ||
| 
 | ||
|   time_t next_upload_time;
 | ||
|   if (settings->GetNextUploadAttemptTime(&next_upload_time) &&
 | ||
|       next_upload_time > 0) {
 | ||
|     const time_t now = time(nullptr);
 | ||
|     if (now < next_upload_time)
 | ||
|       return true;
 | ||
|   }
 | ||
| 
 | ||
|   return false;
 | ||
| }
 | ||
| 
 | ||
| void CefCrashReportUploadThread::IncreaseBackoff() {
 | ||
|   if (!options_.rate_limit)
 | ||
|     return;
 | ||
| 
 | ||
|   const int kHour = 60 * 60;  // 1 hour
 | ||
|   const int kBackoffSchedule[] = {
 | ||
|       kHour / 4,   // 15 minutes
 | ||
|       kHour,       // 1 hour
 | ||
|       kHour * 2,   // 2 hours
 | ||
|       kHour * 4,   // 4 hours
 | ||
|       kHour * 8,   // 8 hours
 | ||
|       kHour * 24,  // 24 hours
 | ||
|   };
 | ||
|   const int kBackoffScheduleSize =
 | ||
|       sizeof(kBackoffSchedule) / sizeof(kBackoffSchedule[0]);
 | ||
| 
 | ||
|   Settings* settings = database_->GetSettings();
 | ||
| 
 | ||
|   int backoff_step = 0;
 | ||
|   if (settings->GetBackoffStep(&backoff_step) && backoff_step < 0)
 | ||
|     backoff_step = 0;
 | ||
|   if (++backoff_step > kBackoffScheduleSize)
 | ||
|     backoff_step = kBackoffScheduleSize;
 | ||
| 
 | ||
|   time_t next_upload_time = time(nullptr);  // now
 | ||
|   next_upload_time += kBackoffSchedule[backoff_step - 1];
 | ||
| 
 | ||
|   settings->SetBackoffStep(backoff_step);
 | ||
|   settings->SetNextUploadAttemptTime(next_upload_time);
 | ||
| 
 | ||
|   if (max_uploads_ > 1) {
 | ||
|     // If the server is having trouble then we don't want to send many crash
 | ||
|     // reports after the backoff expires. Reduce max uploads to 1 per 24 hours
 | ||
|     // until the client is restarted.
 | ||
|     max_uploads_ = 1;
 | ||
|   }
 | ||
| }
 | ||
| 
 | ||
| void CefCrashReportUploadThread::ResetBackoff() {
 | ||
|   if (!options_.rate_limit)
 | ||
|     return;
 | ||
| 
 | ||
|   Settings* settings = database_->GetSettings();
 | ||
|   settings->SetBackoffStep(0);
 | ||
|   settings->SetNextUploadAttemptTime(0);
 | ||
| }
 |