1 /*
2 * Copyright (c) 2015-2016 The Khronos Group Inc.
3 * Copyright (c) 2015-2016 Valve Corporation
4 * Copyright (c) 2015-2016 LunarG, Inc.
5 * Copyright (C) 2015-2016 Google Inc.
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and/or associated documentation files (the "Materials"), to
9 * deal in the Materials without restriction, including without limitation the
10 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
11 * sell copies of the Materials, and to permit persons to whom the Materials are
12 * furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice(s) and this permission notice shall be included in
15 * all copies or substantial portions of the Materials.
16 *
17 * THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 *
21 * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
22 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
23 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE MATERIALS OR THE
24 * USE OR OTHER DEALINGS IN THE MATERIALS.
25 *
26 * Author: Courtney Goeltzenleuchter <courtney@LunarG.com>
27 * Author: Jon Ashburn <jon@LunarG.com>
28 *
29 */
30
31 #define _GNU_SOURCE
32 #include <stdio.h>
33 #include <string.h>
34 #include <stdlib.h>
35 #include <inttypes.h>
36 #ifndef WIN32
37 #include <signal.h>
38 #else
39 #endif
40 #include "vk_loader_platform.h"
41 #include "debug_report.h"
42 #include "vulkan/vk_layer.h"
43
44 typedef void(VKAPI_PTR *PFN_stringCallback)(char *message);
45
46 static const VkExtensionProperties debug_report_extension_info = {
47 .extensionName = VK_EXT_DEBUG_REPORT_EXTENSION_NAME,
48 .specVersion = VK_EXT_DEBUG_REPORT_SPEC_VERSION,
49 };
50
debug_report_add_instance_extensions(const struct loader_instance * inst,struct loader_extension_list * ext_list)51 void debug_report_add_instance_extensions(
52 const struct loader_instance *inst,
53 struct loader_extension_list *ext_list) {
54 loader_add_to_ext_list(inst, ext_list, 1, &debug_report_extension_info);
55 }
56
debug_report_create_instance(struct loader_instance * ptr_instance,const VkInstanceCreateInfo * pCreateInfo)57 void debug_report_create_instance(struct loader_instance *ptr_instance,
58 const VkInstanceCreateInfo *pCreateInfo) {
59 ptr_instance->debug_report_enabled = false;
60
61 for (uint32_t i = 0; i < pCreateInfo->enabledExtensionCount; i++) {
62 if (strcmp(pCreateInfo->ppEnabledExtensionNames[i],
63 VK_EXT_DEBUG_REPORT_EXTENSION_NAME) == 0) {
64 ptr_instance->debug_report_enabled = true;
65 return;
66 }
67 }
68 }
69
70 VkResult
util_CreateDebugReportCallback(struct loader_instance * inst,VkDebugReportCallbackCreateInfoEXT * pCreateInfo,const VkAllocationCallbacks * pAllocator,VkDebugReportCallbackEXT callback)71 util_CreateDebugReportCallback(struct loader_instance *inst,
72 VkDebugReportCallbackCreateInfoEXT *pCreateInfo,
73 const VkAllocationCallbacks *pAllocator,
74 VkDebugReportCallbackEXT callback) {
75 VkLayerDbgFunctionNode *pNewDbgFuncNode;
76 if (pAllocator != NULL) {
77 pNewDbgFuncNode = (VkLayerDbgFunctionNode *)pAllocator->pfnAllocation(
78 pAllocator->pUserData, sizeof(VkLayerDbgFunctionNode),
79 sizeof(int *), VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE);
80 } else {
81 pNewDbgFuncNode = (VkLayerDbgFunctionNode *)loader_heap_alloc(
82 inst, sizeof(VkLayerDbgFunctionNode),
83 VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
84 }
85 if (!pNewDbgFuncNode)
86 return VK_ERROR_OUT_OF_HOST_MEMORY;
87
88 pNewDbgFuncNode->msgCallback = callback;
89 pNewDbgFuncNode->pfnMsgCallback = pCreateInfo->pfnCallback;
90 pNewDbgFuncNode->msgFlags = pCreateInfo->flags;
91 pNewDbgFuncNode->pUserData = pCreateInfo->pUserData;
92 pNewDbgFuncNode->pNext = inst->DbgFunctionHead;
93 inst->DbgFunctionHead = pNewDbgFuncNode;
94
95 return VK_SUCCESS;
96 }
97
debug_report_CreateDebugReportCallback(VkInstance instance,VkDebugReportCallbackCreateInfoEXT * pCreateInfo,VkAllocationCallbacks * pAllocator,VkDebugReportCallbackEXT * pCallback)98 static VKAPI_ATTR VkResult VKAPI_CALL debug_report_CreateDebugReportCallback(
99 VkInstance instance, VkDebugReportCallbackCreateInfoEXT *pCreateInfo,
100 VkAllocationCallbacks *pAllocator, VkDebugReportCallbackEXT *pCallback) {
101 struct loader_instance *inst = loader_get_instance(instance);
102 loader_platform_thread_lock_mutex(&loader_lock);
103 VkResult result = inst->disp->CreateDebugReportCallbackEXT(
104 instance, pCreateInfo, pAllocator, pCallback);
105 if (result == VK_SUCCESS) {
106 result = util_CreateDebugReportCallback(inst, pCreateInfo, pAllocator,
107 *pCallback);
108 }
109 loader_platform_thread_unlock_mutex(&loader_lock);
110 return result;
111 }
112
113 // Utility function to handle reporting
util_DebugReportMessage(const struct loader_instance * inst,VkFlags msgFlags,VkDebugReportObjectTypeEXT objectType,uint64_t srcObject,size_t location,int32_t msgCode,const char * pLayerPrefix,const char * pMsg)114 VkBool32 util_DebugReportMessage(const struct loader_instance *inst,
115 VkFlags msgFlags,
116 VkDebugReportObjectTypeEXT objectType,
117 uint64_t srcObject, size_t location,
118 int32_t msgCode, const char *pLayerPrefix,
119 const char *pMsg) {
120 VkBool32 bail = false;
121 VkLayerDbgFunctionNode *pTrav = inst->DbgFunctionHead;
122 while (pTrav) {
123 if (pTrav->msgFlags & msgFlags) {
124 if (pTrav->pfnMsgCallback(msgFlags, objectType, srcObject, location,
125 msgCode, pLayerPrefix, pMsg,
126 pTrav->pUserData)) {
127 bail = true;
128 }
129 }
130 pTrav = pTrav->pNext;
131 }
132
133 return bail;
134 }
135
util_DestroyDebugReportCallback(struct loader_instance * inst,VkDebugReportCallbackEXT callback,const VkAllocationCallbacks * pAllocator)136 void util_DestroyDebugReportCallback(struct loader_instance *inst,
137 VkDebugReportCallbackEXT callback,
138 const VkAllocationCallbacks *pAllocator) {
139 VkLayerDbgFunctionNode *pTrav = inst->DbgFunctionHead;
140 VkLayerDbgFunctionNode *pPrev = pTrav;
141
142 while (pTrav) {
143 if (pTrav->msgCallback == callback) {
144 pPrev->pNext = pTrav->pNext;
145 if (inst->DbgFunctionHead == pTrav)
146 inst->DbgFunctionHead = pTrav->pNext;
147 if (pAllocator != NULL) {
148 pAllocator->pfnFree(pAllocator->pUserData, pTrav);
149 } else {
150 loader_heap_free(inst, pTrav);
151 }
152 break;
153 }
154 pPrev = pTrav;
155 pTrav = pTrav->pNext;
156 }
157 }
158
159 static VKAPI_ATTR void VKAPI_CALL
debug_report_DestroyDebugReportCallback(VkInstance instance,VkDebugReportCallbackEXT callback,VkAllocationCallbacks * pAllocator)160 debug_report_DestroyDebugReportCallback(VkInstance instance,
161 VkDebugReportCallbackEXT callback,
162 VkAllocationCallbacks *pAllocator) {
163 struct loader_instance *inst = loader_get_instance(instance);
164 loader_platform_thread_lock_mutex(&loader_lock);
165
166 inst->disp->DestroyDebugReportCallbackEXT(instance, callback, pAllocator);
167
168 util_DestroyDebugReportCallback(inst, callback, pAllocator);
169
170 loader_platform_thread_unlock_mutex(&loader_lock);
171 }
172
debug_report_DebugReportMessage(VkInstance instance,VkDebugReportFlagsEXT flags,VkDebugReportObjectTypeEXT objType,uint64_t object,size_t location,int32_t msgCode,const char * pLayerPrefix,const char * pMsg)173 static VKAPI_ATTR void VKAPI_CALL debug_report_DebugReportMessage(
174 VkInstance instance, VkDebugReportFlagsEXT flags,
175 VkDebugReportObjectTypeEXT objType, uint64_t object, size_t location,
176 int32_t msgCode, const char *pLayerPrefix, const char *pMsg) {
177 struct loader_instance *inst = loader_get_instance(instance);
178
179 inst->disp->DebugReportMessageEXT(instance, flags, objType, object,
180 location, msgCode, pLayerPrefix, pMsg);
181 }
182
183 /*
184 * This is the instance chain terminator function
185 * for CreateDebugReportCallback
186 */
187
terminator_CreateDebugReportCallback(VkInstance instance,const VkDebugReportCallbackCreateInfoEXT * pCreateInfo,const VkAllocationCallbacks * pAllocator,VkDebugReportCallbackEXT * pCallback)188 VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateDebugReportCallback(
189 VkInstance instance, const VkDebugReportCallbackCreateInfoEXT *pCreateInfo,
190 const VkAllocationCallbacks *pAllocator,
191 VkDebugReportCallbackEXT *pCallback) {
192 VkDebugReportCallbackEXT *icd_info;
193 const struct loader_icd *icd;
194 struct loader_instance *inst = (struct loader_instance *)instance;
195 VkResult res = VK_SUCCESS;
196 uint32_t storage_idx;
197
198 icd_info = calloc(sizeof(VkDebugReportCallbackEXT), inst->total_icd_count);
199 if (!icd_info) {
200 return VK_ERROR_OUT_OF_HOST_MEMORY;
201 }
202
203 storage_idx = 0;
204 for (icd = inst->icds; icd; icd = icd->next) {
205 if (!icd->CreateDebugReportCallbackEXT) {
206 continue;
207 }
208
209 res = icd->CreateDebugReportCallbackEXT(
210 icd->instance, pCreateInfo, pAllocator, &icd_info[storage_idx]);
211
212 if (res != VK_SUCCESS) {
213 break;
214 }
215 storage_idx++;
216 }
217
218 /* roll back on errors */
219 if (icd) {
220 storage_idx = 0;
221 for (icd = inst->icds; icd; icd = icd->next) {
222 if (icd_info[storage_idx]) {
223 icd->DestroyDebugReportCallbackEXT(
224 icd->instance, icd_info[storage_idx], pAllocator);
225 }
226 storage_idx++;
227 }
228
229 return res;
230 }
231
232 *(VkDebugReportCallbackEXT **)pCallback = icd_info;
233
234 return VK_SUCCESS;
235 }
236
237 /*
238 * This is the instance chain terminator function
239 * for DestroyDebugReportCallback
240 */
241 VKAPI_ATTR void VKAPI_CALL
terminator_DestroyDebugReportCallback(VkInstance instance,VkDebugReportCallbackEXT callback,const VkAllocationCallbacks * pAllocator)242 terminator_DestroyDebugReportCallback(VkInstance instance,
243 VkDebugReportCallbackEXT callback,
244 const VkAllocationCallbacks *pAllocator) {
245 uint32_t storage_idx;
246 VkDebugReportCallbackEXT *icd_info;
247 const struct loader_icd *icd;
248
249 struct loader_instance *inst = (struct loader_instance *)instance;
250 icd_info = *(VkDebugReportCallbackEXT **)&callback;
251 storage_idx = 0;
252 for (icd = inst->icds; icd; icd = icd->next) {
253 if (icd_info[storage_idx]) {
254 icd->DestroyDebugReportCallbackEXT(
255 icd->instance, icd_info[storage_idx], pAllocator);
256 }
257 storage_idx++;
258 }
259 }
260
261 /*
262 * This is the instance chain terminator function
263 * for DebugReportMessage
264 */
265 VKAPI_ATTR void VKAPI_CALL
terminator_DebugReportMessage(VkInstance instance,VkDebugReportFlagsEXT flags,VkDebugReportObjectTypeEXT objType,uint64_t object,size_t location,int32_t msgCode,const char * pLayerPrefix,const char * pMsg)266 terminator_DebugReportMessage(VkInstance instance, VkDebugReportFlagsEXT flags,
267 VkDebugReportObjectTypeEXT objType,
268 uint64_t object, size_t location, int32_t msgCode,
269 const char *pLayerPrefix, const char *pMsg) {
270 const struct loader_icd *icd;
271
272 struct loader_instance *inst = (struct loader_instance *)instance;
273
274 loader_platform_thread_lock_mutex(&loader_lock);
275 for (icd = inst->icds; icd; icd = icd->next) {
276 if (icd->DebugReportMessageEXT != NULL) {
277 icd->DebugReportMessageEXT(icd->instance, flags, objType, object,
278 location, msgCode, pLayerPrefix, pMsg);
279 }
280 }
281
282 /*
283 * Now that all ICDs have seen the message, call the necessary callbacks.
284 * Ignoring "bail" return value as there is nothing to bail from at this
285 * point.
286 */
287
288 util_DebugReportMessage(inst, flags, objType, object, location, msgCode,
289 pLayerPrefix, pMsg);
290
291 loader_platform_thread_unlock_mutex(&loader_lock);
292 }
293
debug_report_instance_gpa(struct loader_instance * ptr_instance,const char * name,void ** addr)294 bool debug_report_instance_gpa(struct loader_instance *ptr_instance,
295 const char *name, void **addr) {
296 // debug_report is currently advertised to be supported by the loader,
297 // so always return the entry points if name matches and it's enabled
298 *addr = NULL;
299
300 if (!strcmp("vkCreateDebugReportCallbackEXT", name)) {
301 *addr = ptr_instance->debug_report_enabled
302 ? (void *)debug_report_CreateDebugReportCallback
303 : NULL;
304 return true;
305 }
306 if (!strcmp("vkDestroyDebugReportCallbackEXT", name)) {
307 *addr = ptr_instance->debug_report_enabled
308 ? (void *)debug_report_DestroyDebugReportCallback
309 : NULL;
310 return true;
311 }
312 if (!strcmp("vkDebugReportMessageEXT", name)) {
313 *addr = ptr_instance->debug_report_enabled
314 ? (void *)debug_report_DebugReportMessage
315 : NULL;
316 return true;
317 }
318 return false;
319 }
320