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

#include "ui/ozone/platform/drm/gpu/page_flip_watchdog.h"

#include <cstdint>

#include "ash/constants/ash_switches.h"
#include "base/containers/ring_buffer.h"
#include "base/logging.h"
#include "base/metrics/histogram_functions.h"
#include "base/syslog_logging.h"

namespace ui {

PageFlipWatchdog::PageFlipWatchdog() {
  plane_assignment_flake_threshold = ash::switches::IsRevenBranding()
                                         ? kFlexPlaneAssignmentFlakeThreshold
                                         : kPlaneAssignmentFlakeThreshold;
}

PageFlipWatchdog::~PageFlipWatchdog() = default;

void PageFlipWatchdog::OnSuccessfulPageFlip() {
  page_flip_status_tracker_.SaveToBuffer(true);
}

void PageFlipWatchdog::CrashOnFailedPlaneAssignment() {
  page_flip_status_tracker_.SaveToBuffer(false);
  failed_page_flip_counter_++;

  // Wait until the log of recent page flips is full to avoid crashing
  // too early.
  if (page_flip_status_tracker_.CurrentIndex() <
      page_flip_status_tracker_.BufferSize())
    return;

  bool last_page_flip_status = true;
  uint32_t flakes = 0;
  uint32_t failures = 0;
  for (auto iter = page_flip_status_tracker_.Begin(); iter; ++iter) {
    bool page_flip_status = **iter;
    if (page_flip_status && !last_page_flip_status)
      flakes += 1;
    if (!page_flip_status)
      failures += 1;
    last_page_flip_status = page_flip_status;
  }

  // Metric to find good threshold for Flex device.
  // TODO(crbug.com/371609830): finalize this threshold.
  if (ash::switches::IsRevenBranding()) {
    base::UmaHistogramCounts100("Platform.FlexPageFlipFlakes2", flakes);
  }

  if (flakes >= plane_assignment_flake_threshold) {
    LOG(FATAL) << "Plane assignment has flaked " << flakes
               << " times, but the threshold is "
               << plane_assignment_flake_threshold
               << ". Crashing the GPU process.";
  }

  if (failures >= kPlaneAssignmentMaximumFailures) {
    LOG(FATAL) << "Plane assignment has failed " << failures << "/"
               << page_flip_status_tracker_.BufferSize()
               << " times, but the threshold is "
               << kPlaneAssignmentMaximumFailures
               << ". Crashing the GPU process.";
  }
}

void PageFlipWatchdog::ArmForFailedCommit() {
  page_flip_status_tracker_.SaveToBuffer(false);
  failed_page_flip_counter_++;
  StartCrashGpuTimer();
}

void PageFlipWatchdog::Disarm() {
  failed_page_flip_counter_ = 0;
  page_flip_status_tracker_.Clear();

  if (crash_gpu_timer_.IsRunning()) {
    crash_gpu_timer_.Stop();
    SYSLOG(INFO)
        << "Detected a modeset attempt after " << failed_page_flip_counter_
        << " failed page flips. Aborting GPU process self-destruct with "
        << crash_gpu_timer_.desired_run_time() - base::TimeTicks::Now()
        << " to spare.";
  }
}

void PageFlipWatchdog::StartCrashGpuTimer() {
  if (!crash_gpu_timer_.IsRunning()) {
    DCHECK_GE(failed_page_flip_counter_, 1);
    LOG(WARNING) << "Initiating GPU process self-destruct in "
                 << kWaitForModesetTimeout
                 << " unless a modeset attempt is detected.";

    crash_gpu_timer_.Start(
        FROM_HERE, kWaitForModesetTimeout, base::BindOnce([] {
          LOG(FATAL) << "Failed to modeset within " << kWaitForModesetTimeout
                     << " of the first page flip failure. Crashing GPU "
                        "process.";
        }));
  }
}

}  // namespace ui