1 /* 2 * Copyright (c) 2015-2016 The Khronos Group Inc. 3 * Copyright (c) 2015-2016 Valve Corporation 4 * Copyright (c) 2015-2016 LunarG, Inc. 5 * Copyright (C) 2015-2016 Google Inc. 6 * 7 * Permission is hereby granted, free of charge, to any person obtaining a copy 8 * of this software and/or associated documentation files (the "Materials"), to 9 * deal in the Materials without restriction, including without limitation the 10 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 11 * sell copies of the Materials, and to permit persons to whom the Materials are 12 * furnished to do so, subject to the following conditions: 13 * 14 * The above copyright notice(s) and this permission notice shall be included in 15 * all copies or substantial portions of the Materials. 16 * 17 * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 * 21 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 22 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 23 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE 24 * USE OR OTHER DEALINGS IN THE MATERIALS. 25 * 26 * Author: Courtney Goeltzenleuchter <courtney (at) LunarG.com> 27 * Author: Jon Ashburn <jon (at) LunarG.com> 28 * 29 */ 30 31 #define _GNU_SOURCE 32 #include <stdio.h> 33 #include <string.h> 34 #include <stdlib.h> 35 #include <inttypes.h> 36 #ifndef WIN32 37 #include <signal.h> 38 #else 39 #endif 40 #include "vk_loader_platform.h" 41 #include "debug_report.h" 42 #include "vulkan/vk_layer.h" 43 44 typedef void(VKAPI_PTR *PFN_stringCallback)(char *message); 45 46 static const VkExtensionProperties debug_report_extension_info = { 47 .extensionName = VK_EXT_DEBUG_REPORT_EXTENSION_NAME, 48 .specVersion = VK_EXT_DEBUG_REPORT_SPEC_VERSION, 49 }; 50 51 void debug_report_add_instance_extensions( 52 const struct loader_instance *inst, 53 struct loader_extension_list *ext_list) { 54 loader_add_to_ext_list(inst, ext_list, 1, &debug_report_extension_info); 55 } 56 57 void debug_report_create_instance(struct loader_instance *ptr_instance, 58 const VkInstanceCreateInfo *pCreateInfo) { 59 ptr_instance->debug_report_enabled = false; 60 61 for (uint32_t i = 0; i < pCreateInfo->enabledExtensionCount; i++) { 62 if (strcmp(pCreateInfo->ppEnabledExtensionNames[i], 63 VK_EXT_DEBUG_REPORT_EXTENSION_NAME) == 0) { 64 ptr_instance->debug_report_enabled = true; 65 return; 66 } 67 } 68 } 69 70 VkResult 71 util_CreateDebugReportCallback(struct loader_instance *inst, 72 VkDebugReportCallbackCreateInfoEXT *pCreateInfo, 73 const VkAllocationCallbacks *pAllocator, 74 VkDebugReportCallbackEXT callback) { 75 VkLayerDbgFunctionNode *pNewDbgFuncNode; 76 if (pAllocator != NULL) { 77 pNewDbgFuncNode = (VkLayerDbgFunctionNode *)pAllocator->pfnAllocation( 78 pAllocator->pUserData, sizeof(VkLayerDbgFunctionNode), 79 sizeof(int *), VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE); 80 } else { 81 pNewDbgFuncNode = (VkLayerDbgFunctionNode *)loader_heap_alloc( 82 inst, sizeof(VkLayerDbgFunctionNode), 83 VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); 84 } 85 if (!pNewDbgFuncNode) 86 return VK_ERROR_OUT_OF_HOST_MEMORY; 87 88 pNewDbgFuncNode->msgCallback = callback; 89 pNewDbgFuncNode->pfnMsgCallback = pCreateInfo->pfnCallback; 90 pNewDbgFuncNode->msgFlags = pCreateInfo->flags; 91 pNewDbgFuncNode->pUserData = pCreateInfo->pUserData; 92 pNewDbgFuncNode->pNext = inst->DbgFunctionHead; 93 inst->DbgFunctionHead = pNewDbgFuncNode; 94 95 return VK_SUCCESS; 96 } 97 98 static VKAPI_ATTR VkResult VKAPI_CALL debug_report_CreateDebugReportCallback( 99 VkInstance instance, VkDebugReportCallbackCreateInfoEXT *pCreateInfo, 100 VkAllocationCallbacks *pAllocator, VkDebugReportCallbackEXT *pCallback) { 101 struct loader_instance *inst = loader_get_instance(instance); 102 loader_platform_thread_lock_mutex(&loader_lock); 103 VkResult result = inst->disp->CreateDebugReportCallbackEXT( 104 instance, pCreateInfo, pAllocator, pCallback); 105 if (result == VK_SUCCESS) { 106 result = util_CreateDebugReportCallback(inst, pCreateInfo, pAllocator, 107 *pCallback); 108 } 109 loader_platform_thread_unlock_mutex(&loader_lock); 110 return result; 111 } 112 113 // Utility function to handle reporting 114 VkBool32 util_DebugReportMessage(const struct loader_instance *inst, 115 VkFlags msgFlags, 116 VkDebugReportObjectTypeEXT objectType, 117 uint64_t srcObject, size_t location, 118 int32_t msgCode, const char *pLayerPrefix, 119 const char *pMsg) { 120 VkBool32 bail = false; 121 VkLayerDbgFunctionNode *pTrav = inst->DbgFunctionHead; 122 while (pTrav) { 123 if (pTrav->msgFlags & msgFlags) { 124 if (pTrav->pfnMsgCallback(msgFlags, objectType, srcObject, location, 125 msgCode, pLayerPrefix, pMsg, 126 pTrav->pUserData)) { 127 bail = true; 128 } 129 } 130 pTrav = pTrav->pNext; 131 } 132 133 return bail; 134 } 135 136 void util_DestroyDebugReportCallback(struct loader_instance *inst, 137 VkDebugReportCallbackEXT callback, 138 const VkAllocationCallbacks *pAllocator) { 139 VkLayerDbgFunctionNode *pTrav = inst->DbgFunctionHead; 140 VkLayerDbgFunctionNode *pPrev = pTrav; 141 142 while (pTrav) { 143 if (pTrav->msgCallback == callback) { 144 pPrev->pNext = pTrav->pNext; 145 if (inst->DbgFunctionHead == pTrav) 146 inst->DbgFunctionHead = pTrav->pNext; 147 if (pAllocator != NULL) { 148 pAllocator->pfnFree(pAllocator->pUserData, pTrav); 149 } else { 150 loader_heap_free(inst, pTrav); 151 } 152 break; 153 } 154 pPrev = pTrav; 155 pTrav = pTrav->pNext; 156 } 157 } 158 159 static VKAPI_ATTR void VKAPI_CALL 160 debug_report_DestroyDebugReportCallback(VkInstance instance, 161 VkDebugReportCallbackEXT callback, 162 VkAllocationCallbacks *pAllocator) { 163 struct loader_instance *inst = loader_get_instance(instance); 164 loader_platform_thread_lock_mutex(&loader_lock); 165 166 inst->disp->DestroyDebugReportCallbackEXT(instance, callback, pAllocator); 167 168 util_DestroyDebugReportCallback(inst, callback, pAllocator); 169 170 loader_platform_thread_unlock_mutex(&loader_lock); 171 } 172 173 static VKAPI_ATTR void VKAPI_CALL debug_report_DebugReportMessage( 174 VkInstance instance, VkDebugReportFlagsEXT flags, 175 VkDebugReportObjectTypeEXT objType, uint64_t object, size_t location, 176 int32_t msgCode, const char *pLayerPrefix, const char *pMsg) { 177 struct loader_instance *inst = loader_get_instance(instance); 178 179 inst->disp->DebugReportMessageEXT(instance, flags, objType, object, 180 location, msgCode, pLayerPrefix, pMsg); 181 } 182 183 /* 184 * This is the instance chain terminator function 185 * for CreateDebugReportCallback 186 */ 187 188 VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateDebugReportCallback( 189 VkInstance instance, const VkDebugReportCallbackCreateInfoEXT *pCreateInfo, 190 const VkAllocationCallbacks *pAllocator, 191 VkDebugReportCallbackEXT *pCallback) { 192 VkDebugReportCallbackEXT *icd_info; 193 const struct loader_icd *icd; 194 struct loader_instance *inst = (struct loader_instance *)instance; 195 VkResult res = VK_SUCCESS; 196 uint32_t storage_idx; 197 198 icd_info = calloc(sizeof(VkDebugReportCallbackEXT), inst->total_icd_count); 199 if (!icd_info) { 200 return VK_ERROR_OUT_OF_HOST_MEMORY; 201 } 202 203 storage_idx = 0; 204 for (icd = inst->icds; icd; icd = icd->next) { 205 if (!icd->CreateDebugReportCallbackEXT) { 206 continue; 207 } 208 209 res = icd->CreateDebugReportCallbackEXT( 210 icd->instance, pCreateInfo, pAllocator, &icd_info[storage_idx]); 211 212 if (res != VK_SUCCESS) { 213 break; 214 } 215 storage_idx++; 216 } 217 218 /* roll back on errors */ 219 if (icd) { 220 storage_idx = 0; 221 for (icd = inst->icds; icd; icd = icd->next) { 222 if (icd_info[storage_idx]) { 223 icd->DestroyDebugReportCallbackEXT( 224 icd->instance, icd_info[storage_idx], pAllocator); 225 } 226 storage_idx++; 227 } 228 229 return res; 230 } 231 232 *(VkDebugReportCallbackEXT **)pCallback = icd_info; 233 234 return VK_SUCCESS; 235 } 236 237 /* 238 * This is the instance chain terminator function 239 * for DestroyDebugReportCallback 240 */ 241 VKAPI_ATTR void VKAPI_CALL 242 terminator_DestroyDebugReportCallback(VkInstance instance, 243 VkDebugReportCallbackEXT callback, 244 const VkAllocationCallbacks *pAllocator) { 245 uint32_t storage_idx; 246 VkDebugReportCallbackEXT *icd_info; 247 const struct loader_icd *icd; 248 249 struct loader_instance *inst = (struct loader_instance *)instance; 250 icd_info = *(VkDebugReportCallbackEXT **)&callback; 251 storage_idx = 0; 252 for (icd = inst->icds; icd; icd = icd->next) { 253 if (icd_info[storage_idx]) { 254 icd->DestroyDebugReportCallbackEXT( 255 icd->instance, icd_info[storage_idx], pAllocator); 256 } 257 storage_idx++; 258 } 259 } 260 261 /* 262 * This is the instance chain terminator function 263 * for DebugReportMessage 264 */ 265 VKAPI_ATTR void VKAPI_CALL 266 terminator_DebugReportMessage(VkInstance instance, VkDebugReportFlagsEXT flags, 267 VkDebugReportObjectTypeEXT objType, 268 uint64_t object, size_t location, int32_t msgCode, 269 const char *pLayerPrefix, const char *pMsg) { 270 const struct loader_icd *icd; 271 272 struct loader_instance *inst = (struct loader_instance *)instance; 273 274 loader_platform_thread_lock_mutex(&loader_lock); 275 for (icd = inst->icds; icd; icd = icd->next) { 276 if (icd->DebugReportMessageEXT != NULL) { 277 icd->DebugReportMessageEXT(icd->instance, flags, objType, object, 278 location, msgCode, pLayerPrefix, pMsg); 279 } 280 } 281 282 /* 283 * Now that all ICDs have seen the message, call the necessary callbacks. 284 * Ignoring "bail" return value as there is nothing to bail from at this 285 * point. 286 */ 287 288 util_DebugReportMessage(inst, flags, objType, object, location, msgCode, 289 pLayerPrefix, pMsg); 290 291 loader_platform_thread_unlock_mutex(&loader_lock); 292 } 293 294 bool debug_report_instance_gpa(struct loader_instance *ptr_instance, 295 const char *name, void **addr) { 296 // debug_report is currently advertised to be supported by the loader, 297 // so always return the entry points if name matches and it's enabled 298 *addr = NULL; 299 300 if (!strcmp("vkCreateDebugReportCallbackEXT", name)) { 301 *addr = ptr_instance->debug_report_enabled 302 ? (void *)debug_report_CreateDebugReportCallback 303 : NULL; 304 return true; 305 } 306 if (!strcmp("vkDestroyDebugReportCallbackEXT", name)) { 307 *addr = ptr_instance->debug_report_enabled 308 ? (void *)debug_report_DestroyDebugReportCallback 309 : NULL; 310 return true; 311 } 312 if (!strcmp("vkDebugReportMessageEXT", name)) { 313 *addr = ptr_instance->debug_report_enabled 314 ? (void *)debug_report_DebugReportMessage 315 : NULL; 316 return true; 317 } 318 return false; 319 } 320