15db71995Sopenharmony_ci/*
25db71995Sopenharmony_ci *
35db71995Sopenharmony_ci * Copyright (c) 2014-2022 The Khronos Group Inc.
45db71995Sopenharmony_ci * Copyright (c) 2014-2022 Valve Corporation
55db71995Sopenharmony_ci * Copyright (c) 2014-2022 LunarG, Inc.
65db71995Sopenharmony_ci *
75db71995Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
85db71995Sopenharmony_ci * you may not use this file except in compliance with the License.
95db71995Sopenharmony_ci * You may obtain a copy of the License at
105db71995Sopenharmony_ci *
115db71995Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
125db71995Sopenharmony_ci *
135db71995Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
145db71995Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
155db71995Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
165db71995Sopenharmony_ci * See the License for the specific language governing permissions and
175db71995Sopenharmony_ci * limitations under the License.
185db71995Sopenharmony_ci *
195db71995Sopenharmony_ci * Author: Jon Ashburn <jon@lunarg.com>
205db71995Sopenharmony_ci * Author: Courtney Goeltzenleuchter <courtney@LunarG.com>
215db71995Sopenharmony_ci * Author: Chia-I Wu <olvaffe@gmail.com>
225db71995Sopenharmony_ci * Author: Chia-I Wu <olv@lunarg.com>
235db71995Sopenharmony_ci * Author: Mark Lobodzinski <mark@LunarG.com>
245db71995Sopenharmony_ci * Author: Lenny Komow <lenny@lunarg.com>
255db71995Sopenharmony_ci * Author: Charles Giessen <charles@lunarg.com>
265db71995Sopenharmony_ci *
275db71995Sopenharmony_ci */
285db71995Sopenharmony_ci
295db71995Sopenharmony_ci#include "log.h"
305db71995Sopenharmony_ci
315db71995Sopenharmony_ci#include <stdio.h>
325db71995Sopenharmony_ci#include <stdarg.h>
335db71995Sopenharmony_ci
345db71995Sopenharmony_ci#include "debug_utils.h"
355db71995Sopenharmony_ci#include "loader_common.h"
365db71995Sopenharmony_ci#include "loader_environment.h"
375db71995Sopenharmony_ci#include "settings.h"
385db71995Sopenharmony_ci#include "vk_loader_platform.h"
395db71995Sopenharmony_ci#ifdef VK_USE_PLATFORM_OHOS
405db71995Sopenharmony_ci#include "loader_hilog.h"
415db71995Sopenharmony_ci#endif
425db71995Sopenharmony_ci
435db71995Sopenharmony_ciuint32_t g_loader_debug = ~0u;
445db71995Sopenharmony_ci
455db71995Sopenharmony_civoid loader_init_global_debug_level(void) {
465db71995Sopenharmony_ci    char *env, *orig;
475db71995Sopenharmony_ci
485db71995Sopenharmony_ci    if (g_loader_debug > 0) return;
495db71995Sopenharmony_ci
505db71995Sopenharmony_ci    g_loader_debug = 0;
515db71995Sopenharmony_ci
525db71995Sopenharmony_ci    // Parse comma-separated debug options
535db71995Sopenharmony_ci    orig = env = loader_getenv("VK_LOADER_DEBUG", NULL);
545db71995Sopenharmony_ci    while (env) {
555db71995Sopenharmony_ci        char *p = strchr(env, ',');
565db71995Sopenharmony_ci        size_t len;
575db71995Sopenharmony_ci
585db71995Sopenharmony_ci        if (p) {
595db71995Sopenharmony_ci            len = p - env;
605db71995Sopenharmony_ci        } else {
615db71995Sopenharmony_ci            len = strlen(env);
625db71995Sopenharmony_ci        }
635db71995Sopenharmony_ci
645db71995Sopenharmony_ci        if (len > 0) {
655db71995Sopenharmony_ci            if (strncmp(env, "all", len) == 0) {
665db71995Sopenharmony_ci                g_loader_debug = ~0u;
675db71995Sopenharmony_ci            } else if (strncmp(env, "warn", len) == 0) {
685db71995Sopenharmony_ci                g_loader_debug |= VULKAN_LOADER_WARN_BIT;
695db71995Sopenharmony_ci            } else if (strncmp(env, "info", len) == 0) {
705db71995Sopenharmony_ci                g_loader_debug |= VULKAN_LOADER_INFO_BIT;
715db71995Sopenharmony_ci            } else if (strncmp(env, "perf", len) == 0) {
725db71995Sopenharmony_ci                g_loader_debug |= VULKAN_LOADER_PERF_BIT;
735db71995Sopenharmony_ci            } else if (strncmp(env, "error", len) == 0) {
745db71995Sopenharmony_ci                g_loader_debug |= VULKAN_LOADER_ERROR_BIT;
755db71995Sopenharmony_ci            } else if (strncmp(env, "debug", len) == 0) {
765db71995Sopenharmony_ci                g_loader_debug |= VULKAN_LOADER_DEBUG_BIT;
775db71995Sopenharmony_ci            } else if (strncmp(env, "layer", len) == 0) {
785db71995Sopenharmony_ci                g_loader_debug |= VULKAN_LOADER_LAYER_BIT;
795db71995Sopenharmony_ci            } else if (strncmp(env, "driver", len) == 0 || strncmp(env, "implem", len) == 0 || strncmp(env, "icd", len) == 0) {
805db71995Sopenharmony_ci                g_loader_debug |= VULKAN_LOADER_DRIVER_BIT;
815db71995Sopenharmony_ci            }
825db71995Sopenharmony_ci        }
835db71995Sopenharmony_ci
845db71995Sopenharmony_ci        if (!p) break;
855db71995Sopenharmony_ci
865db71995Sopenharmony_ci        env = p + 1;
875db71995Sopenharmony_ci    }
885db71995Sopenharmony_ci
895db71995Sopenharmony_ci    loader_free_getenv(orig, NULL);
905db71995Sopenharmony_ci}
915db71995Sopenharmony_ci
925db71995Sopenharmony_civoid loader_set_global_debug_level(uint32_t new_loader_debug) { g_loader_debug = new_loader_debug; }
935db71995Sopenharmony_ci
945db71995Sopenharmony_civoid loader_log(const struct loader_instance *inst, VkFlags msg_type, int32_t msg_code, const char *format, ...) {
955db71995Sopenharmony_ci    (void)msg_code;
965db71995Sopenharmony_ci    char msg[512] = {0};
975db71995Sopenharmony_ci
985db71995Sopenharmony_ci    va_list ap;
995db71995Sopenharmony_ci    va_start(ap, format);
1005db71995Sopenharmony_ci    int ret = vsnprintf(msg, sizeof(msg), format, ap);
1015db71995Sopenharmony_ci    if ((ret >= (int)sizeof(msg)) || ret < 0) {
1025db71995Sopenharmony_ci        msg[sizeof(msg) - 1] = '\0';
1035db71995Sopenharmony_ci    }
1045db71995Sopenharmony_ci    va_end(ap);
1055db71995Sopenharmony_ci
1065db71995Sopenharmony_ci    if (inst) {
1075db71995Sopenharmony_ci        VkDebugUtilsMessageSeverityFlagBitsEXT severity = 0;
1085db71995Sopenharmony_ci        VkDebugUtilsMessageTypeFlagsEXT type = 0;
1095db71995Sopenharmony_ci        VkDebugUtilsMessengerCallbackDataEXT callback_data = {0};
1105db71995Sopenharmony_ci        VkDebugUtilsObjectNameInfoEXT object_name = {0};
1115db71995Sopenharmony_ci
1125db71995Sopenharmony_ci        if ((msg_type & VULKAN_LOADER_INFO_BIT) != 0) {
1135db71995Sopenharmony_ci            severity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT;
1145db71995Sopenharmony_ci        } else if ((msg_type & VULKAN_LOADER_WARN_BIT) != 0) {
1155db71995Sopenharmony_ci            severity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT;
1165db71995Sopenharmony_ci        } else if ((msg_type & VULKAN_LOADER_ERROR_BIT) != 0) {
1175db71995Sopenharmony_ci            severity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
1185db71995Sopenharmony_ci        } else if ((msg_type & VULKAN_LOADER_DEBUG_BIT) != 0) {
1195db71995Sopenharmony_ci            severity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT;
1205db71995Sopenharmony_ci        } else if ((msg_type & VULKAN_LOADER_LAYER_BIT) != 0 || (msg_type & VULKAN_LOADER_DRIVER_BIT) != 0) {
1215db71995Sopenharmony_ci            // Just driver or just layer bit should be treated as an info message in debug utils.
1225db71995Sopenharmony_ci            severity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT;
1235db71995Sopenharmony_ci        }
1245db71995Sopenharmony_ci
1255db71995Sopenharmony_ci        if ((msg_type & VULKAN_LOADER_PERF_BIT) != 0) {
1265db71995Sopenharmony_ci            type = VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
1275db71995Sopenharmony_ci        } else if ((msg_type & VULKAN_LOADER_VALIDATION_BIT) != 0) {
1285db71995Sopenharmony_ci            // For loader logging, if it's a validation message, we still want to also keep the general flag as well
1295db71995Sopenharmony_ci            // so messages of type validation can still be triggered for general message callbacks.
1305db71995Sopenharmony_ci            type = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT;
1315db71995Sopenharmony_ci        } else {
1325db71995Sopenharmony_ci            type = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT;
1335db71995Sopenharmony_ci        }
1345db71995Sopenharmony_ci
1355db71995Sopenharmony_ci        callback_data.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CALLBACK_DATA_EXT;
1365db71995Sopenharmony_ci        callback_data.pMessageIdName = "Loader Message";
1375db71995Sopenharmony_ci        callback_data.pMessage = msg;
1385db71995Sopenharmony_ci        callback_data.objectCount = 1;
1395db71995Sopenharmony_ci        callback_data.pObjects = &object_name;
1405db71995Sopenharmony_ci        object_name.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
1415db71995Sopenharmony_ci        object_name.objectType = VK_OBJECT_TYPE_INSTANCE;
1425db71995Sopenharmony_ci        object_name.objectHandle = (uint64_t)(uintptr_t)inst;
1435db71995Sopenharmony_ci
1445db71995Sopenharmony_ci        util_SubmitDebugUtilsMessageEXT(inst, severity, type, &callback_data);
1455db71995Sopenharmony_ci    }
1465db71995Sopenharmony_ci
1475db71995Sopenharmony_ci    // Always log to stderr if this is a fatal error
1485db71995Sopenharmony_ci    if (0 == (msg_type & VULKAN_LOADER_FATAL_ERROR_BIT)) {
1495db71995Sopenharmony_ci        // Exit early if the current instance settings do not ask for logging to stderr
1505db71995Sopenharmony_ci        if (inst && inst->settings.settings_active && 0 == (msg_type & inst->settings.debug_level)) {
1515db71995Sopenharmony_ci            return;
1525db71995Sopenharmony_ci            // Check the global settings and if that doesn't say to skip, check the environment variable
1535db71995Sopenharmony_ci        } else if (0 == (msg_type & g_loader_debug)) {
1545db71995Sopenharmony_ci            return;
1555db71995Sopenharmony_ci        }
1565db71995Sopenharmony_ci    }
1575db71995Sopenharmony_ci
1585db71995Sopenharmony_ci    // Only need enough space to create the filter description header for log messages
1595db71995Sopenharmony_ci    // Also use the same header for all output
1605db71995Sopenharmony_ci    char cmd_line_msg[64];
1615db71995Sopenharmony_ci    size_t cmd_line_size = sizeof(cmd_line_msg);
1625db71995Sopenharmony_ci    size_t num_used = 0;
1635db71995Sopenharmony_ci
1645db71995Sopenharmony_ci    cmd_line_msg[0] = '\0';
1655db71995Sopenharmony_ci
1665db71995Sopenharmony_ci// Helper macro which strncat's the given string literal, then updates num_used & cmd_line_end
1675db71995Sopenharmony_ci// Assumes that we haven't used the entire buffer - must manually check this when adding new filter types
1685db71995Sopenharmony_ci// We concat at the end of cmd_line_msg, so that strncat isn't a victim of Schlemiel the Painter
1695db71995Sopenharmony_ci// We write to the end - 1 of cmd_line_msg, as the end is actually a null terminator
1705db71995Sopenharmony_ci#define STRNCAT_TO_BUFFER(string_literal_to_cat)                                                                             \
1715db71995Sopenharmony_ci    loader_strncat(cmd_line_msg + num_used, cmd_line_size - num_used, string_literal_to_cat, sizeof(string_literal_to_cat)); \
1725db71995Sopenharmony_ci    num_used += sizeof(string_literal_to_cat) - 1;  // subtract one to remove the null terminator in the string literal
1735db71995Sopenharmony_ci
1745db71995Sopenharmony_ci    if ((msg_type & VULKAN_LOADER_ERROR_BIT) != 0) {
1755db71995Sopenharmony_ci        STRNCAT_TO_BUFFER("ERROR");
1765db71995Sopenharmony_ci    } else if ((msg_type & VULKAN_LOADER_WARN_BIT) != 0) {
1775db71995Sopenharmony_ci        STRNCAT_TO_BUFFER("WARNING");
1785db71995Sopenharmony_ci    } else if ((msg_type & VULKAN_LOADER_INFO_BIT) != 0) {
1795db71995Sopenharmony_ci        STRNCAT_TO_BUFFER("INFO");
1805db71995Sopenharmony_ci    } else if ((msg_type & VULKAN_LOADER_DEBUG_BIT) != 0) {
1815db71995Sopenharmony_ci        STRNCAT_TO_BUFFER("DEBUG");
1825db71995Sopenharmony_ci    }
1835db71995Sopenharmony_ci
1845db71995Sopenharmony_ci    if ((msg_type & VULKAN_LOADER_PERF_BIT) != 0) {
1855db71995Sopenharmony_ci        if (num_used > 1) {
1865db71995Sopenharmony_ci            STRNCAT_TO_BUFFER(" | ");
1875db71995Sopenharmony_ci        }
1885db71995Sopenharmony_ci        STRNCAT_TO_BUFFER("PERF");
1895db71995Sopenharmony_ci    }
1905db71995Sopenharmony_ci    if ((msg_type & VULKAN_LOADER_DRIVER_BIT) != 0) {
1915db71995Sopenharmony_ci        if (num_used > 1) {
1925db71995Sopenharmony_ci            STRNCAT_TO_BUFFER(" | ");
1935db71995Sopenharmony_ci        }
1945db71995Sopenharmony_ci        STRNCAT_TO_BUFFER("DRIVER");
1955db71995Sopenharmony_ci    }
1965db71995Sopenharmony_ci    if ((msg_type & VULKAN_LOADER_LAYER_BIT) != 0) {
1975db71995Sopenharmony_ci        if (num_used > 1) {
1985db71995Sopenharmony_ci            STRNCAT_TO_BUFFER(" | ");
1995db71995Sopenharmony_ci        }
2005db71995Sopenharmony_ci        STRNCAT_TO_BUFFER("LAYER");
2015db71995Sopenharmony_ci    }
2025db71995Sopenharmony_ci
2035db71995Sopenharmony_ci    // Add a ": " to separate the filters from the message
2045db71995Sopenharmony_ci    STRNCAT_TO_BUFFER(": ");
2055db71995Sopenharmony_ci#undef STRNCAT_TO_BUFFER
2065db71995Sopenharmony_ci
2075db71995Sopenharmony_ci    // Justifies the output to at least 19 spaces
2085db71995Sopenharmony_ci    if (num_used < 19) {
2095db71995Sopenharmony_ci        const char *space_buffer = "                   ";
2105db71995Sopenharmony_ci        // Only write (19 - num_used) spaces
2115db71995Sopenharmony_ci        loader_strncat(cmd_line_msg + num_used, cmd_line_size - num_used, space_buffer, 19 - num_used);
2125db71995Sopenharmony_ci        num_used += sizeof(space_buffer) - 1 - num_used;
2135db71995Sopenharmony_ci    }
2145db71995Sopenharmony_ci    // Assert that we didn't write more than what is available in cmd_line_msg
2155db71995Sopenharmony_ci    assert(cmd_line_size > num_used);
2165db71995Sopenharmony_ci
2175db71995Sopenharmony_ci#if !defined (__OHOS__)
2185db71995Sopenharmony_ci    fputs(cmd_line_msg, stderr);
2195db71995Sopenharmony_ci    fputs(msg, stderr);
2205db71995Sopenharmony_ci    fputc('\n', stderr);
2215db71995Sopenharmony_ci#endif
2225db71995Sopenharmony_ci#if defined(WIN32)
2235db71995Sopenharmony_ci    OutputDebugString(cmd_line_msg);
2245db71995Sopenharmony_ci    OutputDebugString(msg);
2255db71995Sopenharmony_ci    OutputDebugString("\n");
2265db71995Sopenharmony_ci#endif
2275db71995Sopenharmony_ci
2285db71995Sopenharmony_ci#if defined(__OHOS__)
2295db71995Sopenharmony_ci    char result[512 + 64];
2305db71995Sopenharmony_ci    strcpy(result, cmd_line_msg);
2315db71995Sopenharmony_ci    strcat(result, msg);
2325db71995Sopenharmony_ci    OpenHarmonyLog(msg_type, result);
2335db71995Sopenharmony_ci#endif
2345db71995Sopenharmony_ci}
2355db71995Sopenharmony_ci
2365db71995Sopenharmony_civoid loader_log_asm_function_not_supported(const struct loader_instance *inst, VkFlags msg_type, int32_t msg_code,
2375db71995Sopenharmony_ci                                           const char *func_name) {
2385db71995Sopenharmony_ci    loader_log(inst, msg_type, msg_code, "Function %s not supported for this physical device", func_name);
2395db71995Sopenharmony_ci}
240