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
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
AddDebugMessageCallback(debug_report_data * debug_data,VkLayerDbgFunctionNode ** list_head,VkLayerDbgFunctionNode * new_node)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
RemoveDebugMessageCallback(debug_report_data * debug_data,VkLayerDbgFunctionNode ** list_head,VkDebugReportCallbackEXT callback)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
RemoveAllMessageCallbacks(debug_report_data * debug_data,VkLayerDbgFunctionNode ** list_head)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
debug_report_log_msg(const debug_report_data * debug_data,VkFlags msgFlags,VkDebugReportObjectTypeEXT objectType,uint64_t srcObject,size_t location,int32_t msgCode,const char * pLayerPrefix,const char * pMsg)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 *
debug_report_create_instance(VkLayerInstanceDispatchTable * table,VkInstance inst,uint32_t extension_count,const char * const * ppEnabledExtensions)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
layer_debug_report_destroy_instance(debug_report_data * debug_data)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
layer_debug_report_create_device(debug_report_data * instance_debug_data,VkDevice device)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
layer_debug_report_destroy_device(VkDevice device)164 static inline void layer_debug_report_destroy_device(VkDevice device) {
165 // Nothing to do since we're using instance data record
166 }
167
layer_destroy_msg_callback(debug_report_data * debug_data,VkDebugReportCallbackEXT callback,const VkAllocationCallbacks * pAllocator)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
layer_create_msg_callback(debug_report_data * debug_data,bool default_callback,const VkDebugReportCallbackCreateInfoEXT * pCreateInfo,const VkAllocationCallbacks * pAllocator,VkDebugReportCallbackEXT * pCallback)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
debug_report_get_instance_proc_addr(debug_report_data * debug_data,const char * funcName)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.
layer_copy_tmp_callbacks(const void * pChain,uint32_t * num_callbacks,VkDebugReportCallbackCreateInfoEXT ** infos,VkDebugReportCallbackEXT ** callbacks)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()
layer_free_tmp_callbacks(VkDebugReportCallbackCreateInfoEXT * infos,VkDebugReportCallbackEXT * 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()
layer_enable_tmp_callbacks(debug_report_data * debug_data,uint32_t num_callbacks,VkDebugReportCallbackCreateInfoEXT * infos,VkDebugReportCallbackEXT * 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()
layer_disable_tmp_callbacks(debug_report_data * debug_data,uint32_t num_callbacks,VkDebugReportCallbackEXT * 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.
will_log_msg(debug_report_data * debug_data,VkFlags msgFlags)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
vasprintf(char ** strp,char const * fmt,va_list ap)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
log_msg(const debug_report_data * debug_data,VkFlags msgFlags,VkDebugReportObjectTypeEXT objectType,uint64_t srcObject,size_t location,int32_t msgCode,const char * pLayerPrefix,const char * format,...)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
log_callback(VkFlags msgFlags,VkDebugReportObjectTypeEXT objType,uint64_t srcObject,size_t location,int32_t msgCode,const char * pLayerPrefix,const char * pMsg,void * pUserData)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
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)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