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 
42 typedef struct _debug_report_data {
43     VkLayerDbgFunctionNode *g_pDbgFunctionHead;
44     VkFlags active_flags;
45     bool g_DEBUG_REPORT;
46 } debug_report_data;
47 
48 template 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
debug_report_log_msg(debug_report_data * debug_data,VkFlags msgFlags,VkDebugReportObjectTypeEXT objectType,uint64_t srcObject,size_t location,int32_t msgCode,const char * pLayerPrefix,const char * pMsg)52 static 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 
69 static inline debug_report_data *
debug_report_create_instance(VkLayerInstanceDispatchTable * table,VkInstance inst,uint32_t extension_count,const char * const * ppEnabledExtensions)70 debug_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 
layer_debug_report_destroy_instance(debug_report_data * debug_data)94 static 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 
layer_debug_report_create_device(debug_report_data * instance_debug_data,VkDevice device)119 static 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 
layer_debug_report_destroy_device(VkDevice device)125 static inline void layer_debug_report_destroy_device(VkDevice device) { /* Nothing to do since we're using instance data record */ }
126 
layer_create_msg_callback(debug_report_data * debug_data,const VkDebugReportCallbackCreateInfoEXT * pCreateInfo,const VkAllocationCallbacks * pAllocator,VkDebugReportCallbackEXT * pCallback)127 static 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 
layer_destroy_msg_callback(debug_report_data * debug_data,VkDebugReportCallbackEXT callback,const VkAllocationCallbacks * pAllocator)152 static 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 
debug_report_get_instance_proc_addr(debug_report_data * debug_data,const char * funcName)182 static 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  */
will_log_msg(debug_report_data * debug_data,VkFlags msgFlags)206 static 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
221 static 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
log_msg(debug_report_data * debug_data,VkFlags msgFlags,VkDebugReportObjectTypeEXT objectType,uint64_t srcObject,size_t location,int32_t msgCode,const char * pLayerPrefix,const char * format,...)225 static 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 
log_callback(VkFlags msgFlags,VkDebugReportObjectTypeEXT objType,uint64_t srcObject,size_t location,int32_t msgCode,const char * pLayerPrefix,const char * pMsg,void * pUserData)241 static 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 
win32_debug_output_msg(VkFlags msgFlags,VkDebugReportObjectTypeEXT objType,uint64_t srcObject,size_t location,int32_t msgCode,const char * pLayerPrefix,const char * pMsg,void * pUserData)255 static 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