blob: 7717bab420ac5270a721b4856a9b99cd8a8d5c35 [file] [log] [blame]
/*
* Copyright (c) 2015-2022 The Khronos Group Inc.
* Copyright (c) 2015-2023 Valve Corporation
* Copyright (c) 2015-2023 LunarG, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "error_monitor.h"
#ifdef VK_USE_PLATFORM_ANDROID_KHR
// Note. VK_EXT_debug_report is deprecated by the VK_EXT_debug_utils extension.
// However, we still support this old extension due to CI running old Android devices.
static VKAPI_ATTR VkBool32 VKAPI_CALL DebugCallback(VkDebugReportFlagsEXT message_flags, VkDebugReportObjectTypeEXT, uint64_t,
size_t, int32_t, const char *, const char *message, void *user_data) {
auto *error_monitor = reinterpret_cast<ErrorMonitor *>(user_data);
if (message_flags & error_monitor->GetMessageFlags()) {
return error_monitor->CheckForDesiredMsg(message);
}
return VK_FALSE;
}
#else
static inline VkDebugReportFlagsEXT DebugAnnotFlagsToReportFlags(VkDebugUtilsMessageSeverityFlagBitsEXT da_severity,
VkDebugUtilsMessageTypeFlagsEXT da_type) {
if (da_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
return VK_DEBUG_REPORT_ERROR_BIT_EXT;
}
if (da_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
if (da_type & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT) {
return VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT;
} else {
return VK_DEBUG_REPORT_WARNING_BIT_EXT;
}
}
if (da_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) {
return VK_DEBUG_REPORT_INFORMATION_BIT_EXT;
}
if (da_severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) {
return VK_DEBUG_REPORT_DEBUG_BIT_EXT;
}
return 0;
}
static VKAPI_ATTR VkBool32 VKAPI_CALL DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT message_severity,
VkDebugUtilsMessageTypeFlagsEXT message_types,
const VkDebugUtilsMessengerCallbackDataEXT *callback_data, void *user_data) {
const auto message_flags = DebugAnnotFlagsToReportFlags(message_severity, message_types);
const char *message = callback_data->pMessage;
auto *error_monitor = reinterpret_cast<ErrorMonitor *>(user_data);
if (message_flags & error_monitor->GetMessageFlags()) {
return error_monitor->CheckForDesiredMsg(message);
}
return VK_FALSE;
}
#endif
ErrorMonitor::ErrorMonitor(bool print_all_errors) : print_all_errors_(print_all_errors) {
MonitorReset();
ExpectSuccess(kErrorBit);
#if !defined(VK_USE_PLATFORM_ANDROID_KHR)
debug_create_info_ = {VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT,
nullptr,
0,
VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT,
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT,
&DebugCallback,
this};
#else
debug_create_info_ = {VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT, nullptr,
VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT |
VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT | VK_DEBUG_REPORT_INFORMATION_BIT_EXT |
VK_DEBUG_REPORT_DEBUG_BIT_EXT,
&DebugCallback, this};
#endif
}
void ErrorMonitor::CreateCallback(VkInstance instance) noexcept {
assert(instance);
assert(!debug_obj_);
#if !defined(VK_USE_PLATFORM_ANDROID_KHR)
assert(vk::CreateDebugUtilsMessengerEXT != nullptr);
const VkResult result = vk::CreateDebugUtilsMessengerEXT(instance, &debug_create_info_, nullptr, &debug_obj_);
#else
assert(vk::CreateDebugReportCallbackEXT != nullptr);
const VkResult result = vk::CreateDebugReportCallbackEXT(instance, &debug_create_info_, nullptr, &debug_obj_);
#endif
if (result != VK_SUCCESS) {
assert(false);
debug_obj_ = VK_NULL_HANDLE;
}
}
void ErrorMonitor::DestroyCallback(VkInstance instance) noexcept {
assert(instance);
assert(debug_obj_); // valid to call with null object, but probably bug
#if !defined(VK_USE_PLATFORM_ANDROID_KHR)
vk::DestroyDebugUtilsMessengerEXT(instance, debug_obj_, nullptr);
#else
vk::DestroyDebugReportCallbackEXT(instance, debug_obj_, nullptr);
#endif
debug_obj_ = VK_NULL_HANDLE;
}
void ErrorMonitor::MonitorReset() {
message_flags_ = kErrorBit;
bailout_ = nullptr;
message_found_ = false;
failure_message_strings_.clear();
desired_message_strings_.clear();
ignore_message_strings_.clear();
allowed_message_strings_.clear();
}
void ErrorMonitor::Reset() {
auto guard = Lock();
MonitorReset();
}
void ErrorMonitor::SetDesiredFailureMsg(const VkFlags msgFlags, const std::string &msg) {
SetDesiredFailureMsg(msgFlags, msg.c_str());
}
void ErrorMonitor::SetDesiredFailureMsg(const VkFlags msgFlags, const char *const msgString) {
if (NeedCheckSuccess()) {
VerifyNotFound();
}
auto guard = Lock();
desired_message_strings_.insert(msgString);
message_flags_ |= msgFlags;
}
void ErrorMonitor::SetAllowedFailureMsg(const char *const msg) {
auto guard = Lock();
allowed_message_strings_.emplace_back(msg);
}
void ErrorMonitor::SetUnexpectedError(const char *const msg) {
if (NeedCheckSuccess()) {
VerifyNotFound();
}
auto guard = Lock();
ignore_message_strings_.emplace_back(msg);
}
VkBool32 ErrorMonitor::CheckForDesiredMsg(const char *const msgString) {
VkBool32 result = VK_FALSE;
auto guard = Lock();
if (bailout_ != nullptr) {
*bailout_ = true;
}
std::string error_string(msgString);
bool found_expected = false;
if (print_all_errors_) {
std::cout << error_string << "\n\n";
}
if (!IgnoreMessage(error_string)) {
for (auto desired_msg_it = desired_message_strings_.begin(); desired_msg_it != desired_message_strings_.end();
++desired_msg_it) {
if (error_string.find(*desired_msg_it) != std::string::npos) {
found_expected = true;
failure_message_strings_.insert(error_string);
message_found_ = true;
result = VK_TRUE;
// Remove a maximum of one failure message from the set
// Multiset mutation is acceptable because `break` causes flow of control to exit the for loop
desired_message_strings_.erase(desired_msg_it);
break;
}
}
if (!found_expected && !allowed_message_strings_.empty()) {
for (auto allowed_msg_it = allowed_message_strings_.begin(); allowed_msg_it != allowed_message_strings_.end();
++allowed_msg_it) {
if (error_string.find(*allowed_msg_it) != std::string::npos) {
found_expected = true;
break;
}
}
}
if (!found_expected) {
result = VK_TRUE;
ADD_FAILURE() << error_string;
}
}
return result;
}
VkDebugReportFlagsEXT ErrorMonitor::GetMessageFlags() { return message_flags_; }
bool ErrorMonitor::AnyDesiredMsgFound() const { return message_found_; }
void ErrorMonitor::SetError(const char *const errorString) {
auto guard = Lock();
message_found_ = true;
failure_message_strings_.insert(errorString);
}
void ErrorMonitor::SetBailout(std::atomic<bool> *bailout) {
auto guard = Lock();
bailout_ = bailout;
}
void ErrorMonitor::ExpectSuccess(VkDebugReportFlagsEXT const message_flag_mask) {
// Match ANY message matching specified type
auto guard = Lock();
desired_message_strings_.clear();
message_flags_ = message_flag_mask;
}
bool ErrorMonitor::ExpectingSuccess() const {
return (desired_message_strings_.size() == 1) && (desired_message_strings_.count("") == 1 && ignore_message_strings_.empty());
}
bool ErrorMonitor::NeedCheckSuccess() const { return ExpectingSuccess(); }
void ErrorMonitor::VerifyFound() {
{
// The lock must be released before the ExpectSuccess call at the end
auto guard = Lock();
// Not receiving expected message(s) is a failure.
if (!desired_message_strings_.empty()) {
for (const auto &desired_msg : desired_message_strings_) {
ADD_FAILURE() << "Did not receive expected error '" << desired_msg << "'";
}
}
MonitorReset();
}
ExpectSuccess();
}
void ErrorMonitor::Finish() {
VerifyNotFound();
Reset();
}
void ErrorMonitor::VerifyNotFound() {
auto guard = Lock();
// ExpectSuccess() configured us to match anything. Any error is a failure.
if (AnyDesiredMsgFound()) {
for (const auto &msg : failure_message_strings_) {
ADD_FAILURE() << "Expected to succeed but got error: " << msg;
}
}
MonitorReset();
}
bool ErrorMonitor::IgnoreMessage(std::string const &msg) const {
if (ignore_message_strings_.empty()) {
return false;
}
return std::find_if(ignore_message_strings_.begin(), ignore_message_strings_.end(), [&msg](std::string const &str) {
return msg.find(str) != std::string::npos;
}) != ignore_message_strings_.end();
}