• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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