1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109
|
// 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_macros.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;
}
if (flakes >= plane_assignment_flake_threshold) {
// Experiment to find good threshold for Flex device.
// TODO(crbug.com/371609830): finalize this threshold
// upon experiment completion.
if (ash::switches::IsRevenBranding()) {
UMA_HISTOGRAM_EXACT_LINEAR("Platform.FlexPageFlipFlakes", flakes, 11);
}
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
|