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