Home | History | Annotate | Download | only in layers
      1 /* Copyright (c) 2015-2016 The Khronos Group Inc.
      2  * Copyright (c) 2015-2016 Valve Corporation
      3  * Copyright (c) 2015-2016 LunarG, Inc.
      4  *
      5  * Licensed under the Apache License, Version 2.0 (the "License");
      6  * you may not use this file except in compliance with the License.
      7  * You may obtain a copy of the License at
      8  *
      9  *     http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  *
     17  * Author: Courtney Goeltzenleuchter <courtney (at) LunarG.com>
     18  * Author: Tobin Ehlis <tobin (at) lunarg.com>
     19  *
     20  */
     21 
     22 #ifndef LAYER_LOGGING_H
     23 #define LAYER_LOGGING_H
     24 
     25 #include "vk_loader_layer.h"
     26 #include "vk_layer_config.h"
     27 #include "vk_layer_data.h"
     28 #include "vk_layer_table.h"
     29 #include "vk_loader_platform.h"
     30 #include "vulkan/vk_layer.h"
     31 #include <cinttypes>
     32 #include <stdarg.h>
     33 #include <stdbool.h>
     34 #include <stdio.h>
     35 #include <unordered_map>
     36 #include <vector>
     37 
     38 typedef struct _debug_report_data {
     39     VkLayerDbgFunctionNode *debug_callback_list;
     40     VkLayerDbgFunctionNode *default_debug_callback_list;
     41     VkFlags active_flags;
     42     bool g_DEBUG_REPORT;
     43 } debug_report_data;
     44 
     45 template debug_report_data *get_my_data_ptr<debug_report_data>(void *data_key,
     46                                                                std::unordered_map<void *, debug_report_data *> &data_map);
     47 
     48 // Forward Declarations
     49 static inline bool debug_report_log_msg(const debug_report_data *debug_data, VkFlags msgFlags,
     50                                         VkDebugReportObjectTypeEXT objectType, uint64_t srcObject, size_t location, int32_t msgCode,
     51                                         const char *pLayerPrefix, const char *pMsg);
     52 
     53 // Add a debug message callback node structure to the specified callback linked list
     54 static inline void AddDebugMessageCallback(debug_report_data *debug_data, VkLayerDbgFunctionNode **list_head,
     55                                            VkLayerDbgFunctionNode *new_node) {
     56 
     57     new_node->pNext = *list_head;
     58     *list_head = new_node;
     59 }
     60 
     61 // Remove specified debug message callback node structure from the specified callback linked list
     62 static inline void RemoveDebugMessageCallback(debug_report_data *debug_data, VkLayerDbgFunctionNode **list_head,
     63                                               VkDebugReportCallbackEXT callback) {
     64     VkLayerDbgFunctionNode *cur_callback = *list_head;
     65     VkLayerDbgFunctionNode *prev_callback = cur_callback;
     66     bool matched = false;
     67 
     68     debug_data->active_flags = 0;
     69     while (cur_callback) {
     70         if (cur_callback->msgCallback == callback) {
     71             matched = true;
     72             prev_callback->pNext = cur_callback->pNext;
     73             if (*list_head == cur_callback) {
     74                 *list_head = cur_callback->pNext;
     75             }
     76             debug_report_log_msg(debug_data, VK_DEBUG_REPORT_DEBUG_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT,
     77                                  reinterpret_cast<uint64_t &>(cur_callback->msgCallback), 0, VK_DEBUG_REPORT_ERROR_CALLBACK_REF_EXT,
     78                                  "DebugReport", "Destroyed callback");
     79         } else {
     80             matched = false;
     81             debug_data->active_flags |= cur_callback->msgFlags;
     82         }
     83         prev_callback = cur_callback;
     84         cur_callback = cur_callback->pNext;
     85         if (matched) {
     86             free(prev_callback);
     87         }
     88     }
     89 }
     90 
     91 // Removes all debug callback function nodes from the specified callback linked lists and frees their resources
     92 static inline void RemoveAllMessageCallbacks(debug_report_data *debug_data, VkLayerDbgFunctionNode **list_head) {
     93     VkLayerDbgFunctionNode *current_callback = *list_head;
     94     VkLayerDbgFunctionNode *prev_callback = current_callback;
     95 
     96     while (current_callback) {
     97         prev_callback = current_callback->pNext;
     98         debug_report_log_msg(debug_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT,
     99                              (uint64_t)current_callback->msgCallback, 0, VK_DEBUG_REPORT_ERROR_CALLBACK_REF_EXT, "DebugReport",
    100                              "Debug Report callbacks not removed before DestroyInstance");
    101         free(current_callback);
    102         current_callback = prev_callback;
    103     }
    104     *list_head = NULL;
    105 }
    106 
    107 // Utility function to handle reporting
    108 static inline bool debug_report_log_msg(const debug_report_data *debug_data, VkFlags msgFlags,
    109                                         VkDebugReportObjectTypeEXT objectType, uint64_t srcObject, size_t location, int32_t msgCode,
    110                                         const char *pLayerPrefix, const char *pMsg) {
    111     bool bail = false;
    112     VkLayerDbgFunctionNode *pTrav = NULL;
    113 
    114     if (debug_data->debug_callback_list != NULL) {
    115         pTrav = debug_data->debug_callback_list;
    116     } else {
    117         pTrav = debug_data->default_debug_callback_list;
    118     }
    119 
    120     while (pTrav) {
    121         if (pTrav->msgFlags & msgFlags) {
    122             if (pTrav->pfnMsgCallback(msgFlags, objectType, srcObject, location, msgCode, pLayerPrefix, pMsg, pTrav->pUserData)) {
    123                 bail = true;
    124             }
    125         }
    126         pTrav = pTrav->pNext;
    127     }
    128 
    129     return bail;
    130 }
    131 
    132 static inline debug_report_data *
    133 debug_report_create_instance(VkLayerInstanceDispatchTable *table, VkInstance inst, uint32_t extension_count,
    134                              const char *const *ppEnabledExtensions) // layer or extension name to be enabled
    135 {
    136     debug_report_data *debug_data = (debug_report_data *)malloc(sizeof(debug_report_data));
    137     if (!debug_data)
    138         return NULL;
    139 
    140     memset(debug_data, 0, sizeof(debug_report_data));
    141     for (uint32_t i = 0; i < extension_count; i++) {
    142         // TODO: Check other property fields
    143         if (strcmp(ppEnabledExtensions[i], VK_EXT_DEBUG_REPORT_EXTENSION_NAME) == 0) {
    144             debug_data->g_DEBUG_REPORT = true;
    145         }
    146     }
    147     return debug_data;
    148 }
    149 
    150 static inline void layer_debug_report_destroy_instance(debug_report_data *debug_data) {
    151     if (debug_data) {
    152         RemoveAllMessageCallbacks(debug_data, &debug_data->default_debug_callback_list);
    153         RemoveAllMessageCallbacks(debug_data, &debug_data->debug_callback_list);
    154         free(debug_data);
    155     }
    156 }
    157 
    158 static inline debug_report_data *layer_debug_report_create_device(debug_report_data *instance_debug_data, VkDevice device) {
    159     // DEBUG_REPORT shares data between Instance and Device,
    160     // so just return instance's data pointer
    161     return instance_debug_data;
    162 }
    163 
    164 static inline void layer_debug_report_destroy_device(VkDevice device) {
    165     // Nothing to do since we're using instance data record
    166 }
    167 
    168 static inline void layer_destroy_msg_callback(debug_report_data *debug_data, VkDebugReportCallbackEXT callback,
    169                                               const VkAllocationCallbacks *pAllocator) {
    170     RemoveDebugMessageCallback(debug_data, &debug_data->debug_callback_list, callback);
    171     RemoveDebugMessageCallback(debug_data, &debug_data->default_debug_callback_list, callback);
    172 }
    173 
    174 static inline VkResult layer_create_msg_callback(debug_report_data *debug_data, bool default_callback,
    175                                                  const VkDebugReportCallbackCreateInfoEXT *pCreateInfo,
    176                                                  const VkAllocationCallbacks *pAllocator, VkDebugReportCallbackEXT *pCallback) {
    177     VkLayerDbgFunctionNode *pNewDbgFuncNode = (VkLayerDbgFunctionNode *)malloc(sizeof(VkLayerDbgFunctionNode));
    178     if (!pNewDbgFuncNode)
    179         return VK_ERROR_OUT_OF_HOST_MEMORY;
    180 
    181     // Handle of 0 is logging_callback so use allocated Node address as unique handle
    182     if (!(*pCallback))
    183         *pCallback = (VkDebugReportCallbackEXT)pNewDbgFuncNode;
    184     pNewDbgFuncNode->msgCallback = *pCallback;
    185     pNewDbgFuncNode->pfnMsgCallback = pCreateInfo->pfnCallback;
    186     pNewDbgFuncNode->msgFlags = pCreateInfo->flags;
    187     pNewDbgFuncNode->pUserData = pCreateInfo->pUserData;
    188 
    189     if (default_callback) {
    190         AddDebugMessageCallback(debug_data, &debug_data->default_debug_callback_list, pNewDbgFuncNode);
    191     } else {
    192         AddDebugMessageCallback(debug_data, &debug_data->debug_callback_list, pNewDbgFuncNode);
    193     }
    194     debug_data->active_flags |= pCreateInfo->flags;
    195 
    196     debug_report_log_msg(debug_data, VK_DEBUG_REPORT_DEBUG_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT,
    197                          (uint64_t)*pCallback, 0, VK_DEBUG_REPORT_ERROR_CALLBACK_REF_EXT, "DebugReport", "Added callback");
    198     return VK_SUCCESS;
    199 }
    200 
    201 static inline PFN_vkVoidFunction debug_report_get_instance_proc_addr(debug_report_data *debug_data, const char *funcName) {
    202     if (!debug_data || !debug_data->g_DEBUG_REPORT) {
    203         return NULL;
    204     }
    205 
    206     if (!strcmp(funcName, "vkCreateDebugReportCallbackEXT")) {
    207         return (PFN_vkVoidFunction)vkCreateDebugReportCallbackEXT;
    208     }
    209     if (!strcmp(funcName, "vkDestroyDebugReportCallbackEXT")) {
    210         return (PFN_vkVoidFunction)vkDestroyDebugReportCallbackEXT;
    211     }
    212     if (!strcmp(funcName, "vkDebugReportMessageEXT")) {
    213         return (PFN_vkVoidFunction)vkDebugReportMessageEXT;
    214     }
    215     return NULL;
    216 }
    217 
    218 // This utility (called at vkCreateInstance() time), looks at a pNext chain.
    219 // It counts any VkDebugReportCallbackCreateInfoEXT structs that it finds.  It
    220 // then allocates an array that can hold that many structs, as well as that
    221 // many VkDebugReportCallbackEXT handles.  It then copies each
    222 // VkDebugReportCallbackCreateInfoEXT, and initializes each handle.
    223 static VkResult layer_copy_tmp_callbacks(const void *pChain, uint32_t *num_callbacks, VkDebugReportCallbackCreateInfoEXT **infos,
    224                                          VkDebugReportCallbackEXT **callbacks) {
    225     uint32_t n = *num_callbacks = 0;
    226 
    227     const void *pNext = pChain;
    228     while (pNext) {
    229         // 1st, count the number VkDebugReportCallbackCreateInfoEXT:
    230         if (((VkDebugReportCallbackCreateInfoEXT *)pNext)->sType == VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT) {
    231             n++;
    232         }
    233         pNext = (void *)((VkDebugReportCallbackCreateInfoEXT *)pNext)->pNext;
    234     }
    235     if (n == 0) {
    236         return VK_SUCCESS;
    237     }
    238 
    239     // 2nd, allocate memory for each VkDebugReportCallbackCreateInfoEXT:
    240     VkDebugReportCallbackCreateInfoEXT *pInfos = *infos =
    241         ((VkDebugReportCallbackCreateInfoEXT *)malloc(n * sizeof(VkDebugReportCallbackCreateInfoEXT)));
    242     if (!pInfos) {
    243         return VK_ERROR_OUT_OF_HOST_MEMORY;
    244     }
    245     // 3rd, allocate memory for a unique handle for each callback:
    246     VkDebugReportCallbackEXT *pCallbacks = *callbacks = ((VkDebugReportCallbackEXT *)malloc(n * sizeof(VkDebugReportCallbackEXT)));
    247     if (!pCallbacks) {
    248         free(pInfos);
    249         return VK_ERROR_OUT_OF_HOST_MEMORY;
    250     }
    251     // 4th, copy each VkDebugReportCallbackCreateInfoEXT for use by
    252     // vkDestroyInstance, and assign a unique handle to each callback (just
    253     // use the address of the copied VkDebugReportCallbackCreateInfoEXT):
    254     pNext = pChain;
    255     while (pNext) {
    256         if (((VkInstanceCreateInfo *)pNext)->sType == VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT) {
    257             memcpy(pInfos, pNext, sizeof(VkDebugReportCallbackCreateInfoEXT));
    258             *pCallbacks++ = (VkDebugReportCallbackEXT)pInfos++;
    259         }
    260         pNext = (void *)((VkInstanceCreateInfo *)pNext)->pNext;
    261     }
    262 
    263     *num_callbacks = n;
    264     return VK_SUCCESS;
    265 }
    266 
    267 // This utility frees the arrays allocated by layer_copy_tmp_callbacks()
    268 static void layer_free_tmp_callbacks(VkDebugReportCallbackCreateInfoEXT *infos, VkDebugReportCallbackEXT *callbacks) {
    269     free(infos);
    270     free(callbacks);
    271 }
    272 
    273 // This utility enables all of the VkDebugReportCallbackCreateInfoEXT structs
    274 // that were copied by layer_copy_tmp_callbacks()
    275 static VkResult layer_enable_tmp_callbacks(debug_report_data *debug_data, uint32_t num_callbacks,
    276                                            VkDebugReportCallbackCreateInfoEXT *infos, VkDebugReportCallbackEXT *callbacks) {
    277     VkResult rtn = VK_SUCCESS;
    278     for (uint32_t i = 0; i < num_callbacks; i++) {
    279         rtn = layer_create_msg_callback(debug_data, false, &infos[i], NULL, &callbacks[i]);
    280         if (rtn != VK_SUCCESS) {
    281             for (uint32_t j = 0; j < i; j++) {
    282                 layer_destroy_msg_callback(debug_data, callbacks[j], NULL);
    283             }
    284             return rtn;
    285         }
    286     }
    287     return rtn;
    288 }
    289 
    290 // This utility disables all of the VkDebugReportCallbackCreateInfoEXT structs
    291 // that were copied by layer_copy_tmp_callbacks()
    292 static void layer_disable_tmp_callbacks(debug_report_data *debug_data, uint32_t num_callbacks,
    293                                         VkDebugReportCallbackEXT *callbacks) {
    294     for (uint32_t i = 0; i < num_callbacks; i++) {
    295         layer_destroy_msg_callback(debug_data, callbacks[i], NULL);
    296     }
    297 }
    298 
    299 // Checks if the message will get logged.
    300 // Allows layer to defer collecting & formating data if the
    301 // message will be discarded.
    302 static inline bool will_log_msg(debug_report_data *debug_data, VkFlags msgFlags) {
    303     if (!debug_data || !(debug_data->active_flags & msgFlags)) {
    304         // Message is not wanted
    305         return false;
    306     }
    307 
    308     return true;
    309 }
    310 
    311 #ifdef WIN32
    312 static inline int vasprintf(char **strp, char const *fmt, va_list ap) {
    313     *strp = nullptr;
    314     int size = _vscprintf(fmt, ap);
    315     if (size >= 0) {
    316         *strp = (char *)malloc(size+1);
    317         if (!*strp) {
    318             return -1;
    319         }
    320         _vsnprintf(*strp, size+1, fmt, ap);
    321     }
    322     return size;
    323 }
    324 #endif
    325 
    326 // Output log message via DEBUG_REPORT
    327 // Takes format and variable arg list so that output string
    328 // is only computed if a message needs to be logged
    329 #ifndef WIN32
    330 static inline bool log_msg(const debug_report_data *debug_data, VkFlags msgFlags, VkDebugReportObjectTypeEXT objectType,
    331                            uint64_t srcObject, size_t location, int32_t msgCode, const char *pLayerPrefix, const char *format, ...)
    332     __attribute__((format(printf, 8, 9)));
    333 #endif
    334 static inline bool log_msg(const debug_report_data *debug_data, VkFlags msgFlags, VkDebugReportObjectTypeEXT objectType,
    335                            uint64_t srcObject, size_t location, int32_t msgCode, const char *pLayerPrefix, const char *format,
    336                            ...) {
    337     if (!debug_data || !(debug_data->active_flags & msgFlags)) {
    338         // Message is not wanted
    339         return false;
    340     }
    341 
    342     va_list argptr;
    343     va_start(argptr, format);
    344     char *str;
    345     if (-1 == vasprintf(&str, format, argptr)) {
    346         // On failure, glibc vasprintf leaves str undefined
    347         str = nullptr;
    348     }
    349     va_end(argptr);
    350     bool result = debug_report_log_msg(debug_data, msgFlags, objectType, srcObject, location, msgCode, pLayerPrefix,
    351                                        str ? str : "Allocation failure");
    352     free(str);
    353     return result;
    354 }
    355 
    356 static inline VKAPI_ATTR VkBool32 VKAPI_CALL log_callback(VkFlags msgFlags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject,
    357                                                           size_t location, int32_t msgCode, const char *pLayerPrefix,
    358                                                           const char *pMsg, void *pUserData) {
    359     char msg_flags[30];
    360 
    361     print_msg_flags(msgFlags, msg_flags);
    362 
    363     fprintf((FILE *)pUserData, "%s(%s): object: 0x%" PRIx64 " type: %d location: %lu msgCode: %d: %s\n", pLayerPrefix, msg_flags,
    364             srcObject, objType, (unsigned long)location, msgCode, pMsg);
    365     fflush((FILE *)pUserData);
    366 
    367     return false;
    368 }
    369 
    370 static inline VKAPI_ATTR VkBool32 VKAPI_CALL win32_debug_output_msg(VkFlags msgFlags, VkDebugReportObjectTypeEXT objType,
    371                                                                     uint64_t srcObject, size_t location, int32_t msgCode,
    372                                                                     const char *pLayerPrefix, const char *pMsg, void *pUserData) {
    373 #ifdef WIN32
    374     char msg_flags[30];
    375     char buf[2048];
    376 
    377     print_msg_flags(msgFlags, msg_flags);
    378     _snprintf(buf, sizeof(buf) - 1,
    379               "%s (%s): object: 0x%" PRIxPTR " type: %d location: " PRINTF_SIZE_T_SPECIFIER " msgCode: %d: %s\n", pLayerPrefix,
    380               msg_flags, (size_t)srcObject, objType, location, msgCode, pMsg);
    381 
    382     OutputDebugString(buf);
    383 #endif
    384 
    385     return false;
    386 }
    387 
    388 #endif // LAYER_LOGGING_H
    389