Home | History | Annotate | Download | only in loader
      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