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_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
38typedef 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
45template 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
49static 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
54static 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
62static 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
92static 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
108static 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
132static inline debug_report_data *
133debug_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
150static 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
158static 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
164static inline void layer_debug_report_destroy_device(VkDevice device) {
165    // Nothing to do since we're using instance data record
166}
167
168static 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
174static 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
201static 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.
223static 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()
268static 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()
275static 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()
292static 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.
302static 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
312static 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
330static 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
334static 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
356static 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
370static 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