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);
 | 
						||
}
 |