910e62b5创建于 1月15日历史提交
// Copyright 2019 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/updater/crash_client.h"

#include <optional>
#include <vector>

#include "base/check.h"
#include "base/debug/dump_without_crashing.h"
#include "base/environment.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/no_destructor.h"
#include "base/strings/string_util.h"
#include "build/build_config.h"
#include "chrome/updater/branded_constants.h"
#include "chrome/updater/tag.h"
#include "chrome/updater/updater_branding.h"
#include "chrome/updater/updater_scope.h"
#include "chrome/updater/usage_stats_permissions.h"
#include "chrome/updater/util/util.h"
#include "third_party/crashpad/crashpad/client/crash_report_database.h"
#include "third_party/crashpad/crashpad/client/crashpad_client.h"
#include "third_party/crashpad/crashpad/client/prune_crash_reports.h"
#include "third_party/crashpad/crashpad/client/settings.h"
#include "third_party/crashpad/crashpad/client/simulate_crash.h"

#if BUILDFLAG(IS_WIN)
#include <windows.h>

#include "base/win/wrapped_window_proc.h"

namespace {

int __cdecl HandleWinProcException(EXCEPTION_POINTERS* exception_pointers) {
  crashpad::CrashpadClient::DumpAndCrash(exception_pointers);
  return EXCEPTION_CONTINUE_SEARCH;
}

}  // namespace

#endif  // BUILDFLAG(IS_WIN)

namespace updater {

CrashClient::CrashClient() = default;
CrashClient::~CrashClient() = default;

CrashClient* CrashClient::GetInstance() {
  static base::NoDestructor<CrashClient> crash_client;
  return crash_client.get();
}

bool CrashClient::SetUploadsEnabled(bool enabled) {
  return database_ ? database_->GetSettings()->SetUploadsEnabled(enabled)
                   : false;
}

bool CrashClient::InitializeDatabaseOnly(UpdaterScope updater_scope) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  const std::optional<base::FilePath> database_path =
      EnsureCrashDatabasePath(updater_scope);
  if (!database_path) {
    LOG(ERROR) << "Failed to get the database path.";
    return false;
  }

  database_ = crashpad::CrashReportDatabase::Initialize(*database_path);
  if (!database_) {
    LOG(ERROR) << "Failed to initialize Crashpad database.";
    return false;
  }

  return true;
}

bool CrashClient::InitializeCrashReporting(UpdaterScope updater_scope) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  static bool initialized = false;
  CHECK(!initialized);
  initialized = true;

  if (!InitializeDatabaseOnly(updater_scope)) {
    return false;
  }

  base::debug::SetDumpWithoutCrashingFunction(
      [] { CRASHPAD_SIMULATE_CRASH(); });

#if BUILDFLAG(IS_WIN)
  // Catch exceptions thrown from a window procedure.
  base::win::WinProcExceptionFilter exception_filter =
      base::win::SetWinProcExceptionFilter(&HandleWinProcException);
  LOG_IF(DFATAL, exception_filter) << "Exception filter already present";
#endif  // BUILDFLAG(IS_WIN)

  std::vector<crashpad::CrashReportDatabase::Report> reports_completed;
  const crashpad::CrashReportDatabase::OperationStatus status_completed =
      database_->GetCompletedReports(&reports_completed);
  if (status_completed == crashpad::CrashReportDatabase::kNoError) {
    VLOG(1) << "Found " << reports_completed.size()
            << " completed crash reports";
    for (const auto& report : reports_completed) {
      VLOG(3) << "Crash since last run: ID \"" << report.id << "\", created at "
              << report.creation_time << ", " << report.upload_attempts
              << " upload attempts, file path \"" << report.file_path
              << "\", unique ID \"" << report.uuid.ToString()
              << "\"; uploaded: " << (report.uploaded ? "yes" : "no");
    }
  } else {
    LOG(ERROR) << "Failed to fetch completed crash reports: "
               << status_completed;
  }

  std::vector<crashpad::CrashReportDatabase::Report> reports_pending;
  const crashpad::CrashReportDatabase::OperationStatus status_pending =
      database_->GetPendingReports(&reports_pending);
  if (status_pending == crashpad::CrashReportDatabase::kNoError) {
    VLOG(1) << "Found " << reports_pending.size() << " pending crash reports";
    for (const auto& report : reports_pending) {
      VLOG(1) << "Crash since last run: (pending), created at "
              << report.creation_time << ", " << report.upload_attempts
              << " upload attempts, file path \"" << report.file_path
              << "\", unique ID \"" << report.uuid.ToString() << "\"";
    }
  } else {
    LOG(ERROR) << "Failed to fetch pending crash reports: " << status_pending;
  }

  std::optional<tagging::TagArgs> tag_args = GetTagArgs().tag_args;
  const bool usage_stats_enabled =
      (tag_args && tag_args->usage_stats_enable &&
       *tag_args->usage_stats_enable) ||
      base::Environment::Create()
              ->GetVar(kUsageStatsEnabled)
              .value_or(std::string()) == kUsageStatsEnabledValueEnabled ||
      AnyAppEnablesUsageStats(updater_scope);

  if (usage_stats_enabled) {
    crashpad::Settings* crashpad_settings = database_->GetSettings();
    CHECK(crashpad_settings);
    crashpad_settings->SetUploadsEnabled(true);
  }

  return true;
}

}  // namespace updater