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