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 * 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: Courtney Goeltzenleuchter <courtney (at) LunarG.com> 20 * Author: Jon Ashburn <jon (at) LunarG.com> 21 * 22 */ 23 24 #define _GNU_SOURCE 25 #include <stdio.h> 26 #include <string.h> 27 #include <stdlib.h> 28 #include <inttypes.h> 29 #ifndef WIN32 30 #include <signal.h> 31 #else 32 #endif 33 #include "vk_loader_platform.h" 34 #include "debug_report.h" 35 #include "vulkan/vk_layer.h" 36 37 typedef void(VKAPI_PTR *PFN_stringCallback)(char *message); 38 39 static const VkExtensionProperties debug_report_extension_info = { 40 .extensionName = VK_EXT_DEBUG_REPORT_EXTENSION_NAME, .specVersion = VK_EXT_DEBUG_REPORT_SPEC_VERSION, 41 }; 42 43 void debug_report_add_instance_extensions(const struct loader_instance *inst, struct loader_extension_list *ext_list) { 44 loader_add_to_ext_list(inst, ext_list, 1, &debug_report_extension_info); 45 } 46 47 void debug_report_create_instance(struct loader_instance *ptr_instance, const VkInstanceCreateInfo *pCreateInfo) { 48 ptr_instance->enabled_known_extensions.ext_debug_report = 0; 49 50 for (uint32_t i = 0; i < pCreateInfo->enabledExtensionCount; i++) { 51 if (strcmp(pCreateInfo->ppEnabledExtensionNames[i], VK_EXT_DEBUG_REPORT_EXTENSION_NAME) == 0) { 52 ptr_instance->enabled_known_extensions.ext_debug_report = 1; 53 return; 54 } 55 } 56 } 57 58 VkResult util_CreateDebugReportCallback(struct loader_instance *inst, VkDebugReportCallbackCreateInfoEXT *pCreateInfo, 59 const VkAllocationCallbacks *pAllocator, VkDebugReportCallbackEXT callback) { 60 VkLayerDbgFunctionNode *pNewDbgFuncNode = NULL; 61 62 #if (DEBUG_DISABLE_APP_ALLOCATORS == 1) 63 { 64 #else 65 if (pAllocator != NULL) { 66 pNewDbgFuncNode = (VkLayerDbgFunctionNode *)pAllocator->pfnAllocation(pAllocator->pUserData, sizeof(VkLayerDbgFunctionNode), 67 sizeof(int *), VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); 68 } else { 69 #endif 70 pNewDbgFuncNode = (VkLayerDbgFunctionNode *)loader_instance_heap_alloc(inst, sizeof(VkLayerDbgFunctionNode), 71 VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); 72 } 73 if (!pNewDbgFuncNode) { 74 return VK_ERROR_OUT_OF_HOST_MEMORY; 75 } 76 memset(pNewDbgFuncNode, 0, sizeof(VkLayerDbgFunctionNode)); 77 78 pNewDbgFuncNode->msgCallback = callback; 79 pNewDbgFuncNode->pfnMsgCallback = pCreateInfo->pfnCallback; 80 pNewDbgFuncNode->msgFlags = pCreateInfo->flags; 81 pNewDbgFuncNode->pUserData = pCreateInfo->pUserData; 82 pNewDbgFuncNode->pNext = inst->DbgFunctionHead; 83 inst->DbgFunctionHead = pNewDbgFuncNode; 84 85 return VK_SUCCESS; 86 } 87 88 static VKAPI_ATTR VkResult VKAPI_CALL 89 debug_report_CreateDebugReportCallbackEXT(VkInstance instance, const VkDebugReportCallbackCreateInfoEXT *pCreateInfo, 90 const VkAllocationCallbacks *pAllocator, VkDebugReportCallbackEXT *pCallback) { 91 struct loader_instance *inst = loader_get_instance(instance); 92 loader_platform_thread_lock_mutex(&loader_lock); 93 VkResult result = inst->disp->layer_inst_disp.CreateDebugReportCallbackEXT(instance, pCreateInfo, pAllocator, pCallback); 94 loader_platform_thread_unlock_mutex(&loader_lock); 95 return result; 96 } 97 98 // Utility function to handle reporting 99 VkBool32 util_DebugReportMessage(const struct loader_instance *inst, VkFlags msgFlags, VkDebugReportObjectTypeEXT objectType, 100 uint64_t srcObject, size_t location, int32_t msgCode, const char *pLayerPrefix, const char *pMsg) { 101 VkBool32 bail = false; 102 VkLayerDbgFunctionNode *pTrav = inst->DbgFunctionHead; 103 while (pTrav) { 104 if (pTrav->msgFlags & msgFlags) { 105 if (pTrav->pfnMsgCallback(msgFlags, objectType, srcObject, location, msgCode, pLayerPrefix, pMsg, pTrav->pUserData)) { 106 bail = true; 107 } 108 } 109 pTrav = pTrav->pNext; 110 } 111 112 return bail; 113 } 114 115 void util_DestroyDebugReportCallback(struct loader_instance *inst, VkDebugReportCallbackEXT callback, 116 const VkAllocationCallbacks *pAllocator) { 117 VkLayerDbgFunctionNode *pTrav = inst->DbgFunctionHead; 118 VkLayerDbgFunctionNode *pPrev = pTrav; 119 120 while (pTrav) { 121 if (pTrav->msgCallback == callback) { 122 pPrev->pNext = pTrav->pNext; 123 if (inst->DbgFunctionHead == pTrav) inst->DbgFunctionHead = pTrav->pNext; 124 #if (DEBUG_DISABLE_APP_ALLOCATORS == 1) 125 { 126 #else 127 if (pAllocator != NULL) { 128 pAllocator->pfnFree(pAllocator->pUserData, pTrav); 129 } else { 130 #endif 131 loader_instance_heap_free(inst, pTrav); 132 } 133 break; 134 } 135 pPrev = pTrav; 136 pTrav = pTrav->pNext; 137 } 138 } 139 140 // This utility (used by vkInstanceCreateInfo(), looks at a pNext chain. It 141 // counts any VkDebugReportCallbackCreateInfoEXT structs that it finds. It 142 // then allocates array that can hold that many structs, as well as that many 143 // VkDebugReportCallbackEXT handles. It then copies each 144 // VkDebugReportCallbackCreateInfoEXT, and initializes each handle. 145 VkResult util_CopyDebugReportCreateInfos(const void *pChain, const VkAllocationCallbacks *pAllocator, uint32_t *num_callbacks, 146 VkDebugReportCallbackCreateInfoEXT **infos, VkDebugReportCallbackEXT **callbacks) { 147 uint32_t n = *num_callbacks = 0; 148 VkDebugReportCallbackCreateInfoEXT *pInfos = NULL; 149 VkDebugReportCallbackEXT *pCallbacks = NULL; 150 151 // NOTE: The loader is not using pAllocator, and so this function doesn't 152 // either. 153 154 const void *pNext = pChain; 155 while (pNext) { 156 // 1st, count the number VkDebugReportCallbackCreateInfoEXT: 157 if (((VkDebugReportCallbackCreateInfoEXT *)pNext)->sType == VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT) { 158 n++; 159 } 160 pNext = (void *)((VkDebugReportCallbackCreateInfoEXT *)pNext)->pNext; 161 } 162 if (n == 0) { 163 return VK_SUCCESS; 164 } 165 166 // 2nd, allocate memory for each VkDebugReportCallbackCreateInfoEXT: 167 #if (DEBUG_DISABLE_APP_ALLOCATORS == 1) 168 { 169 #else 170 if (pAllocator != NULL) { 171 pInfos = *infos = ((VkDebugReportCallbackCreateInfoEXT *)pAllocator->pfnAllocation( 172 pAllocator->pUserData, n * sizeof(VkDebugReportCallbackCreateInfoEXT), sizeof(void *), 173 VK_SYSTEM_ALLOCATION_SCOPE_OBJECT)); 174 } else { 175 #endif 176 pInfos = *infos = ((VkDebugReportCallbackCreateInfoEXT *)malloc(n * sizeof(VkDebugReportCallbackCreateInfoEXT))); 177 } 178 if (!pInfos) { 179 return VK_ERROR_OUT_OF_HOST_MEMORY; 180 } 181 // 3rd, allocate memory for a unique handle for each callback: 182 #if (DEBUG_DISABLE_APP_ALLOCATORS == 1) 183 { 184 #else 185 if (pAllocator != NULL) { 186 pCallbacks = *callbacks = ((VkDebugReportCallbackEXT *)pAllocator->pfnAllocation( 187 pAllocator->pUserData, n * sizeof(VkDebugReportCallbackEXT), sizeof(void *), VK_SYSTEM_ALLOCATION_SCOPE_OBJECT)); 188 if (!pCallbacks) { 189 pAllocator->pfnFree(pAllocator->pUserData, pInfos); 190 return VK_ERROR_OUT_OF_HOST_MEMORY; 191 } 192 } else { 193 #endif 194 pCallbacks = *callbacks = ((VkDebugReportCallbackEXT *)malloc(n * sizeof(VkDebugReportCallbackEXT))); 195 if (!pCallbacks) { 196 free(pInfos); 197 return VK_ERROR_OUT_OF_HOST_MEMORY; 198 } 199 } 200 // 4th, copy each VkDebugReportCallbackCreateInfoEXT for use by 201 // vkDestroyInstance, and assign a unique handle to each callback (just 202 // use the address of the copied VkDebugReportCallbackCreateInfoEXT): 203 pNext = pChain; 204 while (pNext) { 205 if (((VkInstanceCreateInfo *)pNext)->sType == VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT) { 206 memcpy(pInfos, pNext, sizeof(VkDebugReportCallbackCreateInfoEXT)); 207 *pCallbacks++ = (VkDebugReportCallbackEXT)(uintptr_t)pInfos++; 208 } 209 pNext = (void *)((VkInstanceCreateInfo *)pNext)->pNext; 210 } 211 212 *num_callbacks = n; 213 return VK_SUCCESS; 214 } 215 216 void util_FreeDebugReportCreateInfos(const VkAllocationCallbacks *pAllocator, VkDebugReportCallbackCreateInfoEXT *infos, 217 VkDebugReportCallbackEXT *callbacks) { 218 #if (DEBUG_DISABLE_APP_ALLOCATORS == 1) 219 { 220 #else 221 if (pAllocator != NULL) { 222 pAllocator->pfnFree(pAllocator->pUserData, infos); 223 pAllocator->pfnFree(pAllocator->pUserData, callbacks); 224 } else { 225 #endif 226 free(infos); 227 free(callbacks); 228 } 229 } 230 231 VkResult util_CreateDebugReportCallbacks(struct loader_instance *inst, const VkAllocationCallbacks *pAllocator, 232 uint32_t num_callbacks, VkDebugReportCallbackCreateInfoEXT *infos, 233 VkDebugReportCallbackEXT *callbacks) { 234 VkResult rtn = VK_SUCCESS; 235 for (uint32_t i = 0; i < num_callbacks; i++) { 236 rtn = util_CreateDebugReportCallback(inst, &infos[i], pAllocator, callbacks[i]); 237 if (rtn != VK_SUCCESS) { 238 for (uint32_t j = 0; j < i; j++) { 239 util_DestroyDebugReportCallback(inst, callbacks[j], pAllocator); 240 } 241 return rtn; 242 } 243 } 244 return rtn; 245 } 246 247 void util_DestroyDebugReportCallbacks(struct loader_instance *inst, const VkAllocationCallbacks *pAllocator, uint32_t num_callbacks, 248 VkDebugReportCallbackEXT *callbacks) { 249 for (uint32_t i = 0; i < num_callbacks; i++) { 250 util_DestroyDebugReportCallback(inst, callbacks[i], pAllocator); 251 } 252 } 253 254 static VKAPI_ATTR void VKAPI_CALL debug_report_DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT callback, 255 const VkAllocationCallbacks *pAllocator) { 256 struct loader_instance *inst = loader_get_instance(instance); 257 loader_platform_thread_lock_mutex(&loader_lock); 258 259 inst->disp->layer_inst_disp.DestroyDebugReportCallbackEXT(instance, callback, pAllocator); 260 261 util_DestroyDebugReportCallback(inst, callback, pAllocator); 262 263 loader_platform_thread_unlock_mutex(&loader_lock); 264 } 265 266 static VKAPI_ATTR void VKAPI_CALL debug_report_DebugReportMessageEXT(VkInstance instance, VkDebugReportFlagsEXT flags, 267 VkDebugReportObjectTypeEXT objType, uint64_t object, 268 size_t location, int32_t msgCode, const char *pLayerPrefix, 269 const char *pMsg) { 270 struct loader_instance *inst = loader_get_instance(instance); 271 272 inst->disp->layer_inst_disp.DebugReportMessageEXT(instance, flags, objType, object, location, msgCode, pLayerPrefix, pMsg); 273 } 274 275 // This is the instance chain terminator function 276 // for CreateDebugReportCallback 277 VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateDebugReportCallbackEXT(VkInstance instance, 278 const VkDebugReportCallbackCreateInfoEXT *pCreateInfo, 279 const VkAllocationCallbacks *pAllocator, 280 VkDebugReportCallbackEXT *pCallback) { 281 VkDebugReportCallbackEXT *icd_info = NULL; 282 const struct loader_icd_term *icd_term; 283 struct loader_instance *inst = (struct loader_instance *)instance; 284 VkResult res = VK_SUCCESS; 285 uint32_t storage_idx; 286 VkLayerDbgFunctionNode *pNewDbgFuncNode = NULL; 287 288 #if (DEBUG_DISABLE_APP_ALLOCATORS == 1) 289 { 290 #else 291 if (pAllocator != NULL) { 292 icd_info = ((VkDebugReportCallbackEXT *)pAllocator->pfnAllocation(pAllocator->pUserData, 293 inst->total_icd_count * sizeof(VkDebugReportCallbackEXT), 294 sizeof(void *), VK_SYSTEM_ALLOCATION_SCOPE_OBJECT)); 295 if (icd_info) { 296 memset(icd_info, 0, inst->total_icd_count * sizeof(VkDebugReportCallbackEXT)); 297 } 298 } else { 299 #endif 300 icd_info = calloc(sizeof(VkDebugReportCallbackEXT), inst->total_icd_count); 301 } 302 if (!icd_info) { 303 res = VK_ERROR_OUT_OF_HOST_MEMORY; 304 goto out; 305 } 306 307 storage_idx = 0; 308 for (icd_term = inst->icd_terms; icd_term; icd_term = icd_term->next) { 309 if (!icd_term->dispatch.CreateDebugReportCallbackEXT) { 310 continue; 311 } 312 313 res = icd_term->dispatch.CreateDebugReportCallbackEXT(icd_term->instance, pCreateInfo, pAllocator, &icd_info[storage_idx]); 314 315 if (res != VK_SUCCESS) { 316 goto out; 317 } 318 storage_idx++; 319 } 320 321 // Setup the debug report callback in the terminator since a layer may want 322 // to grab the information itself (RenderDoc) and then return back to the 323 // user callback a sub-set of the messages. 324 #if (DEBUG_DISABLE_APP_ALLOCATORS == 0) 325 if (pAllocator != NULL) { 326 pNewDbgFuncNode = (VkLayerDbgFunctionNode *)pAllocator->pfnAllocation(pAllocator->pUserData, sizeof(VkLayerDbgFunctionNode), 327 sizeof(int *), VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); 328 } else { 329 #else 330 { 331 #endif 332 pNewDbgFuncNode = (VkLayerDbgFunctionNode *)loader_instance_heap_alloc(inst, sizeof(VkLayerDbgFunctionNode), 333 VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); 334 } 335 if (!pNewDbgFuncNode) { 336 res = VK_ERROR_OUT_OF_HOST_MEMORY; 337 goto out; 338 } 339 memset(pNewDbgFuncNode, 0, sizeof(VkLayerDbgFunctionNode)); 340 341 pNewDbgFuncNode->pfnMsgCallback = pCreateInfo->pfnCallback; 342 pNewDbgFuncNode->msgFlags = pCreateInfo->flags; 343 pNewDbgFuncNode->pUserData = pCreateInfo->pUserData; 344 pNewDbgFuncNode->pNext = inst->DbgFunctionHead; 345 inst->DbgFunctionHead = pNewDbgFuncNode; 346 347 *(VkDebugReportCallbackEXT **)pCallback = icd_info; 348 pNewDbgFuncNode->msgCallback = *pCallback; 349 350 out: 351 352 // Roll back on errors 353 if (VK_SUCCESS != res) { 354 storage_idx = 0; 355 for (icd_term = inst->icd_terms; icd_term; icd_term = icd_term->next) { 356 if (NULL == icd_term->dispatch.DestroyDebugReportCallbackEXT) { 357 continue; 358 } 359 360 if (icd_info && icd_info[storage_idx]) { 361 icd_term->dispatch.DestroyDebugReportCallbackEXT(icd_term->instance, icd_info[storage_idx], pAllocator); 362 } 363 storage_idx++; 364 } 365 366 #if (DEBUG_DISABLE_APP_ALLOCATORS == 1) 367 { 368 #else 369 if (pAllocator != NULL) { 370 if (NULL != pNewDbgFuncNode) { 371 pAllocator->pfnFree(pAllocator->pUserData, pNewDbgFuncNode); 372 } 373 if (NULL != icd_info) { 374 pAllocator->pfnFree(pAllocator->pUserData, icd_info); 375 } 376 } else { 377 #endif 378 if (NULL != pNewDbgFuncNode) { 379 free(pNewDbgFuncNode); 380 } 381 if (NULL != icd_info) { 382 free(icd_info); 383 } 384 } 385 } 386 387 return res; 388 } 389 390 // This is the instance chain terminator function for DestroyDebugReportCallback 391 VKAPI_ATTR void VKAPI_CALL terminator_DestroyDebugReportCallbackEXT(VkInstance instance, VkDebugReportCallbackEXT callback, 392 const VkAllocationCallbacks *pAllocator) { 393 uint32_t storage_idx; 394 VkDebugReportCallbackEXT *icd_info; 395 const struct loader_icd_term *icd_term; 396 397 struct loader_instance *inst = (struct loader_instance *)instance; 398 icd_info = *(VkDebugReportCallbackEXT **)&callback; 399 storage_idx = 0; 400 for (icd_term = inst->icd_terms; icd_term; icd_term = icd_term->next) { 401 if (NULL == icd_term->dispatch.DestroyDebugReportCallbackEXT) { 402 continue; 403 } 404 405 if (icd_info[storage_idx]) { 406 icd_term->dispatch.DestroyDebugReportCallbackEXT(icd_term->instance, icd_info[storage_idx], pAllocator); 407 } 408 storage_idx++; 409 } 410 411 #if (DEBUG_DISABLE_APP_ALLOCATORS == 1) 412 { 413 #else 414 if (pAllocator != NULL) { 415 pAllocator->pfnFree(pAllocator->pUserData, icd_info); 416 } else { 417 #endif 418 free(icd_info); 419 } 420 } 421 422 // This is the instance chain terminator function for DebugReportMessage 423 VKAPI_ATTR void VKAPI_CALL terminator_DebugReportMessageEXT(VkInstance instance, VkDebugReportFlagsEXT flags, 424 VkDebugReportObjectTypeEXT objType, uint64_t object, size_t location, 425 int32_t msgCode, const char *pLayerPrefix, const char *pMsg) { 426 const struct loader_icd_term *icd_term; 427 428 struct loader_instance *inst = (struct loader_instance *)instance; 429 430 loader_platform_thread_lock_mutex(&loader_lock); 431 for (icd_term = inst->icd_terms; icd_term; icd_term = icd_term->next) { 432 if (icd_term->dispatch.DebugReportMessageEXT != NULL) { 433 icd_term->dispatch.DebugReportMessageEXT(icd_term->instance, flags, objType, object, location, msgCode, pLayerPrefix, 434 pMsg); 435 } 436 } 437 438 // Now that all ICDs have seen the message, call the necessary callbacks. Ignoring "bail" return value 439 // as there is nothing to bail from at this point. 440 441 util_DebugReportMessage(inst, flags, objType, object, location, msgCode, pLayerPrefix, pMsg); 442 443 loader_platform_thread_unlock_mutex(&loader_lock); 444 } 445 446 bool debug_report_instance_gpa(struct loader_instance *ptr_instance, const char *name, void **addr) { 447 // debug_report is currently advertised to be supported by the loader, 448 // so always return the entry points if name matches and it's enabled 449 *addr = NULL; 450 451 if (!strcmp("vkCreateDebugReportCallbackEXT", name)) { 452 *addr = (ptr_instance->enabled_known_extensions.ext_debug_report == 1) ? (void *)debug_report_CreateDebugReportCallbackEXT 453 : NULL; 454 return true; 455 } 456 if (!strcmp("vkDestroyDebugReportCallbackEXT", name)) { 457 *addr = (ptr_instance->enabled_known_extensions.ext_debug_report == 1) ? (void *)debug_report_DestroyDebugReportCallbackEXT 458 : NULL; 459 return true; 460 } 461 if (!strcmp("vkDebugReportMessageEXT", name)) { 462 *addr = (ptr_instance->enabled_known_extensions.ext_debug_report == 1) ? (void *)debug_report_DebugReportMessageEXT : NULL; 463 return true; 464 } 465 return false; 466 } 467