/src/vulkan-loader/loader/log.c
Line | Count | Source |
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 | | |
40 | | uint32_t g_loader_debug = 0; |
41 | | |
42 | 2 | void loader_init_global_debug_level(void) { |
43 | 2 | char *env; |
44 | 2 | char *orig; |
45 | | |
46 | 2 | if (g_loader_debug > 0) return; |
47 | | |
48 | 2 | g_loader_debug = 0; |
49 | | |
50 | | // Parse comma-separated debug options |
51 | 2 | orig = env = loader_getenv("VK_LOADER_DEBUG", NULL); |
52 | 2 | while (env) { |
53 | 0 | char *p = strchr(env, ','); |
54 | 0 | size_t len; |
55 | |
|
56 | 0 | if (p) { |
57 | 0 | len = p - env; |
58 | 0 | } else { |
59 | 0 | len = strlen(env); |
60 | 0 | } |
61 | |
|
62 | 0 | if (len > 0) { |
63 | 0 | if (strncmp(env, "all", len) == 0) { |
64 | 0 | g_loader_debug = ~0u; |
65 | 0 | } else if (strncmp(env, "warn", len) == 0) { |
66 | 0 | g_loader_debug |= VULKAN_LOADER_WARN_BIT; |
67 | 0 | } else if (strncmp(env, "info", len) == 0) { |
68 | 0 | g_loader_debug |= VULKAN_LOADER_INFO_BIT; |
69 | 0 | } else if (strncmp(env, "perf", len) == 0) { |
70 | 0 | g_loader_debug |= VULKAN_LOADER_PERF_BIT; |
71 | 0 | } else if (strncmp(env, "error", len) == 0) { |
72 | 0 | g_loader_debug |= VULKAN_LOADER_ERROR_BIT; |
73 | 0 | } else if (strncmp(env, "debug", len) == 0) { |
74 | 0 | g_loader_debug |= VULKAN_LOADER_DEBUG_BIT; |
75 | 0 | } else if (strncmp(env, "layer", len) == 0) { |
76 | 0 | g_loader_debug |= VULKAN_LOADER_LAYER_BIT; |
77 | 0 | } else if (strncmp(env, "driver", len) == 0 || strncmp(env, "implem", len) == 0 || strncmp(env, "icd", len) == 0) { |
78 | 0 | g_loader_debug |= VULKAN_LOADER_DRIVER_BIT; |
79 | 0 | } |
80 | 0 | } |
81 | |
|
82 | 0 | if (!p) break; |
83 | | |
84 | 0 | env = p + 1; |
85 | 0 | } |
86 | | |
87 | 2 | loader_free_getenv(orig, NULL); |
88 | 2 | } |
89 | | |
90 | 125 | void loader_set_global_debug_level(uint32_t new_loader_debug) { g_loader_debug = new_loader_debug; } |
91 | | |
92 | | // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) - msg_type/cmd_line_size are convertible types, always passed in this order |
93 | 1.06k | void generate_debug_flag_str(VkFlags msg_type, size_t cmd_line_size, char *cmd_line_msg) { |
94 | 1.06k | cmd_line_msg[0] = '\0'; |
95 | | |
96 | 1.06k | if ((msg_type & VULKAN_LOADER_ERROR_BIT) != 0) { |
97 | 31 | loader_strncat(cmd_line_msg, cmd_line_size, "ERROR", sizeof("ERROR")); |
98 | 31 | } |
99 | 1.06k | if ((msg_type & VULKAN_LOADER_WARN_BIT) != 0) { |
100 | 51 | if (strlen(cmd_line_msg) > 0) { |
101 | 25 | loader_strncat(cmd_line_msg, cmd_line_size, " | ", sizeof(" | ")); |
102 | 25 | } |
103 | 51 | loader_strncat(cmd_line_msg, cmd_line_size, "WARNING", sizeof("WARNING")); |
104 | 51 | } |
105 | 1.06k | if ((msg_type & VULKAN_LOADER_INFO_BIT) != 0) { |
106 | 38 | if (strlen(cmd_line_msg) > 0) { |
107 | 26 | loader_strncat(cmd_line_msg, cmd_line_size, " | ", sizeof(" | ")); |
108 | 26 | } |
109 | 38 | loader_strncat(cmd_line_msg, cmd_line_size, "INFO", sizeof("INFO")); |
110 | 38 | } |
111 | 1.06k | if ((msg_type & VULKAN_LOADER_DEBUG_BIT) != 0) { |
112 | 47 | if (strlen(cmd_line_msg) > 0) { |
113 | 34 | loader_strncat(cmd_line_msg, cmd_line_size, " | ", sizeof(" | ")); |
114 | 34 | } |
115 | 47 | loader_strncat(cmd_line_msg, cmd_line_size, "DEBUG", sizeof("DEBUG")); |
116 | 47 | } |
117 | 1.06k | if ((msg_type & VULKAN_LOADER_PERF_BIT) != 0) { |
118 | 43 | if (strlen(cmd_line_msg) > 0) { |
119 | 33 | loader_strncat(cmd_line_msg, cmd_line_size, " | ", sizeof(" | ")); |
120 | 33 | } |
121 | 43 | loader_strncat(cmd_line_msg, cmd_line_size, "PERF", sizeof("PERF")); |
122 | 43 | } |
123 | 1.06k | if ((msg_type & VULKAN_LOADER_DRIVER_BIT) != 0) { |
124 | 48 | if (strlen(cmd_line_msg) > 0) { |
125 | 36 | loader_strncat(cmd_line_msg, cmd_line_size, " | ", sizeof(" | ")); |
126 | 36 | } |
127 | 48 | loader_strncat(cmd_line_msg, cmd_line_size, "DRIVER", sizeof("DRIVER")); |
128 | 48 | } |
129 | 1.06k | if ((msg_type & VULKAN_LOADER_LAYER_BIT) != 0) { |
130 | 50 | if (strlen(cmd_line_msg) > 0) { |
131 | 37 | loader_strncat(cmd_line_msg, cmd_line_size, " | ", sizeof(" | ")); |
132 | 37 | } |
133 | 50 | loader_strncat(cmd_line_msg, cmd_line_size, "LAYER", sizeof("LAYER")); |
134 | 50 | } |
135 | | |
136 | 1.06k | #undef STRNCAT_TO_BUFFER |
137 | 1.06k | } |
138 | | |
139 | | void DECORATE_PRINTF(4, 5) |
140 | | // NOLINTNEXTLINE(bugprone-easily-swappable-parameters) - msg_type/msg_code (unused) are convertible types; public API order |
141 | 619k | loader_log(const struct loader_instance *inst, VkFlags msg_type, int32_t msg_code, const char *format, ...) { |
142 | 619k | (void)msg_code; |
143 | 619k | char msg[512] = {0}; |
144 | | |
145 | 619k | va_list ap; |
146 | 619k | va_start(ap, format); |
147 | 619k | int ret = vsnprintf(msg, sizeof(msg), format, ap); |
148 | 619k | if ((ret >= (int)sizeof(msg)) || ret < 0) { |
149 | 482 | msg[sizeof(msg) - 1] = '\0'; |
150 | 482 | } |
151 | 619k | va_end(ap); |
152 | | |
153 | 619k | if (inst) { |
154 | | // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange) - external Vulkan enum, has no zero value we can add |
155 | 0 | VkDebugUtilsMessageSeverityFlagBitsEXT severity = 0; |
156 | 0 | VkDebugUtilsMessageTypeFlagsEXT type = 0; |
157 | 0 | VkDebugUtilsMessengerCallbackDataEXT callback_data = {0}; |
158 | 0 | VkDebugUtilsObjectNameInfoEXT object_name = {0}; |
159 | | |
160 | | // NOLINTNEXTLINE(bugprone-branch-clone) - duplicated so combined bit calls (e.g. WARN_BIT|LAYER_BIT) match first branch |
161 | 0 | if ((msg_type & VULKAN_LOADER_INFO_BIT) != 0) { |
162 | 0 | severity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT; |
163 | 0 | } else if ((msg_type & VULKAN_LOADER_WARN_BIT) != 0) { |
164 | 0 | severity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT; |
165 | 0 | } else if ((msg_type & VULKAN_LOADER_ERROR_BIT) != 0) { |
166 | 0 | severity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; |
167 | 0 | } else if ((msg_type & VULKAN_LOADER_DEBUG_BIT) != 0) { |
168 | 0 | severity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT; |
169 | 0 | } else if ((msg_type & VULKAN_LOADER_LAYER_BIT) != 0 || (msg_type & VULKAN_LOADER_DRIVER_BIT) != 0) { |
170 | | // Just driver or just layer bit should be treated as an info message in debug utils. |
171 | 0 | severity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT; |
172 | 0 | } |
173 | |
|
174 | 0 | if ((msg_type & VULKAN_LOADER_PERF_BIT) != 0) { |
175 | 0 | type = VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; |
176 | 0 | } else if ((msg_type & VULKAN_LOADER_VALIDATION_BIT) != 0) { |
177 | | // For loader logging, if it's a validation message, we still want to also keep the general flag as well |
178 | | // so messages of type validation can still be triggered for general message callbacks. |
179 | 0 | type = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT; |
180 | 0 | } else { |
181 | 0 | type = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT; |
182 | 0 | } |
183 | |
|
184 | 0 | callback_data.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CALLBACK_DATA_EXT; |
185 | 0 | callback_data.pMessageIdName = "Loader Message"; |
186 | 0 | callback_data.pMessage = msg; |
187 | 0 | callback_data.objectCount = 1; |
188 | 0 | callback_data.pObjects = &object_name; |
189 | 0 | object_name.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT; |
190 | 0 | object_name.objectType = VK_OBJECT_TYPE_INSTANCE; |
191 | 0 | object_name.objectHandle = (uint64_t)(uintptr_t)inst; |
192 | |
|
193 | 0 | util_SubmitDebugUtilsMessageEXT(inst, severity, type, &callback_data); |
194 | 0 | } |
195 | | |
196 | | // Always log to stderr if this is a fatal error |
197 | 619k | if (0 == (msg_type & VULKAN_LOADER_FATAL_ERROR_BIT)) { |
198 | 619k | if (inst && inst->settings.settings_active && inst->settings.debug_level > 0) { |
199 | | // Exit early if the current instance settings have some debugging options but do match the current msg_type |
200 | 0 | if (0 == (msg_type & inst->settings.debug_level)) { |
201 | 0 | return; |
202 | 0 | } |
203 | | // Check the global settings and if that doesn't say to skip, check the environment variable |
204 | 619k | } else if (0 == (msg_type & g_loader_debug)) { |
205 | 226k | return; |
206 | 226k | } |
207 | 619k | } |
208 | | |
209 | | #if defined(DEBUG) |
210 | | int debug_flag_mask = |
211 | | (int)(msg_type & (VULKAN_LOADER_ERROR_BIT | VULKAN_LOADER_WARN_BIT | VULKAN_LOADER_INFO_BIT | VULKAN_LOADER_DEBUG_BIT)); |
212 | | assert((debug_flag_mask == 0 || debug_flag_mask == VULKAN_LOADER_ERROR_BIT || debug_flag_mask == VULKAN_LOADER_WARN_BIT || |
213 | | debug_flag_mask == VULKAN_LOADER_INFO_BIT || debug_flag_mask == VULKAN_LOADER_DEBUG_BIT) && |
214 | | "This log has more than one exclusive debug flags (error, warn, info, debug) set"); |
215 | | #endif |
216 | | |
217 | | // Only need enough space to create the filter description header for log messages |
218 | | // Also use the same header for all output |
219 | 392k | char cmd_line_msg[64] = {0}; |
220 | 392k | size_t cmd_line_size = sizeof(cmd_line_msg); |
221 | | |
222 | 392k | loader_strncat(cmd_line_msg, cmd_line_size, "[Vulkan Loader] ", sizeof("[Vulkan Loader] ")); |
223 | | |
224 | 392k | bool need_separator = false; |
225 | 392k | if ((msg_type & VULKAN_LOADER_ERROR_BIT) != 0) { |
226 | 3.54k | loader_strncat(cmd_line_msg, cmd_line_size, "ERROR", sizeof("ERROR")); |
227 | 3.54k | need_separator = true; |
228 | 388k | } else if ((msg_type & VULKAN_LOADER_WARN_BIT) != 0) { |
229 | 132k | loader_strncat(cmd_line_msg, cmd_line_size, "WARNING", sizeof("WARNING")); |
230 | 132k | need_separator = true; |
231 | 256k | } else if ((msg_type & VULKAN_LOADER_INFO_BIT) != 0) { |
232 | 54.1k | loader_strncat(cmd_line_msg, cmd_line_size, "INFO", sizeof("INFO")); |
233 | 54.1k | need_separator = true; |
234 | 202k | } else if ((msg_type & VULKAN_LOADER_DEBUG_BIT) != 0) { |
235 | 130k | loader_strncat(cmd_line_msg, cmd_line_size, "DEBUG", sizeof("DEBUG")); |
236 | 130k | need_separator = true; |
237 | 130k | } |
238 | | |
239 | 392k | if ((msg_type & VULKAN_LOADER_PERF_BIT) != 0) { |
240 | 0 | if (need_separator) { |
241 | 0 | loader_strncat(cmd_line_msg, cmd_line_size, " | ", sizeof(" | ")); |
242 | 0 | } |
243 | 0 | loader_strncat(cmd_line_msg, cmd_line_size, "PERF", sizeof("PERF")); |
244 | 392k | } else if ((msg_type & VULKAN_LOADER_DRIVER_BIT) != 0) { |
245 | 0 | if (need_separator) { |
246 | 0 | loader_strncat(cmd_line_msg, cmd_line_size, " | ", sizeof(" | ")); |
247 | 0 | } |
248 | 0 | loader_strncat(cmd_line_msg, cmd_line_size, "DRIVER", sizeof("DRIVER")); |
249 | 392k | } else if ((msg_type & VULKAN_LOADER_LAYER_BIT) != 0) { |
250 | 140k | if (need_separator) { |
251 | 68.8k | loader_strncat(cmd_line_msg, cmd_line_size, " | ", sizeof(" | ")); |
252 | 68.8k | } |
253 | 140k | loader_strncat(cmd_line_msg, cmd_line_size, "LAYER", sizeof("LAYER")); |
254 | 140k | } |
255 | | |
256 | 392k | loader_strncat(cmd_line_msg, cmd_line_size, ": ", sizeof(": ")); |
257 | 392k | size_t num_used = strlen(cmd_line_msg); |
258 | | |
259 | | // Justifies the output to at least 29 spaces |
260 | 392k | if (num_used < 32) { |
261 | 374k | const char space_buffer[] = " "; |
262 | | // Only write (32 - num_used) spaces |
263 | 374k | loader_strncat(cmd_line_msg, cmd_line_size, space_buffer, sizeof(space_buffer) - 1 - num_used); |
264 | 374k | } |
265 | | // Assert that we didn't write more than what is available in cmd_line_msg |
266 | 392k | assert(cmd_line_size > num_used); |
267 | | |
268 | | // NOLINTBEGIN(cert-err33-c) - this is the logger itself; no sane recovery from a failed stderr write |
269 | | //fputs(cmd_line_msg, stderr); |
270 | | //fputs(msg, stderr); |
271 | | //fputc('\n', stderr); |
272 | | // NOLINTEND(cert-err33-c) |
273 | | #if defined(WIN32) |
274 | | OutputDebugString(cmd_line_msg); |
275 | | OutputDebugString(msg); |
276 | | OutputDebugString("\n"); |
277 | | #endif |
278 | 392k | } |
279 | | |
280 | | void loader_log_asm_function_not_supported(const struct loader_instance *inst, VkFlags msg_type, int32_t msg_code, |
281 | 0 | const char *func_name) { |
282 | 0 | loader_log(inst, msg_type, msg_code, "Function %s not supported for this physical device", func_name); |
283 | 0 | } |
284 | | |
285 | 0 | void loader_log_generate_uuid_string(const uint8_t uuid[16], char output[UUID_STR_LEN]) { |
286 | 0 | assert(uuid && output); |
287 | | // NOLINTNEXTLINE(cert-err33-c) - fixed format string and fixed-size output buffer sized to fit exactly; cannot fail or truncate |
288 | 0 | snprintf(output, UUID_STR_LEN, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x", uuid[0], uuid[1], |
289 | 0 | uuid[2], uuid[3], uuid[4], uuid[5], uuid[6], uuid[7], uuid[8], uuid[9], uuid[10], uuid[11], uuid[12], uuid[13], |
290 | 0 | uuid[14], uuid[15]); |
291 | 0 | } |