master ohmu/fastfetch-openbsd / src / common / impl / debug.c
ohmu  ·  2026-10-15
  1#include <errno.h>
  2
  3#ifndef NDEBUG
  4
  5    #include "common/debug.h"
  6    #include "common/thread.h"
  7
  8[[gnu::nonnull(1), gnu::pure, gnu::always_inline, nodiscard]]
  9static inline const char* ffFindFileName(const char* file) {
 10    const char* lastSlash = __builtin_strrchr(file, '/');
 11    #ifdef _WIN32
 12    if (lastSlash == nullptr) {
 13        lastSlash = __builtin_strrchr(file, '\\');
 14    }
 15    #endif
 16    if (lastSlash != nullptr) {
 17        return lastSlash + 1;
 18    }
 19    return file;
 20}
 21
 22#if __GNUC__ && !__clang__
 23[[gnu::optimize("O2")]]
 24#endif
 25void ffDebugPrint(const char* file, int line, const char* format, ...) {
 26    if (!instance.config.display.debugMode) {
 27        return;
 28    }
 29
 30    static FFThreadMutex debugMutex = FF_THREAD_MUTEX_INITIALIZER;
 31    // Saved before the lock and restored after the unlock: FF_DEBUG() is called from error paths
 32    // that go on to read `errno`, so nothing this function does -- neither the lock nor the
 33    // formatting -- may be allowed to leave its own value behind.
 34    int errno_ = errno;
 35    ffThreadMutexLock(&debugMutex);
 36    static char errmsg_[1024]; // Don't increase stack size
 37    va_list args;
 38    va_start(args, format);
 39    vsnprintf(errmsg_, sizeof(errmsg_), format, args);
 40    va_end(args);
 41    fprintf(stderr, "[%s%4d, %s] %s\n", ffFindFileName(file), line, ffTimeToTimeStr(ffTimeGetNow()), errmsg_);
 42    ffThreadMutexUnlock(&debugMutex);
 43    errno = errno_;
 44}
 45
 46    #if _WIN32
 47        #include "common/windows/nt.h"
 48
 49        #include <windows.h>
 50        #include <cfgmgr32.h>
 51
 52const char* ffDebugWin32Error(DWORD errorCode) {
 53    static char buffer[512];
 54
 55    wchar_t bufferW[256];
 56    ULONG len = FormatMessageW(
 57        FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
 58        nullptr,
 59        (DWORD) errorCode,
 60        0,
 61        bufferW,
 62        ARRAY_SIZE(bufferW),
 63        nullptr);
 64
 65    if (len == 0) {
 66        snprintf(buffer, sizeof(buffer), "Unknown error code (%lu)", errorCode);
 67    } else {
 68        // Remove trailing newline
 69        while (len > 0 && (bufferW[len - 1] == '\r' || bufferW[len - 1] == '\n')) {
 70            --len;
 71        }
 72
 73        if (NT_SUCCESS(RtlUnicodeToUTF8N(buffer, sizeof(buffer), &len, bufferW, len * sizeof(wchar_t)))) {
 74            snprintf(buffer + len, sizeof(buffer) - len, " (%lu)", errorCode);
 75        } else {
 76            snprintf(buffer, sizeof(buffer), "Unknown error (%lu)", errorCode);
 77        }
 78    }
 79
 80    return buffer;
 81}
 82
 83const char* ffDebugConfigRet(CONFIGRET ret) {
 84    return ffDebugWin32Error(CM_MapCrToWin32Err(ret, ERROR_INTERNAL_ERROR));
 85}
 86
 87const char* ffDebugNtStatus(NTSTATUS status) {
 88    return ffDebugWin32Error(RtlNtStatusToDosError(status));
 89}
 90
 91static inline DWORD HRESULTToWin32Error(HRESULT hr) {
 92    if (SUCCEEDED(hr)) {
 93        return ERROR_SUCCESS;
 94    }
 95
 96    if (HRESULT_FACILITY(hr) == FACILITY_WIN32) {
 97        return HRESULT_CODE(hr);
 98    }
 99
100    return ERROR_INTERNAL_ERROR;
101}
102
103const char* ffDebugHResult(HRESULT hr) {
104    return ffDebugWin32Error(HRESULTToWin32Error(hr));
105}
106    #endif
107
108#endif