1/* Copyright (c) 2015-2016 The Khronos Group Inc. 2 * Copyright (c) 2015-2016 Valve Corporation 3 * Copyright (c) 2015-2016 LunarG, Inc. 4 * 5 * Licensed under the Apache License, Version 2.0 (the "License"); 6 * you may not use this file except in compliance with the License. 7 * You may obtain a copy of the License at 8 * 9 * http://www.apache.org/licenses/LICENSE-2.0 10 * 11 * Unless required by applicable law or agreed to in writing, software 12 * distributed under the License is distributed on an "AS IS" BASIS, 13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 * See the License for the specific language governing permissions and 15 * limitations under the License. 16 * 17 * Author: Courtney Goeltzenleuchter <courtney@LunarG.com> 18 * Author: Tobin Ehlis <tobin@lunarg.com> 19 * 20 */ 21 22#ifndef LAYER_LOGGING_H 23#define LAYER_LOGGING_H 24 25#include "vk_loader_layer.h" 26#include "vk_layer_config.h" 27#include "vk_layer_data.h" 28#include "vk_layer_table.h" 29#include "vk_loader_platform.h" 30#include "vulkan/vk_layer.h" 31#include <cinttypes> 32#include <stdarg.h> 33#include <stdbool.h> 34#include <stdio.h> 35#include <unordered_map> 36#include <vector> 37 38typedef struct _debug_report_data { 39 VkLayerDbgFunctionNode *debug_callback_list; 40 VkLayerDbgFunctionNode *default_debug_callback_list; 41 VkFlags active_flags; 42 bool g_DEBUG_REPORT; 43} debug_report_data; 44 45template debug_report_data *get_my_data_ptr<debug_report_data>(void *data_key, 46 std::unordered_map<void *, debug_report_data *> &data_map); 47 48// Forward Declarations 49static inline bool debug_report_log_msg(const debug_report_data *debug_data, VkFlags msgFlags, 50 VkDebugReportObjectTypeEXT objectType, uint64_t srcObject, size_t location, int32_t msgCode, 51 const char *pLayerPrefix, const char *pMsg); 52 53// Add a debug message callback node structure to the specified callback linked list 54static inline void AddDebugMessageCallback(debug_report_data *debug_data, VkLayerDbgFunctionNode **list_head, 55 VkLayerDbgFunctionNode *new_node) { 56 57 new_node->pNext = *list_head; 58 *list_head = new_node; 59} 60 61// Remove specified debug message callback node structure from the specified callback linked list 62static inline void RemoveDebugMessageCallback(debug_report_data *debug_data, VkLayerDbgFunctionNode **list_head, 63 VkDebugReportCallbackEXT callback) { 64 VkLayerDbgFunctionNode *cur_callback = *list_head; 65 VkLayerDbgFunctionNode *prev_callback = cur_callback; 66 bool matched = false; 67 68 debug_data->active_flags = 0; 69 while (cur_callback) { 70 if (cur_callback->msgCallback == callback) { 71 matched = true; 72 prev_callback->pNext = cur_callback->pNext; 73 if (*list_head == cur_callback) { 74 *list_head = cur_callback->pNext; 75 } 76 debug_report_log_msg(debug_data, VK_DEBUG_REPORT_DEBUG_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT, 77 reinterpret_cast<uint64_t &>(cur_callback->msgCallback), 0, VK_DEBUG_REPORT_ERROR_CALLBACK_REF_EXT, 78 "DebugReport", "Destroyed callback"); 79 } else { 80 matched = false; 81 debug_data->active_flags |= cur_callback->msgFlags; 82 } 83 prev_callback = cur_callback; 84 cur_callback = cur_callback->pNext; 85 if (matched) { 86 free(prev_callback); 87 } 88 } 89} 90 91// Removes all debug callback function nodes from the specified callback linked lists and frees their resources 92static inline void RemoveAllMessageCallbacks(debug_report_data *debug_data, VkLayerDbgFunctionNode **list_head) { 93 VkLayerDbgFunctionNode *current_callback = *list_head; 94 VkLayerDbgFunctionNode *prev_callback = current_callback; 95 96 while (current_callback) { 97 prev_callback = current_callback->pNext; 98 debug_report_log_msg(debug_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT, 99 (uint64_t)current_callback->msgCallback, 0, VK_DEBUG_REPORT_ERROR_CALLBACK_REF_EXT, "DebugReport", 100 "Debug Report callbacks not removed before DestroyInstance"); 101 free(current_callback); 102 current_callback = prev_callback; 103 } 104 *list_head = NULL; 105} 106 107// Utility function to handle reporting 108static inline bool debug_report_log_msg(const debug_report_data *debug_data, VkFlags msgFlags, 109 VkDebugReportObjectTypeEXT objectType, uint64_t srcObject, size_t location, int32_t msgCode, 110 const char *pLayerPrefix, const char *pMsg) { 111 bool bail = false; 112 VkLayerDbgFunctionNode *pTrav = NULL; 113 114 if (debug_data->debug_callback_list != NULL) { 115 pTrav = debug_data->debug_callback_list; 116 } else { 117 pTrav = debug_data->default_debug_callback_list; 118 } 119 120 while (pTrav) { 121 if (pTrav->msgFlags & msgFlags) { 122 if (pTrav->pfnMsgCallback(msgFlags, objectType, srcObject, location, msgCode, pLayerPrefix, pMsg, pTrav->pUserData)) { 123 bail = true; 124 } 125 } 126 pTrav = pTrav->pNext; 127 } 128 129 return bail; 130} 131 132static inline debug_report_data * 133debug_report_create_instance(VkLayerInstanceDispatchTable *table, VkInstance inst, uint32_t extension_count, 134 const char *const *ppEnabledExtensions) // layer or extension name to be enabled 135{ 136 debug_report_data *debug_data = (debug_report_data *)malloc(sizeof(debug_report_data)); 137 if (!debug_data) 138 return NULL; 139 140 memset(debug_data, 0, sizeof(debug_report_data)); 141 for (uint32_t i = 0; i < extension_count; i++) { 142 // TODO: Check other property fields 143 if (strcmp(ppEnabledExtensions[i], VK_EXT_DEBUG_REPORT_EXTENSION_NAME) == 0) { 144 debug_data->g_DEBUG_REPORT = true; 145 } 146 } 147 return debug_data; 148} 149 150static inline void layer_debug_report_destroy_instance(debug_report_data *debug_data) { 151 if (debug_data) { 152 RemoveAllMessageCallbacks(debug_data, &debug_data->default_debug_callback_list); 153 RemoveAllMessageCallbacks(debug_data, &debug_data->debug_callback_list); 154 free(debug_data); 155 } 156} 157 158static inline debug_report_data *layer_debug_report_create_device(debug_report_data *instance_debug_data, VkDevice device) { 159 // DEBUG_REPORT shares data between Instance and Device, 160 // so just return instance's data pointer 161 return instance_debug_data; 162} 163 164static inline void layer_debug_report_destroy_device(VkDevice device) { 165 // Nothing to do since we're using instance data record 166} 167 168static inline void layer_destroy_msg_callback(debug_report_data *debug_data, VkDebugReportCallbackEXT callback, 169 const VkAllocationCallbacks *pAllocator) { 170 RemoveDebugMessageCallback(debug_data, &debug_data->debug_callback_list, callback); 171 RemoveDebugMessageCallback(debug_data, &debug_data->default_debug_callback_list, callback); 172} 173 174static inline VkResult layer_create_msg_callback(debug_report_data *debug_data, bool default_callback, 175 const VkDebugReportCallbackCreateInfoEXT *pCreateInfo, 176 const VkAllocationCallbacks *pAllocator, VkDebugReportCallbackEXT *pCallback) { 177 VkLayerDbgFunctionNode *pNewDbgFuncNode = (VkLayerDbgFunctionNode *)malloc(sizeof(VkLayerDbgFunctionNode)); 178 if (!pNewDbgFuncNode) 179 return VK_ERROR_OUT_OF_HOST_MEMORY; 180 181 // Handle of 0 is logging_callback so use allocated Node address as unique handle 182 if (!(*pCallback)) 183 *pCallback = (VkDebugReportCallbackEXT)pNewDbgFuncNode; 184 pNewDbgFuncNode->msgCallback = *pCallback; 185 pNewDbgFuncNode->pfnMsgCallback = pCreateInfo->pfnCallback; 186 pNewDbgFuncNode->msgFlags = pCreateInfo->flags; 187 pNewDbgFuncNode->pUserData = pCreateInfo->pUserData; 188 189 if (default_callback) { 190 AddDebugMessageCallback(debug_data, &debug_data->default_debug_callback_list, pNewDbgFuncNode); 191 } else { 192 AddDebugMessageCallback(debug_data, &debug_data->debug_callback_list, pNewDbgFuncNode); 193 } 194 debug_data->active_flags |= pCreateInfo->flags; 195 196 debug_report_log_msg(debug_data, VK_DEBUG_REPORT_DEBUG_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT, 197 (uint64_t)*pCallback, 0, VK_DEBUG_REPORT_ERROR_CALLBACK_REF_EXT, "DebugReport", "Added callback"); 198 return VK_SUCCESS; 199} 200 201static inline PFN_vkVoidFunction debug_report_get_instance_proc_addr(debug_report_data *debug_data, const char *funcName) { 202 if (!debug_data || !debug_data->g_DEBUG_REPORT) { 203 return NULL; 204 } 205 206 if (!strcmp(funcName, "vkCreateDebugReportCallbackEXT")) { 207 return (PFN_vkVoidFunction)vkCreateDebugReportCallbackEXT; 208 } 209 if (!strcmp(funcName, "vkDestroyDebugReportCallbackEXT")) { 210 return (PFN_vkVoidFunction)vkDestroyDebugReportCallbackEXT; 211 } 212 if (!strcmp(funcName, "vkDebugReportMessageEXT")) { 213 return (PFN_vkVoidFunction)vkDebugReportMessageEXT; 214 } 215 return NULL; 216} 217 218// This utility (called at vkCreateInstance() time), looks at a pNext chain. 219// It counts any VkDebugReportCallbackCreateInfoEXT structs that it finds. It 220// then allocates an array that can hold that many structs, as well as that 221// many VkDebugReportCallbackEXT handles. It then copies each 222// VkDebugReportCallbackCreateInfoEXT, and initializes each handle. 223static VkResult layer_copy_tmp_callbacks(const void *pChain, uint32_t *num_callbacks, VkDebugReportCallbackCreateInfoEXT **infos, 224 VkDebugReportCallbackEXT **callbacks) { 225 uint32_t n = *num_callbacks = 0; 226 227 const void *pNext = pChain; 228 while (pNext) { 229 // 1st, count the number VkDebugReportCallbackCreateInfoEXT: 230 if (((VkDebugReportCallbackCreateInfoEXT *)pNext)->sType == VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT) { 231 n++; 232 } 233 pNext = (void *)((VkDebugReportCallbackCreateInfoEXT *)pNext)->pNext; 234 } 235 if (n == 0) { 236 return VK_SUCCESS; 237 } 238 239 // 2nd, allocate memory for each VkDebugReportCallbackCreateInfoEXT: 240 VkDebugReportCallbackCreateInfoEXT *pInfos = *infos = 241 ((VkDebugReportCallbackCreateInfoEXT *)malloc(n * sizeof(VkDebugReportCallbackCreateInfoEXT))); 242 if (!pInfos) { 243 return VK_ERROR_OUT_OF_HOST_MEMORY; 244 } 245 // 3rd, allocate memory for a unique handle for each callback: 246 VkDebugReportCallbackEXT *pCallbacks = *callbacks = ((VkDebugReportCallbackEXT *)malloc(n * sizeof(VkDebugReportCallbackEXT))); 247 if (!pCallbacks) { 248 free(pInfos); 249 return VK_ERROR_OUT_OF_HOST_MEMORY; 250 } 251 // 4th, copy each VkDebugReportCallbackCreateInfoEXT for use by 252 // vkDestroyInstance, and assign a unique handle to each callback (just 253 // use the address of the copied VkDebugReportCallbackCreateInfoEXT): 254 pNext = pChain; 255 while (pNext) { 256 if (((VkInstanceCreateInfo *)pNext)->sType == VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT) { 257 memcpy(pInfos, pNext, sizeof(VkDebugReportCallbackCreateInfoEXT)); 258 *pCallbacks++ = (VkDebugReportCallbackEXT)pInfos++; 259 } 260 pNext = (void *)((VkInstanceCreateInfo *)pNext)->pNext; 261 } 262 263 *num_callbacks = n; 264 return VK_SUCCESS; 265} 266 267// This utility frees the arrays allocated by layer_copy_tmp_callbacks() 268static void layer_free_tmp_callbacks(VkDebugReportCallbackCreateInfoEXT *infos, VkDebugReportCallbackEXT *callbacks) { 269 free(infos); 270 free(callbacks); 271} 272 273// This utility enables all of the VkDebugReportCallbackCreateInfoEXT structs 274// that were copied by layer_copy_tmp_callbacks() 275static VkResult layer_enable_tmp_callbacks(debug_report_data *debug_data, uint32_t num_callbacks, 276 VkDebugReportCallbackCreateInfoEXT *infos, VkDebugReportCallbackEXT *callbacks) { 277 VkResult rtn = VK_SUCCESS; 278 for (uint32_t i = 0; i < num_callbacks; i++) { 279 rtn = layer_create_msg_callback(debug_data, false, &infos[i], NULL, &callbacks[i]); 280 if (rtn != VK_SUCCESS) { 281 for (uint32_t j = 0; j < i; j++) { 282 layer_destroy_msg_callback(debug_data, callbacks[j], NULL); 283 } 284 return rtn; 285 } 286 } 287 return rtn; 288} 289 290// This utility disables all of the VkDebugReportCallbackCreateInfoEXT structs 291// that were copied by layer_copy_tmp_callbacks() 292static void layer_disable_tmp_callbacks(debug_report_data *debug_data, uint32_t num_callbacks, 293 VkDebugReportCallbackEXT *callbacks) { 294 for (uint32_t i = 0; i < num_callbacks; i++) { 295 layer_destroy_msg_callback(debug_data, callbacks[i], NULL); 296 } 297} 298 299// Checks if the message will get logged. 300// Allows layer to defer collecting & formating data if the 301// message will be discarded. 302static inline bool will_log_msg(debug_report_data *debug_data, VkFlags msgFlags) { 303 if (!debug_data || !(debug_data->active_flags & msgFlags)) { 304 // Message is not wanted 305 return false; 306 } 307 308 return true; 309} 310 311#ifdef WIN32 312static inline int vasprintf(char **strp, char const *fmt, va_list ap) { 313 *strp = nullptr; 314 int size = _vscprintf(fmt, ap); 315 if (size >= 0) { 316 *strp = (char *)malloc(size+1); 317 if (!*strp) { 318 return -1; 319 } 320 _vsnprintf(*strp, size+1, fmt, ap); 321 } 322 return size; 323} 324#endif 325 326// Output log message via DEBUG_REPORT 327// Takes format and variable arg list so that output string 328// is only computed if a message needs to be logged 329#ifndef WIN32 330static inline bool log_msg(const debug_report_data *debug_data, VkFlags msgFlags, VkDebugReportObjectTypeEXT objectType, 331 uint64_t srcObject, size_t location, int32_t msgCode, const char *pLayerPrefix, const char *format, ...) 332 __attribute__((format(printf, 8, 9))); 333#endif 334static inline bool log_msg(const debug_report_data *debug_data, VkFlags msgFlags, VkDebugReportObjectTypeEXT objectType, 335 uint64_t srcObject, size_t location, int32_t msgCode, const char *pLayerPrefix, const char *format, 336 ...) { 337 if (!debug_data || !(debug_data->active_flags & msgFlags)) { 338 // Message is not wanted 339 return false; 340 } 341 342 va_list argptr; 343 va_start(argptr, format); 344 char *str; 345 if (-1 == vasprintf(&str, format, argptr)) { 346 // On failure, glibc vasprintf leaves str undefined 347 str = nullptr; 348 } 349 va_end(argptr); 350 bool result = debug_report_log_msg(debug_data, msgFlags, objectType, srcObject, location, msgCode, pLayerPrefix, 351 str ? str : "Allocation failure"); 352 free(str); 353 return result; 354} 355 356static inline VKAPI_ATTR VkBool32 VKAPI_CALL log_callback(VkFlags msgFlags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject, 357 size_t location, int32_t msgCode, const char *pLayerPrefix, 358 const char *pMsg, void *pUserData) { 359 char msg_flags[30]; 360 361 print_msg_flags(msgFlags, msg_flags); 362 363 fprintf((FILE *)pUserData, "%s(%s): object: 0x%" PRIx64 " type: %d location: %lu msgCode: %d: %s\n", pLayerPrefix, msg_flags, 364 srcObject, objType, (unsigned long)location, msgCode, pMsg); 365 fflush((FILE *)pUserData); 366 367 return false; 368} 369 370static inline VKAPI_ATTR VkBool32 VKAPI_CALL win32_debug_output_msg(VkFlags msgFlags, VkDebugReportObjectTypeEXT objType, 371 uint64_t srcObject, size_t location, int32_t msgCode, 372 const char *pLayerPrefix, const char *pMsg, void *pUserData) { 373#ifdef WIN32 374 char msg_flags[30]; 375 char buf[2048]; 376 377 print_msg_flags(msgFlags, msg_flags); 378 _snprintf(buf, sizeof(buf) - 1, 379 "%s (%s): object: 0x%" PRIxPTR " type: %d location: " PRINTF_SIZE_T_SPECIFIER " msgCode: %d: %s\n", pLayerPrefix, 380 msg_flags, (size_t)srcObject, objType, location, msgCode, pMsg); 381 382 OutputDebugString(buf); 383#endif 384 385 return false; 386} 387 388#endif // LAYER_LOGGING_H 389