debug_report.c revision cc7c305c82f6443c324165edb7af59f60fc87eeb
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@LunarG.com> 20 * Author: Jon Ashburn <jon@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 37typedef void(VKAPI_PTR *PFN_stringCallback)(char *message); 38 39static const VkExtensionProperties debug_report_extension_info = { 40 .extensionName = VK_EXT_DEBUG_REPORT_EXTENSION_NAME, .specVersion = VK_EXT_DEBUG_REPORT_SPEC_VERSION, 41}; 42 43void 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 47void 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 58VkResult 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 88static VKAPI_ATTR VkResult VKAPI_CALL 89debug_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 99VkBool32 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 115void 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. 145VkResult 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 216void 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 231VkResult 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 247void 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 254static 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 266static 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/* 276 * This is the instance chain terminator function 277 * for CreateDebugReportCallback 278 */ 279 280VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateDebugReportCallback(VkInstance instance, 281 const VkDebugReportCallbackCreateInfoEXT *pCreateInfo, 282 const VkAllocationCallbacks *pAllocator, 283 VkDebugReportCallbackEXT *pCallback) { 284 VkDebugReportCallbackEXT *icd_info = NULL; 285 const struct loader_icd_term *icd_term; 286 struct loader_instance *inst = (struct loader_instance *)instance; 287 VkResult res = VK_SUCCESS; 288 uint32_t storage_idx; 289 VkLayerDbgFunctionNode *pNewDbgFuncNode = NULL; 290 291#if (DEBUG_DISABLE_APP_ALLOCATORS == 1) 292 { 293#else 294 if (pAllocator != NULL) { 295 icd_info = ((VkDebugReportCallbackEXT *)pAllocator->pfnAllocation(pAllocator->pUserData, 296 inst->total_icd_count * sizeof(VkDebugReportCallbackEXT), 297 sizeof(void *), VK_SYSTEM_ALLOCATION_SCOPE_OBJECT)); 298 if (icd_info) { 299 memset(icd_info, 0, inst->total_icd_count * sizeof(VkDebugReportCallbackEXT)); 300 } 301 } else { 302#endif 303 icd_info = calloc(sizeof(VkDebugReportCallbackEXT), inst->total_icd_count); 304 } 305 if (!icd_info) { 306 res = VK_ERROR_OUT_OF_HOST_MEMORY; 307 goto out; 308 } 309 310 storage_idx = 0; 311 for (icd_term = inst->icd_terms; icd_term; icd_term = icd_term->next) { 312 if (!icd_term->CreateDebugReportCallbackEXT) { 313 continue; 314 } 315 316 res = icd_term->CreateDebugReportCallbackEXT(icd_term->instance, pCreateInfo, pAllocator, &icd_info[storage_idx]); 317 318 if (res != VK_SUCCESS) { 319 goto out; 320 } 321 storage_idx++; 322 } 323 324// Setup the debug report callback in the terminator since a layer may want 325// to grab the information itself (RenderDoc) and then return back to the 326// user callback a sub-set of the messages. 327#if (DEBUG_DISABLE_APP_ALLOCATORS == 0) 328 if (pAllocator != NULL) { 329 pNewDbgFuncNode = (VkLayerDbgFunctionNode *)pAllocator->pfnAllocation(pAllocator->pUserData, sizeof(VkLayerDbgFunctionNode), 330 sizeof(int *), VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); 331 } else { 332#else 333 { 334#endif 335 pNewDbgFuncNode = (VkLayerDbgFunctionNode *)loader_instance_heap_alloc(inst, sizeof(VkLayerDbgFunctionNode), 336 VK_SYSTEM_ALLOCATION_SCOPE_OBJECT); 337 } 338 if (!pNewDbgFuncNode) { 339 res = VK_ERROR_OUT_OF_HOST_MEMORY; 340 goto out; 341 } 342 memset(pNewDbgFuncNode, 0, sizeof(VkLayerDbgFunctionNode)); 343 344 pNewDbgFuncNode->pfnMsgCallback = pCreateInfo->pfnCallback; 345 pNewDbgFuncNode->msgFlags = pCreateInfo->flags; 346 pNewDbgFuncNode->pUserData = pCreateInfo->pUserData; 347 pNewDbgFuncNode->pNext = inst->DbgFunctionHead; 348 inst->DbgFunctionHead = pNewDbgFuncNode; 349 350 *(VkDebugReportCallbackEXT **)pCallback = icd_info; 351 pNewDbgFuncNode->msgCallback = *pCallback; 352 353out: 354 355 // Roll back on errors 356 if (VK_SUCCESS != res) { 357 storage_idx = 0; 358 for (icd_term = inst->icd_terms; icd_term; icd_term = icd_term->next) { 359 if (NULL == icd_term->DestroyDebugReportCallbackEXT) { 360 continue; 361 } 362 363 if (icd_info && icd_info[storage_idx]) { 364 icd_term->DestroyDebugReportCallbackEXT(icd_term->instance, icd_info[storage_idx], pAllocator); 365 } 366 storage_idx++; 367 } 368 369#if (DEBUG_DISABLE_APP_ALLOCATORS == 1) 370 { 371#else 372 if (pAllocator != NULL) { 373 if (NULL != pNewDbgFuncNode) { 374 pAllocator->pfnFree(pAllocator->pUserData, pNewDbgFuncNode); 375 } 376 if (NULL != icd_info) { 377 pAllocator->pfnFree(pAllocator->pUserData, icd_info); 378 } 379 } else { 380#endif 381 if (NULL != pNewDbgFuncNode) { 382 free(pNewDbgFuncNode); 383 } 384 if (NULL != icd_info) { 385 free(icd_info); 386 } 387 } 388 } 389 390 return res; 391} 392 393/* 394 * This is the instance chain terminator function 395 * for DestroyDebugReportCallback 396 */ 397VKAPI_ATTR void VKAPI_CALL terminator_DestroyDebugReportCallback(VkInstance instance, VkDebugReportCallbackEXT callback, 398 const VkAllocationCallbacks *pAllocator) { 399 uint32_t storage_idx; 400 VkDebugReportCallbackEXT *icd_info; 401 const struct loader_icd_term *icd_term; 402 403 struct loader_instance *inst = (struct loader_instance *)instance; 404 icd_info = *(VkDebugReportCallbackEXT **)&callback; 405 storage_idx = 0; 406 for (icd_term = inst->icd_terms; icd_term; icd_term = icd_term->next) { 407 if (NULL == icd_term->DestroyDebugReportCallbackEXT) { 408 continue; 409 } 410 411 if (icd_info[storage_idx]) { 412 icd_term->DestroyDebugReportCallbackEXT(icd_term->instance, icd_info[storage_idx], pAllocator); 413 } 414 storage_idx++; 415 } 416 417#if (DEBUG_DISABLE_APP_ALLOCATORS == 1) 418 { 419#else 420 if (pAllocator != NULL) { 421 pAllocator->pfnFree(pAllocator->pUserData, icd_info); 422 } else { 423#endif 424 free(icd_info); 425 } 426} 427 428/* 429 * This is the instance chain terminator function 430 * for DebugReportMessage 431 */ 432VKAPI_ATTR void VKAPI_CALL terminator_DebugReportMessage(VkInstance instance, VkDebugReportFlagsEXT flags, 433 VkDebugReportObjectTypeEXT objType, uint64_t object, size_t location, 434 int32_t msgCode, const char *pLayerPrefix, const char *pMsg) { 435 const struct loader_icd_term *icd_term; 436 437 struct loader_instance *inst = (struct loader_instance *)instance; 438 439 loader_platform_thread_lock_mutex(&loader_lock); 440 for (icd_term = inst->icd_terms; icd_term; icd_term = icd_term->next) { 441 if (icd_term->DebugReportMessageEXT != NULL) { 442 icd_term->DebugReportMessageEXT(icd_term->instance, flags, objType, object, location, msgCode, pLayerPrefix, pMsg); 443 } 444 } 445 446 /* 447 * Now that all ICDs have seen the message, call the necessary callbacks. 448 * Ignoring "bail" return value as there is nothing to bail from at this 449 * point. 450 */ 451 452 util_DebugReportMessage(inst, flags, objType, object, location, msgCode, pLayerPrefix, pMsg); 453 454 loader_platform_thread_unlock_mutex(&loader_lock); 455} 456 457bool debug_report_instance_gpa(struct loader_instance *ptr_instance, const char *name, void **addr) { 458 // debug_report is currently advertised to be supported by the loader, 459 // so always return the entry points if name matches and it's enabled 460 *addr = NULL; 461 462 if (!strcmp("vkCreateDebugReportCallbackEXT", name)) { 463 *addr = (ptr_instance->enabled_known_extensions.ext_debug_report == 1) ? (void *)debug_report_CreateDebugReportCallbackEXT 464 : NULL; 465 return true; 466 } 467 if (!strcmp("vkDestroyDebugReportCallbackEXT", name)) { 468 *addr = (ptr_instance->enabled_known_extensions.ext_debug_report == 1) ? (void *)debug_report_DestroyDebugReportCallbackEXT 469 : NULL; 470 return true; 471 } 472 if (!strcmp("vkDebugReportMessageEXT", name)) { 473 *addr = (ptr_instance->enabled_known_extensions.ext_debug_report == 1) ? (void *)debug_report_DebugReportMessageEXT : NULL; 474 return true; 475 } 476 return false; 477} 478