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