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 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 * Author: Courtney Goeltzenleuchter <courtney@LunarG.com>
20 * Author: Jon Ashburn <jon@LunarG.com>
21 *
22 */
23
24 #define _GNU_SOURCE
25 #include <stdio.h>
26 #include <string.h>
27 #include <stdlib.h>
28 #include <inttypes.h>
29 #ifndef WIN32
30 #include <signal.h>
31 #else
32 #endif
33 #include "vk_loader_platform.h"
34 #include "debug_report.h"
35 #include "vulkan/vk_layer.h"
36
37 typedef void(VKAPI_PTR *PFN_stringCallback)(char *message);
38
39 static const VkExtensionProperties debug_report_extension_info = {
40 .extensionName = VK_EXT_DEBUG_REPORT_EXTENSION_NAME,
41 .specVersion = VK_EXT_DEBUG_REPORT_SPEC_VERSION,
42 };
43
debug_report_add_instance_extensions(const struct loader_instance * inst,struct loader_extension_list * ext_list)44 void debug_report_add_instance_extensions(
45 const struct loader_instance *inst,
46 struct loader_extension_list *ext_list) {
47 loader_add_to_ext_list(inst, ext_list, 1, &debug_report_extension_info);
48 }
49
debug_report_create_instance(struct loader_instance * ptr_instance,const VkInstanceCreateInfo * pCreateInfo)50 void debug_report_create_instance(struct loader_instance *ptr_instance,
51 const VkInstanceCreateInfo *pCreateInfo) {
52 ptr_instance->enabled_known_extensions.ext_debug_report = 0;
53
54 for (uint32_t i = 0; i < pCreateInfo->enabledExtensionCount; i++) {
55 if (strcmp(pCreateInfo->ppEnabledExtensionNames[i],
56 VK_EXT_DEBUG_REPORT_EXTENSION_NAME) == 0) {
57 ptr_instance->enabled_known_extensions.ext_debug_report = 1;
58 return;
59 }
60 }
61 }
62
63 VkResult
util_CreateDebugReportCallback(struct loader_instance * inst,VkDebugReportCallbackCreateInfoEXT * pCreateInfo,const VkAllocationCallbacks * pAllocator,VkDebugReportCallbackEXT callback)64 util_CreateDebugReportCallback(struct loader_instance *inst,
65 VkDebugReportCallbackCreateInfoEXT *pCreateInfo,
66 const VkAllocationCallbacks *pAllocator,
67 VkDebugReportCallbackEXT callback) {
68 VkLayerDbgFunctionNode *pNewDbgFuncNode = NULL;
69
70 #if (DEBUG_DISABLE_APP_ALLOCATORS == 1)
71 {
72 #else
73 if (pAllocator != NULL) {
74 pNewDbgFuncNode =
75 (VkLayerDbgFunctionNode *)pAllocator->pfnAllocation(
76 pAllocator->pUserData, sizeof(VkLayerDbgFunctionNode),
77 sizeof(int *), VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
78 } else {
79 #endif
80 pNewDbgFuncNode =
81 (VkLayerDbgFunctionNode *)loader_instance_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 memset(pNewDbgFuncNode, 0, sizeof(VkLayerDbgFunctionNode));
89
90 pNewDbgFuncNode->msgCallback = callback;
91 pNewDbgFuncNode->pfnMsgCallback = pCreateInfo->pfnCallback;
92 pNewDbgFuncNode->msgFlags = pCreateInfo->flags;
93 pNewDbgFuncNode->pUserData = pCreateInfo->pUserData;
94 pNewDbgFuncNode->pNext = inst->DbgFunctionHead;
95 inst->DbgFunctionHead = pNewDbgFuncNode;
96
97 return VK_SUCCESS;
98 }
99
100 static VKAPI_ATTR VkResult VKAPI_CALL debug_report_CreateDebugReportCallbackEXT(
101 VkInstance instance, const VkDebugReportCallbackCreateInfoEXT *pCreateInfo,
102 const VkAllocationCallbacks *pAllocator,
103 VkDebugReportCallbackEXT *pCallback) {
104 struct loader_instance *inst = loader_get_instance(instance);
105 loader_platform_thread_lock_mutex(&loader_lock);
106 VkResult result = inst->disp->CreateDebugReportCallbackEXT(
107 instance, pCreateInfo, pAllocator, pCallback);
108 loader_platform_thread_unlock_mutex(&loader_lock);
109 return result;
110 }
111
112 // Utility function to handle reporting
113 VkBool32 util_DebugReportMessage(const struct loader_instance *inst,
114 VkFlags msgFlags,
115 VkDebugReportObjectTypeEXT objectType,
116 uint64_t srcObject, size_t location,
117 int32_t msgCode, const char *pLayerPrefix,
118 const char *pMsg) {
119 VkBool32 bail = false;
120 VkLayerDbgFunctionNode *pTrav = inst->DbgFunctionHead;
121 while (pTrav) {
122 if (pTrav->msgFlags & msgFlags) {
123 if (pTrav->pfnMsgCallback(msgFlags, objectType, srcObject, location,
124 msgCode, pLayerPrefix, pMsg,
125 pTrav->pUserData)) {
126 bail = true;
127 }
128 }
129 pTrav = pTrav->pNext;
130 }
131
132 return bail;
133 }
134
135 void util_DestroyDebugReportCallback(struct loader_instance *inst,
136 VkDebugReportCallbackEXT callback,
137 const VkAllocationCallbacks *pAllocator) {
138 VkLayerDbgFunctionNode *pTrav = inst->DbgFunctionHead;
139 VkLayerDbgFunctionNode *pPrev = pTrav;
140
141 while (pTrav) {
142 if (pTrav->msgCallback == callback) {
143 pPrev->pNext = pTrav->pNext;
144 if (inst->DbgFunctionHead == pTrav)
145 inst->DbgFunctionHead = pTrav->pNext;
146 #if (DEBUG_DISABLE_APP_ALLOCATORS == 1)
147 {
148 #else
149 if (pAllocator != NULL) {
150 pAllocator->pfnFree(pAllocator->pUserData, pTrav);
151 } else {
152 #endif
153 loader_instance_heap_free(inst, pTrav);
154 }
155 break;
156 }
157 pPrev = pTrav;
158 pTrav = pTrav->pNext;
159 }
160 }
161
162 // This utility (used by vkInstanceCreateInfo(), looks at a pNext chain. It
163 // counts any VkDebugReportCallbackCreateInfoEXT structs that it finds. It
164 // then allocates array that can hold that many structs, as well as that many
165 // VkDebugReportCallbackEXT handles. It then copies each
166 // VkDebugReportCallbackCreateInfoEXT, and initializes each handle.
167 VkResult util_CopyDebugReportCreateInfos(
168 const void *pChain, const VkAllocationCallbacks *pAllocator,
169 uint32_t *num_callbacks, VkDebugReportCallbackCreateInfoEXT **infos,
170 VkDebugReportCallbackEXT **callbacks) {
171 uint32_t n = *num_callbacks = 0;
172 VkDebugReportCallbackCreateInfoEXT *pInfos = NULL;
173 VkDebugReportCallbackEXT *pCallbacks = NULL;
174
175 // NOTE: The loader is not using pAllocator, and so this function doesn't
176 // either.
177
178 const void *pNext = pChain;
179 while (pNext) {
180 // 1st, count the number VkDebugReportCallbackCreateInfoEXT:
181 if (((VkDebugReportCallbackCreateInfoEXT *)pNext)->sType ==
182 VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT) {
183 n++;
184 }
185 pNext = (void *)((VkDebugReportCallbackCreateInfoEXT *)pNext)->pNext;
186 }
187 if (n == 0) {
188 return VK_SUCCESS;
189 }
190
191 // 2nd, allocate memory for each VkDebugReportCallbackCreateInfoEXT:
192 #if (DEBUG_DISABLE_APP_ALLOCATORS == 1)
193 {
194 #else
195 if (pAllocator != NULL) {
196 pInfos = *infos =
197 ((VkDebugReportCallbackCreateInfoEXT *)pAllocator->pfnAllocation(
198 pAllocator->pUserData,
199 n * sizeof(VkDebugReportCallbackCreateInfoEXT), sizeof(void *),
200 VK_SYSTEM_ALLOCATION_SCOPE_OBJECT));
201 } else {
202 #endif
203 pInfos = *infos = ((VkDebugReportCallbackCreateInfoEXT *)malloc(
204 n * sizeof(VkDebugReportCallbackCreateInfoEXT)));
205 }
206 if (!pInfos) {
207 return VK_ERROR_OUT_OF_HOST_MEMORY;
208 }
209 // 3rd, allocate memory for a unique handle for each callback:
210 #if (DEBUG_DISABLE_APP_ALLOCATORS == 1)
211 {
212 #else
213 if (pAllocator != NULL) {
214 pCallbacks = *callbacks =
215 ((VkDebugReportCallbackEXT *)pAllocator->pfnAllocation(
216 pAllocator->pUserData, n * sizeof(VkDebugReportCallbackEXT),
217 sizeof(void *), VK_SYSTEM_ALLOCATION_SCOPE_OBJECT));
218 } else {
219 #endif
220 pCallbacks = *callbacks = ((VkDebugReportCallbackEXT *)malloc(
221 n * sizeof(VkDebugReportCallbackEXT)));
222 }
223 if (!pCallbacks) {
224 free(pInfos);
225 return VK_ERROR_OUT_OF_HOST_MEMORY;
226 }
227 // 4th, copy each VkDebugReportCallbackCreateInfoEXT for use by
228 // vkDestroyInstance, and assign a unique handle to each callback (just
229 // use the address of the copied VkDebugReportCallbackCreateInfoEXT):
230 pNext = pChain;
231 while (pNext) {
232 if (((VkInstanceCreateInfo *)pNext)->sType ==
233 VK_STRUCTURE_TYPE_DEBUG_REPORT_CREATE_INFO_EXT) {
234 memcpy(pInfos, pNext, sizeof(VkDebugReportCallbackCreateInfoEXT));
235 *pCallbacks++ = (VkDebugReportCallbackEXT)pInfos++;
236 }
237 pNext = (void *)((VkInstanceCreateInfo *)pNext)->pNext;
238 }
239
240 *num_callbacks = n;
241 return VK_SUCCESS;
242 }
243
244 void util_FreeDebugReportCreateInfos(const VkAllocationCallbacks *pAllocator,
245 VkDebugReportCallbackCreateInfoEXT *infos,
246 VkDebugReportCallbackEXT *callbacks) {
247 free(infos);
248 free(callbacks);
249 }
250
251 VkResult util_CreateDebugReportCallbacks(
252 struct loader_instance *inst, const VkAllocationCallbacks *pAllocator,
253 uint32_t num_callbacks, VkDebugReportCallbackCreateInfoEXT *infos,
254 VkDebugReportCallbackEXT *callbacks) {
255 VkResult rtn = VK_SUCCESS;
256 for (uint32_t i = 0; i < num_callbacks; i++) {
257 rtn = util_CreateDebugReportCallback(inst, &infos[i], pAllocator,
258 callbacks[i]);
259 if (rtn != VK_SUCCESS) {
260 for (uint32_t j = 0; j < i; j++) {
261 util_DestroyDebugReportCallback(inst, callbacks[j], pAllocator);
262 }
263 return rtn;
264 }
265 }
266 return rtn;
267 }
268
269 void util_DestroyDebugReportCallbacks(struct loader_instance *inst,
270 const VkAllocationCallbacks *pAllocator,
271 uint32_t num_callbacks,
272 VkDebugReportCallbackEXT *callbacks) {
273 for (uint32_t i = 0; i < num_callbacks; i++) {
274 util_DestroyDebugReportCallback(inst, callbacks[i], pAllocator);
275 }
276 }
277
278 static VKAPI_ATTR void VKAPI_CALL
279 debug_report_DestroyDebugReportCallbackEXT(
280 VkInstance instance, VkDebugReportCallbackEXT callback,
281 const VkAllocationCallbacks *pAllocator) {
282 struct loader_instance *inst = loader_get_instance(instance);
283 loader_platform_thread_lock_mutex(&loader_lock);
284
285 inst->disp->DestroyDebugReportCallbackEXT(instance, callback, pAllocator);
286
287 util_DestroyDebugReportCallback(inst, callback, pAllocator);
288
289 loader_platform_thread_unlock_mutex(&loader_lock);
290 }
291
292 static VKAPI_ATTR void VKAPI_CALL debug_report_DebugReportMessageEXT(
293 VkInstance instance, VkDebugReportFlagsEXT flags,
294 VkDebugReportObjectTypeEXT objType, uint64_t object, size_t location,
295 int32_t msgCode, const char *pLayerPrefix, const char *pMsg) {
296 struct loader_instance *inst = loader_get_instance(instance);
297
298 inst->disp->DebugReportMessageEXT(instance, flags, objType, object,
299 location, msgCode, pLayerPrefix, pMsg);
300 }
301
302 /*
303 * This is the instance chain terminator function
304 * for CreateDebugReportCallback
305 */
306
307 VKAPI_ATTR VkResult VKAPI_CALL terminator_CreateDebugReportCallback(
308 VkInstance instance, const VkDebugReportCallbackCreateInfoEXT *pCreateInfo,
309 const VkAllocationCallbacks *pAllocator,
310 VkDebugReportCallbackEXT *pCallback) {
311 VkDebugReportCallbackEXT *icd_info = NULL;
312 const struct loader_icd *icd;
313 struct loader_instance *inst = (struct loader_instance *)instance;
314 VkResult res = VK_SUCCESS;
315 uint32_t storage_idx;
316 VkLayerDbgFunctionNode *pNewDbgFuncNode = NULL;
317
318 #if (DEBUG_DISABLE_APP_ALLOCATORS == 1)
319 {
320 #else
321 if (pAllocator != NULL) {
322 icd_info = ((VkDebugReportCallbackEXT *)pAllocator->pfnAllocation(
323 pAllocator->pUserData,
324 inst->total_icd_count * sizeof(VkDebugReportCallbackEXT),
325 sizeof(void *), VK_SYSTEM_ALLOCATION_SCOPE_OBJECT));
326 memset(icd_info, 0,
327 inst->total_icd_count * sizeof(VkDebugReportCallbackEXT));
328 } else {
329 #endif
330 icd_info =
331 calloc(sizeof(VkDebugReportCallbackEXT), inst->total_icd_count);
332 }
333 if (!icd_info) {
334 res = VK_ERROR_OUT_OF_HOST_MEMORY;
335 goto out;
336 }
337
338 storage_idx = 0;
339 for (icd = inst->icds; icd; icd = icd->next) {
340 if (!icd->CreateDebugReportCallbackEXT) {
341 continue;
342 }
343
344 res = icd->CreateDebugReportCallbackEXT(
345 icd->instance, pCreateInfo, pAllocator, &icd_info[storage_idx]);
346
347 if (res != VK_SUCCESS) {
348 goto out;
349 }
350 storage_idx++;
351 }
352
353 // Setup the debug report callback in the terminator since a layer may want
354 // to grab the information itself (RenderDoc) and then return back to the
355 // user callback a sub-set of the messages.
356 #if (DEBUG_DISABLE_APP_ALLOCATORS == 0)
357 if (pAllocator != NULL) {
358 pNewDbgFuncNode =
359 (VkLayerDbgFunctionNode *)pAllocator->pfnAllocation(
360 pAllocator->pUserData, sizeof(VkLayerDbgFunctionNode),
361 sizeof(int *), VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
362 } else {
363 #else
364 {
365 #endif
366 pNewDbgFuncNode =
367 (VkLayerDbgFunctionNode *)loader_instance_heap_alloc(
368 inst, sizeof(VkLayerDbgFunctionNode),
369 VK_SYSTEM_ALLOCATION_SCOPE_OBJECT);
370 }
371 if (!pNewDbgFuncNode) {
372 res = VK_ERROR_OUT_OF_HOST_MEMORY;
373 goto out;
374 }
375 memset(pNewDbgFuncNode, 0, sizeof(VkLayerDbgFunctionNode));
376
377 pNewDbgFuncNode->pfnMsgCallback = pCreateInfo->pfnCallback;
378 pNewDbgFuncNode->msgFlags = pCreateInfo->flags;
379 pNewDbgFuncNode->pUserData = pCreateInfo->pUserData;
380 pNewDbgFuncNode->pNext = inst->DbgFunctionHead;
381 inst->DbgFunctionHead = pNewDbgFuncNode;
382
383 *(VkDebugReportCallbackEXT **)pCallback = icd_info;
384 pNewDbgFuncNode->msgCallback = *pCallback;
385
386 out:
387
388 // Roll back on errors
389 if (VK_SUCCESS != res) {
390 storage_idx = 0;
391 for (icd = inst->icds; icd; icd = icd->next) {
392 if (NULL == icd->DestroyDebugReportCallbackEXT) {
393 continue;
394 }
395
396 if (icd_info[storage_idx]) {
397 icd->DestroyDebugReportCallbackEXT(
398 icd->instance, icd_info[storage_idx], pAllocator);
399 }
400 storage_idx++;
401 }
402
403 #if (DEBUG_DISABLE_APP_ALLOCATORS == 1)
404 {
405 #else
406 if (pAllocator != NULL) {
407 if (NULL != pNewDbgFuncNode) {
408 pAllocator->pfnFree(pAllocator->pUserData, pNewDbgFuncNode);
409 }
410 if (NULL != icd_info) {
411 pAllocator->pfnFree(pAllocator->pUserData, icd_info);
412 }
413 } else {
414 #endif
415 if (NULL != pNewDbgFuncNode) {
416 free(pNewDbgFuncNode);
417 }
418 if (NULL != icd_info) {
419 free(icd_info);
420 }
421 }
422 }
423
424 return res;
425 }
426
427 /*
428 * This is the instance chain terminator function
429 * for DestroyDebugReportCallback
430 */
431 VKAPI_ATTR void VKAPI_CALL terminator_DestroyDebugReportCallback(
432 VkInstance instance, VkDebugReportCallbackEXT callback,
433 const VkAllocationCallbacks *pAllocator) {
434 uint32_t storage_idx;
435 VkDebugReportCallbackEXT *icd_info;
436 const struct loader_icd *icd;
437
438 struct loader_instance *inst = (struct loader_instance *)instance;
439 icd_info = *(VkDebugReportCallbackEXT **)&callback;
440 storage_idx = 0;
441 for (icd = inst->icds; icd; icd = icd->next) {
442 if (NULL == icd->DestroyDebugReportCallbackEXT) {
443 continue;
444 }
445
446 if (icd_info[storage_idx]) {
447 icd->DestroyDebugReportCallbackEXT(
448 icd->instance, icd_info[storage_idx], pAllocator);
449 }
450 storage_idx++;
451 }
452 }
453
454 /*
455 * This is the instance chain terminator function
456 * for DebugReportMessage
457 */
458 VKAPI_ATTR void VKAPI_CALL terminator_DebugReportMessage(
459 VkInstance instance, VkDebugReportFlagsEXT flags,
460 VkDebugReportObjectTypeEXT objType, uint64_t object, size_t location,
461 int32_t msgCode, const char *pLayerPrefix, const char *pMsg) {
462 const struct loader_icd *icd;
463
464 struct loader_instance *inst = (struct loader_instance *)instance;
465
466 loader_platform_thread_lock_mutex(&loader_lock);
467 for (icd = inst->icds; icd; icd = icd->next) {
468 if (icd->DebugReportMessageEXT != NULL) {
469 icd->DebugReportMessageEXT(icd->instance, flags, objType, object,
470 location, msgCode, pLayerPrefix, pMsg);
471 }
472 }
473
474 /*
475 * Now that all ICDs have seen the message, call the necessary callbacks.
476 * Ignoring "bail" return value as there is nothing to bail from at this
477 * point.
478 */
479
480 util_DebugReportMessage(inst, flags, objType, object, location, msgCode,
481 pLayerPrefix, pMsg);
482
483 loader_platform_thread_unlock_mutex(&loader_lock);
484 }
485
486 bool debug_report_instance_gpa(struct loader_instance *ptr_instance,
487 const char *name, void **addr) {
488 // debug_report is currently advertised to be supported by the loader,
489 // so always return the entry points if name matches and it's enabled
490 *addr = NULL;
491
492 if (!strcmp("vkCreateDebugReportCallbackEXT", name)) {
493 *addr = (ptr_instance->enabled_known_extensions.ext_debug_report == 1)
494 ? (void *)debug_report_CreateDebugReportCallbackEXT
495 : NULL;
496 return true;
497 }
498 if (!strcmp("vkDestroyDebugReportCallbackEXT", name)) {
499 *addr = (ptr_instance->enabled_known_extensions.ext_debug_report == 1)
500 ? (void *)debug_report_DestroyDebugReportCallbackEXT
501 : NULL;
502 return true;
503 }
504 if (!strcmp("vkDebugReportMessageEXT", name)) {
505 *addr = (ptr_instance->enabled_known_extensions.ext_debug_report == 1)
506 ? (void *)debug_report_DebugReportMessageEXT
507 : NULL;
508 return true;
509 }
510 return false;
511 }
512