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