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
44typedef void(VKAPI_PTR *PFN_stringCallback)(char *message);
45
46static const VkExtensionProperties debug_report_extension_info = {
47    .extensionName = VK_EXT_DEBUG_REPORT_EXTENSION_NAME,
48    .specVersion = VK_EXT_DEBUG_REPORT_SPEC_VERSION,
49};
50
51void 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
57void 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
70VkResult
71util_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
98static 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
114VkBool32 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
136void 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
159static VKAPI_ATTR void VKAPI_CALL
160debug_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
173static 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
188VKAPI_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 */
241VKAPI_ATTR void VKAPI_CALL
242terminator_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 */
265VKAPI_ATTR void VKAPI_CALL
266terminator_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
294bool 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