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 * Permission is hereby granted, free of charge, to any person obtaining a copy 6 * of this software and/or associated documentation files (the "Materials"), to 7 * deal in the Materials without restriction, including without limitation the 8 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 9 * sell copies of the Materials, and to permit persons to whom the Materials 10 * are furnished to do so, subject to the following conditions: 11 * 12 * The above copyright notice(s) and this permission notice shall be included 13 * in all copies or substantial portions of the Materials. 14 * 15 * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 18 * 19 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 20 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 21 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE 22 * USE OR OTHER DEALINGS IN THE MATERIALS 23 * 24 * Author: Courtney Goeltzenleuchter <courtney@LunarG.com> 25 * Author: Tobin Ehlis <tobin@lunarg.com> 26 * 27 */ 28 29#ifndef LAYER_LOGGING_H 30#define LAYER_LOGGING_H 31 32#include <stdio.h> 33#include <stdarg.h> 34#include <stdbool.h> 35#include <unordered_map> 36#include <inttypes.h> 37#include "vk_loader_platform.h" 38#include "vulkan/vk_layer.h" 39#include "vk_layer_data.h" 40#include "vk_layer_table.h" 41 42typedef struct _debug_report_data { 43 VkLayerDbgFunctionNode *g_pDbgFunctionHead; 44 VkFlags active_flags; 45 bool g_DEBUG_REPORT; 46} debug_report_data; 47 48template debug_report_data *get_my_data_ptr<debug_report_data>(void *data_key, 49 std::unordered_map<void *, debug_report_data *> &data_map); 50 51// Utility function to handle reporting 52static inline VkBool32 debug_report_log_msg(debug_report_data *debug_data, VkFlags msgFlags, VkDebugReportObjectTypeEXT objectType, 53 uint64_t srcObject, size_t location, int32_t msgCode, const char *pLayerPrefix, 54 const char *pMsg) { 55 VkBool32 bail = false; 56 VkLayerDbgFunctionNode *pTrav = debug_data->g_pDbgFunctionHead; 57 while (pTrav) { 58 if (pTrav->msgFlags & msgFlags) { 59 if (pTrav->pfnMsgCallback(msgFlags, objectType, srcObject, location, msgCode, pLayerPrefix, pMsg, pTrav->pUserData)) { 60 bail = true; 61 } 62 } 63 pTrav = pTrav->pNext; 64 } 65 66 return bail; 67} 68 69static inline debug_report_data * 70debug_report_create_instance(VkLayerInstanceDispatchTable *table, VkInstance inst, uint32_t extension_count, 71 const char *const *ppEnabledExtensions) // layer or extension name to be enabled 72{ 73 debug_report_data *debug_data; 74 PFN_vkGetInstanceProcAddr gpa = table->GetInstanceProcAddr; 75 76 table->CreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)gpa(inst, "vkCreateDebugReportCallbackEXT"); 77 table->DestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT)gpa(inst, "vkDestroyDebugReportCallbackEXT"); 78 table->DebugReportMessageEXT = (PFN_vkDebugReportMessageEXT)gpa(inst, "vkDebugReportMessageEXT"); 79 80 debug_data = (debug_report_data *)malloc(sizeof(debug_report_data)); 81 if (!debug_data) 82 return NULL; 83 84 memset(debug_data, 0, sizeof(debug_report_data)); 85 for (uint32_t i = 0; i < extension_count; i++) { 86 /* TODO: Check other property fields */ 87 if (strcmp(ppEnabledExtensions[i], VK_EXT_DEBUG_REPORT_EXTENSION_NAME) == 0) { 88 debug_data->g_DEBUG_REPORT = true; 89 } 90 } 91 return debug_data; 92} 93 94static inline void layer_debug_report_destroy_instance(debug_report_data *debug_data) { 95 VkLayerDbgFunctionNode *pTrav; 96 VkLayerDbgFunctionNode *pTravNext; 97 98 if (!debug_data) { 99 return; 100 } 101 102 pTrav = debug_data->g_pDbgFunctionHead; 103 /* Clear out any leftover callbacks */ 104 while (pTrav) { 105 pTravNext = pTrav->pNext; 106 107 debug_report_log_msg(debug_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT, 108 (uint64_t)pTrav->msgCallback, 0, VK_DEBUG_REPORT_ERROR_CALLBACK_REF_EXT, "DebugReport", 109 "Debug Report callbacks not removed before DestroyInstance"); 110 111 free(pTrav); 112 pTrav = pTravNext; 113 } 114 debug_data->g_pDbgFunctionHead = NULL; 115 116 free(debug_data); 117} 118 119static inline debug_report_data *layer_debug_report_create_device(debug_report_data *instance_debug_data, VkDevice device) { 120 /* DEBUG_REPORT shares data between Instance and Device, 121 * so just return instance's data pointer */ 122 return instance_debug_data; 123} 124 125static inline void layer_debug_report_destroy_device(VkDevice device) { /* Nothing to do since we're using instance data record */ } 126 127static inline VkResult layer_create_msg_callback(debug_report_data *debug_data, 128 const VkDebugReportCallbackCreateInfoEXT *pCreateInfo, 129 const VkAllocationCallbacks *pAllocator, VkDebugReportCallbackEXT *pCallback) { 130 /* TODO: Use app allocator */ 131 VkLayerDbgFunctionNode *pNewDbgFuncNode = (VkLayerDbgFunctionNode *)malloc(sizeof(VkLayerDbgFunctionNode)); 132 if (!pNewDbgFuncNode) 133 return VK_ERROR_OUT_OF_HOST_MEMORY; 134 135 // Handle of 0 is logging_callback so use allocated Node address as unique handle 136 if (!(*pCallback)) 137 *pCallback = (VkDebugReportCallbackEXT)pNewDbgFuncNode; 138 pNewDbgFuncNode->msgCallback = *pCallback; 139 pNewDbgFuncNode->pfnMsgCallback = pCreateInfo->pfnCallback; 140 pNewDbgFuncNode->msgFlags = pCreateInfo->flags; 141 pNewDbgFuncNode->pUserData = pCreateInfo->pUserData; 142 pNewDbgFuncNode->pNext = debug_data->g_pDbgFunctionHead; 143 144 debug_data->g_pDbgFunctionHead = pNewDbgFuncNode; 145 debug_data->active_flags |= pCreateInfo->flags; 146 147 debug_report_log_msg(debug_data, VK_DEBUG_REPORT_DEBUG_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT, 148 (uint64_t)*pCallback, 0, VK_DEBUG_REPORT_ERROR_CALLBACK_REF_EXT, "DebugReport", "Added callback"); 149 return VK_SUCCESS; 150} 151 152static inline void layer_destroy_msg_callback(debug_report_data *debug_data, VkDebugReportCallbackEXT callback, 153 const VkAllocationCallbacks *pAllocator) { 154 VkLayerDbgFunctionNode *pTrav = debug_data->g_pDbgFunctionHead; 155 VkLayerDbgFunctionNode *pPrev = pTrav; 156 bool matched; 157 158 debug_data->active_flags = 0; 159 while (pTrav) { 160 if (pTrav->msgCallback == callback) { 161 matched = true; 162 pPrev->pNext = pTrav->pNext; 163 if (debug_data->g_pDbgFunctionHead == pTrav) { 164 debug_data->g_pDbgFunctionHead = pTrav->pNext; 165 } 166 debug_report_log_msg(debug_data, VK_DEBUG_REPORT_DEBUG_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT, 167 (uint64_t)pTrav->msgCallback, 0, VK_DEBUG_REPORT_ERROR_CALLBACK_REF_EXT, "DebugReport", 168 "Destroyed callback"); 169 } else { 170 matched = false; 171 debug_data->active_flags |= pTrav->msgFlags; 172 } 173 pPrev = pTrav; 174 pTrav = pTrav->pNext; 175 if (matched) { 176 /* TODO: Use pAllocator */ 177 free(pPrev); 178 } 179 } 180} 181 182static inline PFN_vkVoidFunction debug_report_get_instance_proc_addr(debug_report_data *debug_data, const char *funcName) { 183 if (!debug_data || !debug_data->g_DEBUG_REPORT) { 184 return NULL; 185 } 186 187 if (!strcmp(funcName, "vkCreateDebugReportCallbackEXT")) { 188 return (PFN_vkVoidFunction)vkCreateDebugReportCallbackEXT; 189 } 190 if (!strcmp(funcName, "vkDestroyDebugReportCallbackEXT")) { 191 return (PFN_vkVoidFunction)vkDestroyDebugReportCallbackEXT; 192 } 193 194 if (!strcmp(funcName, "vkDebugReportMessageEXT")) { 195 return (PFN_vkVoidFunction)vkDebugReportMessageEXT; 196 } 197 198 return NULL; 199} 200 201/* 202 * Checks if the message will get logged. 203 * Allows layer to defer collecting & formating data if the 204 * message will be discarded. 205 */ 206static inline VkBool32 will_log_msg(debug_report_data *debug_data, VkFlags msgFlags) { 207 if (!debug_data || !(debug_data->active_flags & msgFlags)) { 208 /* message is not wanted */ 209 return false; 210 } 211 212 return true; 213} 214 215/* 216 * Output log message via DEBUG_REPORT 217 * Takes format and variable arg list so that output string 218 * is only computed if a message needs to be logged 219 */ 220#ifndef WIN32 221static inline VkBool32 log_msg(debug_report_data *debug_data, VkFlags msgFlags, VkDebugReportObjectTypeEXT objectType, 222 uint64_t srcObject, size_t location, int32_t msgCode, const char *pLayerPrefix, const char *format, 223 ...) __attribute__((format(printf, 8, 9))); 224#endif 225static inline VkBool32 log_msg(debug_report_data *debug_data, VkFlags msgFlags, VkDebugReportObjectTypeEXT objectType, 226 uint64_t srcObject, size_t location, int32_t msgCode, const char *pLayerPrefix, const char *format, 227 ...) { 228 if (!debug_data || !(debug_data->active_flags & msgFlags)) { 229 /* message is not wanted */ 230 return false; 231 } 232 233 char str[1024]; 234 va_list argptr; 235 va_start(argptr, format); 236 vsnprintf(str, 1024, format, argptr); 237 va_end(argptr); 238 return debug_report_log_msg(debug_data, msgFlags, objectType, srcObject, location, msgCode, pLayerPrefix, str); 239} 240 241static inline VKAPI_ATTR VkBool32 VKAPI_CALL log_callback(VkFlags msgFlags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject, 242 size_t location, int32_t msgCode, const char *pLayerPrefix, 243 const char *pMsg, void *pUserData) { 244 char msg_flags[30]; 245 246 print_msg_flags(msgFlags, msg_flags); 247 248 fprintf((FILE *)pUserData, "%s(%s): object: %#" PRIx64 " type: %d location: %lu msgCode: %d: %s\n", pLayerPrefix, msg_flags, 249 srcObject, objType, (unsigned long)location, msgCode, pMsg); 250 fflush((FILE *)pUserData); 251 252 return false; 253} 254 255static inline VKAPI_ATTR VkBool32 VKAPI_CALL win32_debug_output_msg(VkFlags msgFlags, VkDebugReportObjectTypeEXT objType, 256 uint64_t srcObject, size_t location, int32_t msgCode, 257 const char *pLayerPrefix, const char *pMsg, void *pUserData) { 258#ifdef WIN32 259 char msg_flags[30]; 260 char buf[2048]; 261 262 print_msg_flags(msgFlags, msg_flags); 263 _snprintf(buf, sizeof(buf) - 1, 264 "%s (%s): object: 0x%" PRIxPTR " type: %d location: " PRINTF_SIZE_T_SPECIFIER " msgCode: %d: %s\n", pLayerPrefix, 265 msg_flags, (size_t)srcObject, objType, location, msgCode, pMsg); 266 267 OutputDebugString(buf); 268#endif 269 270 return false; 271} 272 273#endif // LAYER_LOGGING_H 274