1/* 2 * 3 * Copyright (c) 2014-2022 The Khronos Group Inc. 4 * Copyright (c) 2014-2022 Valve Corporation 5 * Copyright (c) 2014-2022 LunarG, Inc. 6 * 7 * Licensed under the Apache License, Version 2.0 (the "License"); 8 * you may not use this file except in compliance with the License. 9 * You may obtain a copy of the License at 10 * 11 * http://www.apache.org/licenses/LICENSE-2.0 12 * 13 * Unless required by applicable law or agreed to in writing, software 14 * distributed under the License is distributed on an "AS IS" BASIS, 15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 * 19 * Author: Jon Ashburn <jon@lunarg.com> 20 * Author: Courtney Goeltzenleuchter <courtney@LunarG.com> 21 * Author: Chia-I Wu <olvaffe@gmail.com> 22 * Author: Chia-I Wu <olv@lunarg.com> 23 * Author: Mark Lobodzinski <mark@LunarG.com> 24 * Author: Lenny Komow <lenny@lunarg.com> 25 * Author: Charles Giessen <charles@lunarg.com> 26 * 27 */ 28 29#include "log.h" 30 31#include <stdio.h> 32#include <stdarg.h> 33 34#include "debug_utils.h" 35#include "loader_common.h" 36#include "loader_environment.h" 37#include "settings.h" 38#include "vk_loader_platform.h" 39#ifdef VK_USE_PLATFORM_OHOS 40#include "loader_hilog.h" 41#endif 42 43uint32_t g_loader_debug = ~0u; 44 45void loader_init_global_debug_level(void) { 46 char *env, *orig; 47 48 if (g_loader_debug > 0) return; 49 50 g_loader_debug = 0; 51 52 // Parse comma-separated debug options 53 orig = env = loader_getenv("VK_LOADER_DEBUG", NULL); 54 while (env) { 55 char *p = strchr(env, ','); 56 size_t len; 57 58 if (p) { 59 len = p - env; 60 } else { 61 len = strlen(env); 62 } 63 64 if (len > 0) { 65 if (strncmp(env, "all", len) == 0) { 66 g_loader_debug = ~0u; 67 } else if (strncmp(env, "warn", len) == 0) { 68 g_loader_debug |= VULKAN_LOADER_WARN_BIT; 69 } else if (strncmp(env, "info", len) == 0) { 70 g_loader_debug |= VULKAN_LOADER_INFO_BIT; 71 } else if (strncmp(env, "perf", len) == 0) { 72 g_loader_debug |= VULKAN_LOADER_PERF_BIT; 73 } else if (strncmp(env, "error", len) == 0) { 74 g_loader_debug |= VULKAN_LOADER_ERROR_BIT; 75 } else if (strncmp(env, "debug", len) == 0) { 76 g_loader_debug |= VULKAN_LOADER_DEBUG_BIT; 77 } else if (strncmp(env, "layer", len) == 0) { 78 g_loader_debug |= VULKAN_LOADER_LAYER_BIT; 79 } else if (strncmp(env, "driver", len) == 0 || strncmp(env, "implem", len) == 0 || strncmp(env, "icd", len) == 0) { 80 g_loader_debug |= VULKAN_LOADER_DRIVER_BIT; 81 } 82 } 83 84 if (!p) break; 85 86 env = p + 1; 87 } 88 89 loader_free_getenv(orig, NULL); 90} 91 92void loader_set_global_debug_level(uint32_t new_loader_debug) { g_loader_debug = new_loader_debug; } 93 94void loader_log(const struct loader_instance *inst, VkFlags msg_type, int32_t msg_code, const char *format, ...) { 95 (void)msg_code; 96 char msg[512] = {0}; 97 98 va_list ap; 99 va_start(ap, format); 100 int ret = vsnprintf(msg, sizeof(msg), format, ap); 101 if ((ret >= (int)sizeof(msg)) || ret < 0) { 102 msg[sizeof(msg) - 1] = '\0'; 103 } 104 va_end(ap); 105 106 if (inst) { 107 VkDebugUtilsMessageSeverityFlagBitsEXT severity = 0; 108 VkDebugUtilsMessageTypeFlagsEXT type = 0; 109 VkDebugUtilsMessengerCallbackDataEXT callback_data = {0}; 110 VkDebugUtilsObjectNameInfoEXT object_name = {0}; 111 112 if ((msg_type & VULKAN_LOADER_INFO_BIT) != 0) { 113 severity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT; 114 } else if ((msg_type & VULKAN_LOADER_WARN_BIT) != 0) { 115 severity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; 116 } else if ((msg_type & VULKAN_LOADER_ERROR_BIT) != 0) { 117 severity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; 118 } else if ((msg_type & VULKAN_LOADER_DEBUG_BIT) != 0) { 119 severity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT; 120 } else if ((msg_type & VULKAN_LOADER_LAYER_BIT) != 0 || (msg_type & VULKAN_LOADER_DRIVER_BIT) != 0) { 121 // Just driver or just layer bit should be treated as an info message in debug utils. 122 severity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT; 123 } 124 125 if ((msg_type & VULKAN_LOADER_PERF_BIT) != 0) { 126 type = VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; 127 } else if ((msg_type & VULKAN_LOADER_VALIDATION_BIT) != 0) { 128 // For loader logging, if it's a validation message, we still want to also keep the general flag as well 129 // so messages of type validation can still be triggered for general message callbacks. 130 type = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT; 131 } else { 132 type = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT; 133 } 134 135 callback_data.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CALLBACK_DATA_EXT; 136 callback_data.pMessageIdName = "Loader Message"; 137 callback_data.pMessage = msg; 138 callback_data.objectCount = 1; 139 callback_data.pObjects = &object_name; 140 object_name.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT; 141 object_name.objectType = VK_OBJECT_TYPE_INSTANCE; 142 object_name.objectHandle = (uint64_t)(uintptr_t)inst; 143 144 util_SubmitDebugUtilsMessageEXT(inst, severity, type, &callback_data); 145 } 146 147 // Always log to stderr if this is a fatal error 148 if (0 == (msg_type & VULKAN_LOADER_FATAL_ERROR_BIT)) { 149 // Exit early if the current instance settings do not ask for logging to stderr 150 if (inst && inst->settings.settings_active && 0 == (msg_type & inst->settings.debug_level)) { 151 return; 152 // Check the global settings and if that doesn't say to skip, check the environment variable 153 } else if (0 == (msg_type & g_loader_debug)) { 154 return; 155 } 156 } 157 158 // Only need enough space to create the filter description header for log messages 159 // Also use the same header for all output 160 char cmd_line_msg[64]; 161 size_t cmd_line_size = sizeof(cmd_line_msg); 162 size_t num_used = 0; 163 164 cmd_line_msg[0] = '\0'; 165 166// Helper macro which strncat's the given string literal, then updates num_used & cmd_line_end 167// Assumes that we haven't used the entire buffer - must manually check this when adding new filter types 168// We concat at the end of cmd_line_msg, so that strncat isn't a victim of Schlemiel the Painter 169// We write to the end - 1 of cmd_line_msg, as the end is actually a null terminator 170#define STRNCAT_TO_BUFFER(string_literal_to_cat) \ 171 loader_strncat(cmd_line_msg + num_used, cmd_line_size - num_used, string_literal_to_cat, sizeof(string_literal_to_cat)); \ 172 num_used += sizeof(string_literal_to_cat) - 1; // subtract one to remove the null terminator in the string literal 173 174 if ((msg_type & VULKAN_LOADER_ERROR_BIT) != 0) { 175 STRNCAT_TO_BUFFER("ERROR"); 176 } else if ((msg_type & VULKAN_LOADER_WARN_BIT) != 0) { 177 STRNCAT_TO_BUFFER("WARNING"); 178 } else if ((msg_type & VULKAN_LOADER_INFO_BIT) != 0) { 179 STRNCAT_TO_BUFFER("INFO"); 180 } else if ((msg_type & VULKAN_LOADER_DEBUG_BIT) != 0) { 181 STRNCAT_TO_BUFFER("DEBUG"); 182 } 183 184 if ((msg_type & VULKAN_LOADER_PERF_BIT) != 0) { 185 if (num_used > 1) { 186 STRNCAT_TO_BUFFER(" | "); 187 } 188 STRNCAT_TO_BUFFER("PERF"); 189 } 190 if ((msg_type & VULKAN_LOADER_DRIVER_BIT) != 0) { 191 if (num_used > 1) { 192 STRNCAT_TO_BUFFER(" | "); 193 } 194 STRNCAT_TO_BUFFER("DRIVER"); 195 } 196 if ((msg_type & VULKAN_LOADER_LAYER_BIT) != 0) { 197 if (num_used > 1) { 198 STRNCAT_TO_BUFFER(" | "); 199 } 200 STRNCAT_TO_BUFFER("LAYER"); 201 } 202 203 // Add a ": " to separate the filters from the message 204 STRNCAT_TO_BUFFER(": "); 205#undef STRNCAT_TO_BUFFER 206 207 // Justifies the output to at least 19 spaces 208 if (num_used < 19) { 209 const char *space_buffer = " "; 210 // Only write (19 - num_used) spaces 211 loader_strncat(cmd_line_msg + num_used, cmd_line_size - num_used, space_buffer, 19 - num_used); 212 num_used += sizeof(space_buffer) - 1 - num_used; 213 } 214 // Assert that we didn't write more than what is available in cmd_line_msg 215 assert(cmd_line_size > num_used); 216 217#if !defined (__OHOS__) 218 fputs(cmd_line_msg, stderr); 219 fputs(msg, stderr); 220 fputc('\n', stderr); 221#endif 222#if defined(WIN32) 223 OutputDebugString(cmd_line_msg); 224 OutputDebugString(msg); 225 OutputDebugString("\n"); 226#endif 227 228#if defined(__OHOS__) 229 char result[512 + 64]; 230 strcpy(result, cmd_line_msg); 231 strcat(result, msg); 232 OpenHarmonyLog(msg_type, result); 233#endif 234} 235 236void loader_log_asm_function_not_supported(const struct loader_instance *inst, VkFlags msg_type, int32_t msg_code, 237 const char *func_name) { 238 loader_log(inst, msg_type, msg_code, "Function %s not supported for this physical device", func_name); 239} 240