xref: /third_party/vulkan-loader/loader/log.c (revision 5db71995)
1/*
2 *
3 * Copyright (c) 2014-2022 The Khronos Group Inc.
4 * Copyright (c) 2014-2022 Valve Corporation
5 * Copyright (c) 2014-2022 LunarG, 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: Jon Ashburn <jon@lunarg.com>
20 * Author: Courtney Goeltzenleuchter <courtney@LunarG.com>
21 * Author: Chia-I Wu <olvaffe@gmail.com>
22 * Author: Chia-I Wu <olv@lunarg.com>
23 * Author: Mark Lobodzinski <mark@LunarG.com>
24 * Author: Lenny Komow <lenny@lunarg.com>
25 * Author: Charles Giessen <charles@lunarg.com>
26 *
27 */
28
29#include "log.h"
30
31#include <stdio.h>
32#include <stdarg.h>
33
34#include "debug_utils.h"
35#include "loader_common.h"
36#include "loader_environment.h"
37#include "settings.h"
38#include "vk_loader_platform.h"
39#ifdef VK_USE_PLATFORM_OHOS
40#include "loader_hilog.h"
41#endif
42
43uint32_t g_loader_debug = ~0u;
44
45void loader_init_global_debug_level(void) {
46    char *env, *orig;
47
48    if (g_loader_debug > 0) return;
49
50    g_loader_debug = 0;
51
52    // Parse comma-separated debug options
53    orig = env = loader_getenv("VK_LOADER_DEBUG", NULL);
54    while (env) {
55        char *p = strchr(env, ',');
56        size_t len;
57
58        if (p) {
59            len = p - env;
60        } else {
61            len = strlen(env);
62        }
63
64        if (len > 0) {
65            if (strncmp(env, "all", len) == 0) {
66                g_loader_debug = ~0u;
67            } else if (strncmp(env, "warn", len) == 0) {
68                g_loader_debug |= VULKAN_LOADER_WARN_BIT;
69            } else if (strncmp(env, "info", len) == 0) {
70                g_loader_debug |= VULKAN_LOADER_INFO_BIT;
71            } else if (strncmp(env, "perf", len) == 0) {
72                g_loader_debug |= VULKAN_LOADER_PERF_BIT;
73            } else if (strncmp(env, "error", len) == 0) {
74                g_loader_debug |= VULKAN_LOADER_ERROR_BIT;
75            } else if (strncmp(env, "debug", len) == 0) {
76                g_loader_debug |= VULKAN_LOADER_DEBUG_BIT;
77            } else if (strncmp(env, "layer", len) == 0) {
78                g_loader_debug |= VULKAN_LOADER_LAYER_BIT;
79            } else if (strncmp(env, "driver", len) == 0 || strncmp(env, "implem", len) == 0 || strncmp(env, "icd", len) == 0) {
80                g_loader_debug |= VULKAN_LOADER_DRIVER_BIT;
81            }
82        }
83
84        if (!p) break;
85
86        env = p + 1;
87    }
88
89    loader_free_getenv(orig, NULL);
90}
91
92void loader_set_global_debug_level(uint32_t new_loader_debug) { g_loader_debug = new_loader_debug; }
93
94void loader_log(const struct loader_instance *inst, VkFlags msg_type, int32_t msg_code, const char *format, ...) {
95    (void)msg_code;
96    char msg[512] = {0};
97
98    va_list ap;
99    va_start(ap, format);
100    int ret = vsnprintf(msg, sizeof(msg), format, ap);
101    if ((ret >= (int)sizeof(msg)) || ret < 0) {
102        msg[sizeof(msg) - 1] = '\0';
103    }
104    va_end(ap);
105
106    if (inst) {
107        VkDebugUtilsMessageSeverityFlagBitsEXT severity = 0;
108        VkDebugUtilsMessageTypeFlagsEXT type = 0;
109        VkDebugUtilsMessengerCallbackDataEXT callback_data = {0};
110        VkDebugUtilsObjectNameInfoEXT object_name = {0};
111
112        if ((msg_type & VULKAN_LOADER_INFO_BIT) != 0) {
113            severity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT;
114        } else if ((msg_type & VULKAN_LOADER_WARN_BIT) != 0) {
115            severity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT;
116        } else if ((msg_type & VULKAN_LOADER_ERROR_BIT) != 0) {
117            severity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
118        } else if ((msg_type & VULKAN_LOADER_DEBUG_BIT) != 0) {
119            severity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT;
120        } else if ((msg_type & VULKAN_LOADER_LAYER_BIT) != 0 || (msg_type & VULKAN_LOADER_DRIVER_BIT) != 0) {
121            // Just driver or just layer bit should be treated as an info message in debug utils.
122            severity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT;
123        }
124
125        if ((msg_type & VULKAN_LOADER_PERF_BIT) != 0) {
126            type = VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
127        } else if ((msg_type & VULKAN_LOADER_VALIDATION_BIT) != 0) {
128            // For loader logging, if it's a validation message, we still want to also keep the general flag as well
129            // so messages of type validation can still be triggered for general message callbacks.
130            type = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT;
131        } else {
132            type = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT;
133        }
134
135        callback_data.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CALLBACK_DATA_EXT;
136        callback_data.pMessageIdName = "Loader Message";
137        callback_data.pMessage = msg;
138        callback_data.objectCount = 1;
139        callback_data.pObjects = &object_name;
140        object_name.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
141        object_name.objectType = VK_OBJECT_TYPE_INSTANCE;
142        object_name.objectHandle = (uint64_t)(uintptr_t)inst;
143
144        util_SubmitDebugUtilsMessageEXT(inst, severity, type, &callback_data);
145    }
146
147    // Always log to stderr if this is a fatal error
148    if (0 == (msg_type & VULKAN_LOADER_FATAL_ERROR_BIT)) {
149        // Exit early if the current instance settings do not ask for logging to stderr
150        if (inst && inst->settings.settings_active && 0 == (msg_type & inst->settings.debug_level)) {
151            return;
152            // Check the global settings and if that doesn't say to skip, check the environment variable
153        } else if (0 == (msg_type & g_loader_debug)) {
154            return;
155        }
156    }
157
158    // Only need enough space to create the filter description header for log messages
159    // Also use the same header for all output
160    char cmd_line_msg[64];
161    size_t cmd_line_size = sizeof(cmd_line_msg);
162    size_t num_used = 0;
163
164    cmd_line_msg[0] = '\0';
165
166// Helper macro which strncat's the given string literal, then updates num_used & cmd_line_end
167// Assumes that we haven't used the entire buffer - must manually check this when adding new filter types
168// We concat at the end of cmd_line_msg, so that strncat isn't a victim of Schlemiel the Painter
169// We write to the end - 1 of cmd_line_msg, as the end is actually a null terminator
170#define STRNCAT_TO_BUFFER(string_literal_to_cat)                                                                             \
171    loader_strncat(cmd_line_msg + num_used, cmd_line_size - num_used, string_literal_to_cat, sizeof(string_literal_to_cat)); \
172    num_used += sizeof(string_literal_to_cat) - 1;  // subtract one to remove the null terminator in the string literal
173
174    if ((msg_type & VULKAN_LOADER_ERROR_BIT) != 0) {
175        STRNCAT_TO_BUFFER("ERROR");
176    } else if ((msg_type & VULKAN_LOADER_WARN_BIT) != 0) {
177        STRNCAT_TO_BUFFER("WARNING");
178    } else if ((msg_type & VULKAN_LOADER_INFO_BIT) != 0) {
179        STRNCAT_TO_BUFFER("INFO");
180    } else if ((msg_type & VULKAN_LOADER_DEBUG_BIT) != 0) {
181        STRNCAT_TO_BUFFER("DEBUG");
182    }
183
184    if ((msg_type & VULKAN_LOADER_PERF_BIT) != 0) {
185        if (num_used > 1) {
186            STRNCAT_TO_BUFFER(" | ");
187        }
188        STRNCAT_TO_BUFFER("PERF");
189    }
190    if ((msg_type & VULKAN_LOADER_DRIVER_BIT) != 0) {
191        if (num_used > 1) {
192            STRNCAT_TO_BUFFER(" | ");
193        }
194        STRNCAT_TO_BUFFER("DRIVER");
195    }
196    if ((msg_type & VULKAN_LOADER_LAYER_BIT) != 0) {
197        if (num_used > 1) {
198            STRNCAT_TO_BUFFER(" | ");
199        }
200        STRNCAT_TO_BUFFER("LAYER");
201    }
202
203    // Add a ": " to separate the filters from the message
204    STRNCAT_TO_BUFFER(": ");
205#undef STRNCAT_TO_BUFFER
206
207    // Justifies the output to at least 19 spaces
208    if (num_used < 19) {
209        const char *space_buffer = "                   ";
210        // Only write (19 - num_used) spaces
211        loader_strncat(cmd_line_msg + num_used, cmd_line_size - num_used, space_buffer, 19 - num_used);
212        num_used += sizeof(space_buffer) - 1 - num_used;
213    }
214    // Assert that we didn't write more than what is available in cmd_line_msg
215    assert(cmd_line_size > num_used);
216
217#if !defined (__OHOS__)
218    fputs(cmd_line_msg, stderr);
219    fputs(msg, stderr);
220    fputc('\n', stderr);
221#endif
222#if defined(WIN32)
223    OutputDebugString(cmd_line_msg);
224    OutputDebugString(msg);
225    OutputDebugString("\n");
226#endif
227
228#if defined(__OHOS__)
229    char result[512 + 64];
230    strcpy(result, cmd_line_msg);
231    strcat(result, msg);
232    OpenHarmonyLog(msg_type, result);
233#endif
234}
235
236void loader_log_asm_function_not_supported(const struct loader_instance *inst, VkFlags msg_type, int32_t msg_code,
237                                           const char *func_name) {
238    loader_log(inst, msg_type, msg_code, "Function %s not supported for this physical device", func_name);
239}
240