vk_layer_logging.h revision 491a3cd11793892b996a8b5771479cc539198f99
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 * Permission is hereby granted, free of charge, to any person obtaining a copy
6 * of this software and/or associated documentation files (the "Materials"), to
7 * deal in the Materials without restriction, including without limitation the
8 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
9 * sell copies of the Materials, and to permit persons to whom the Materials
10 * are furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice(s) and this permission notice shall be included
13 * in all copies or substantial portions of the Materials.
14 *
15 * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18 *
19 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
20 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
21 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE
22 * USE OR OTHER DEALINGS IN THE MATERIALS
23 *
24 * Author: Courtney Goeltzenleuchter <courtney@LunarG.com>
25 * Author: Tobin Ehlis <tobin@lunarg.com>
26 *
27 */
28
29#ifndef LAYER_LOGGING_H
30#define LAYER_LOGGING_H
31
32#include <stdio.h>
33#include <stdarg.h>
34#include <stdbool.h>
35#include <unordered_map>
36#include <inttypes.h>
37#include "vk_loader_platform.h"
38#include "vulkan/vk_layer.h"
39#include "vk_layer_data.h"
40#include "vk_layer_table.h"
41
42typedef struct _debug_report_data {
43    VkLayerDbgFunctionNode *g_pDbgFunctionHead;
44    VkFlags active_flags;
45    bool g_DEBUG_REPORT;
46} debug_report_data;
47
48template debug_report_data *get_my_data_ptr<debug_report_data>(void *data_key,
49                                                               std::unordered_map<void *, debug_report_data *> &data_map);
50
51// Utility function to handle reporting
52static inline VkBool32 debug_report_log_msg(debug_report_data *debug_data, VkFlags msgFlags, VkDebugReportObjectTypeEXT objectType,
53                                            uint64_t srcObject, size_t location, int32_t msgCode, const char *pLayerPrefix,
54                                            const char *pMsg) {
55    VkBool32 bail = false;
56    VkLayerDbgFunctionNode *pTrav = debug_data->g_pDbgFunctionHead;
57    while (pTrav) {
58        if (pTrav->msgFlags & msgFlags) {
59            if (pTrav->pfnMsgCallback(msgFlags, objectType, srcObject, location, msgCode, pLayerPrefix, pMsg, pTrav->pUserData)) {
60                bail = true;
61            }
62        }
63        pTrav = pTrav->pNext;
64    }
65
66    return bail;
67}
68
69static inline debug_report_data *
70debug_report_create_instance(VkLayerInstanceDispatchTable *table, VkInstance inst, uint32_t extension_count,
71                             const char *const *ppEnabledExtensions) // layer or extension name to be enabled
72{
73    debug_report_data *debug_data;
74    PFN_vkGetInstanceProcAddr gpa = table->GetInstanceProcAddr;
75
76    table->CreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)gpa(inst, "vkCreateDebugReportCallbackEXT");
77    table->DestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT)gpa(inst, "vkDestroyDebugReportCallbackEXT");
78    table->DebugReportMessageEXT = (PFN_vkDebugReportMessageEXT)gpa(inst, "vkDebugReportMessageEXT");
79
80    debug_data = (debug_report_data *)malloc(sizeof(debug_report_data));
81    if (!debug_data)
82        return NULL;
83
84    memset(debug_data, 0, sizeof(debug_report_data));
85    for (uint32_t i = 0; i < extension_count; i++) {
86        /* TODO: Check other property fields */
87        if (strcmp(ppEnabledExtensions[i], VK_EXT_DEBUG_REPORT_EXTENSION_NAME) == 0) {
88            debug_data->g_DEBUG_REPORT = true;
89        }
90    }
91    return debug_data;
92}
93
94static inline void layer_debug_report_destroy_instance(debug_report_data *debug_data) {
95    VkLayerDbgFunctionNode *pTrav;
96    VkLayerDbgFunctionNode *pTravNext;
97
98    if (!debug_data) {
99        return;
100    }
101
102    pTrav = debug_data->g_pDbgFunctionHead;
103    /* Clear out any leftover callbacks */
104    while (pTrav) {
105        pTravNext = pTrav->pNext;
106
107        debug_report_log_msg(debug_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT,
108                             (uint64_t)pTrav->msgCallback, 0, VK_DEBUG_REPORT_ERROR_CALLBACK_REF_EXT, "DebugReport",
109                             "Debug Report callbacks not removed before DestroyInstance");
110
111        free(pTrav);
112        pTrav = pTravNext;
113    }
114    debug_data->g_pDbgFunctionHead = NULL;
115
116    free(debug_data);
117}
118
119static inline debug_report_data *layer_debug_report_create_device(debug_report_data *instance_debug_data, VkDevice device) {
120    /* DEBUG_REPORT shares data between Instance and Device,
121     * so just return instance's data pointer */
122    return instance_debug_data;
123}
124
125static inline void layer_debug_report_destroy_device(VkDevice device) { /* Nothing to do since we're using instance data record */ }
126
127static inline VkResult layer_create_msg_callback(debug_report_data *debug_data,
128                                                 const VkDebugReportCallbackCreateInfoEXT *pCreateInfo,
129                                                 const VkAllocationCallbacks *pAllocator, VkDebugReportCallbackEXT *pCallback) {
130    /* TODO: Use app allocator */
131    VkLayerDbgFunctionNode *pNewDbgFuncNode = (VkLayerDbgFunctionNode *)malloc(sizeof(VkLayerDbgFunctionNode));
132    if (!pNewDbgFuncNode)
133        return VK_ERROR_OUT_OF_HOST_MEMORY;
134
135    // Handle of 0 is logging_callback so use allocated Node address as unique handle
136    if (!(*pCallback))
137        *pCallback = (VkDebugReportCallbackEXT)pNewDbgFuncNode;
138    pNewDbgFuncNode->msgCallback = *pCallback;
139    pNewDbgFuncNode->pfnMsgCallback = pCreateInfo->pfnCallback;
140    pNewDbgFuncNode->msgFlags = pCreateInfo->flags;
141    pNewDbgFuncNode->pUserData = pCreateInfo->pUserData;
142    pNewDbgFuncNode->pNext = debug_data->g_pDbgFunctionHead;
143
144    debug_data->g_pDbgFunctionHead = pNewDbgFuncNode;
145    debug_data->active_flags |= pCreateInfo->flags;
146
147    debug_report_log_msg(debug_data, VK_DEBUG_REPORT_DEBUG_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT,
148                         (uint64_t)*pCallback, 0, VK_DEBUG_REPORT_ERROR_CALLBACK_REF_EXT, "DebugReport", "Added callback");
149    return VK_SUCCESS;
150}
151
152static inline void layer_destroy_msg_callback(debug_report_data *debug_data, VkDebugReportCallbackEXT callback,
153                                              const VkAllocationCallbacks *pAllocator) {
154    VkLayerDbgFunctionNode *pTrav = debug_data->g_pDbgFunctionHead;
155    VkLayerDbgFunctionNode *pPrev = pTrav;
156    bool matched;
157
158    debug_data->active_flags = 0;
159    while (pTrav) {
160        if (pTrav->msgCallback == callback) {
161            matched = true;
162            pPrev->pNext = pTrav->pNext;
163            if (debug_data->g_pDbgFunctionHead == pTrav) {
164                debug_data->g_pDbgFunctionHead = pTrav->pNext;
165            }
166            debug_report_log_msg(debug_data, VK_DEBUG_REPORT_DEBUG_BIT_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_EXT,
167                                 (uint64_t)pTrav->msgCallback, 0, VK_DEBUG_REPORT_ERROR_CALLBACK_REF_EXT, "DebugReport",
168                                 "Destroyed callback");
169        } else {
170            matched = false;
171            debug_data->active_flags |= pTrav->msgFlags;
172        }
173        pPrev = pTrav;
174        pTrav = pTrav->pNext;
175        if (matched) {
176            /* TODO: Use pAllocator */
177            free(pPrev);
178        }
179    }
180}
181
182static inline PFN_vkVoidFunction debug_report_get_instance_proc_addr(debug_report_data *debug_data, const char *funcName) {
183    if (!debug_data || !debug_data->g_DEBUG_REPORT) {
184        return NULL;
185    }
186
187    if (!strcmp(funcName, "vkCreateDebugReportCallbackEXT")) {
188        return (PFN_vkVoidFunction)vkCreateDebugReportCallbackEXT;
189    }
190    if (!strcmp(funcName, "vkDestroyDebugReportCallbackEXT")) {
191        return (PFN_vkVoidFunction)vkDestroyDebugReportCallbackEXT;
192    }
193
194    if (!strcmp(funcName, "vkDebugReportMessageEXT")) {
195        return (PFN_vkVoidFunction)vkDebugReportMessageEXT;
196    }
197
198    return NULL;
199}
200
201/*
202 * Checks if the message will get logged.
203 * Allows layer to defer collecting & formating data if the
204 * message will be discarded.
205 */
206static inline VkBool32 will_log_msg(debug_report_data *debug_data, VkFlags msgFlags) {
207    if (!debug_data || !(debug_data->active_flags & msgFlags)) {
208        /* message is not wanted */
209        return false;
210    }
211
212    return true;
213}
214
215/*
216 * Output log message via DEBUG_REPORT
217 * Takes format and variable arg list so that output string
218 * is only computed if a message needs to be logged
219 */
220#ifndef WIN32
221static inline VkBool32 log_msg(debug_report_data *debug_data, VkFlags msgFlags, VkDebugReportObjectTypeEXT objectType,
222                               uint64_t srcObject, size_t location, int32_t msgCode, const char *pLayerPrefix, const char *format,
223                               ...) __attribute__((format(printf, 8, 9)));
224#endif
225static inline VkBool32 log_msg(debug_report_data *debug_data, VkFlags msgFlags, VkDebugReportObjectTypeEXT objectType,
226                               uint64_t srcObject, size_t location, int32_t msgCode, const char *pLayerPrefix, const char *format,
227                               ...) {
228    if (!debug_data || !(debug_data->active_flags & msgFlags)) {
229        /* message is not wanted */
230        return false;
231    }
232
233    char str[1024];
234    va_list argptr;
235    va_start(argptr, format);
236    vsnprintf(str, 1024, format, argptr);
237    va_end(argptr);
238    return debug_report_log_msg(debug_data, msgFlags, objectType, srcObject, location, msgCode, pLayerPrefix, str);
239}
240
241static inline VKAPI_ATTR VkBool32 VKAPI_CALL log_callback(VkFlags msgFlags, VkDebugReportObjectTypeEXT objType, uint64_t srcObject,
242                                                          size_t location, int32_t msgCode, const char *pLayerPrefix,
243                                                          const char *pMsg, void *pUserData) {
244    char msg_flags[30];
245
246    print_msg_flags(msgFlags, msg_flags);
247
248    fprintf((FILE *)pUserData, "%s(%s): object: %#" PRIx64 " type: %d location: %lu msgCode: %d: %s\n", pLayerPrefix, msg_flags,
249            srcObject, objType, (unsigned long)location, msgCode, pMsg);
250    fflush((FILE *)pUserData);
251
252    return false;
253}
254
255static inline VKAPI_ATTR VkBool32 VKAPI_CALL win32_debug_output_msg(VkFlags msgFlags, VkDebugReportObjectTypeEXT objType,
256                                                                    uint64_t srcObject, size_t location, int32_t msgCode,
257                                                                    const char *pLayerPrefix, const char *pMsg, void *pUserData) {
258#ifdef WIN32
259    char msg_flags[30];
260    char buf[2048];
261
262    print_msg_flags(msgFlags, msg_flags);
263    _snprintf(buf, sizeof(buf) - 1,
264              "%s (%s): object: 0x%" PRIxPTR " type: %d location: " PRINTF_SIZE_T_SPECIFIER " msgCode: %d: %s\n", pLayerPrefix,
265              msg_flags, (size_t)srcObject, objType, location, msgCode, pMsg);
266
267    OutputDebugString(buf);
268#endif
269
270    return false;
271}
272
273#endif // LAYER_LOGGING_H
274