From 2965164487fc0e4bf4c25047430e36dbe350a1a8 Mon Sep 17 00:00:00 2001 From: Alex Duran Date: Tue, 28 Oct 2025 15:33:47 +0100 Subject: [PATCH 1/9] [OFFLOAD] Add support for more fine grained debug messages control --- offload/include/Shared/Debug.h | 188 +++++++++++++++++++++++ offload/libomptarget/OffloadRTL.cpp | 2 +- offload/libomptarget/PluginManager.cpp | 2 +- offload/plugins-nextgen/host/src/rtl.cpp | 2 + 4 files changed, 192 insertions(+), 2 deletions(-) diff --git a/offload/include/Shared/Debug.h b/offload/include/Shared/Debug.h index 7c3db8dbf119f..25922d190d20e 100644 --- a/offload/include/Shared/Debug.h +++ b/offload/include/Shared/Debug.h @@ -42,6 +42,8 @@ #include #include +#include "llvm/Support/circular_raw_ostream.h" + /// 32-Bit field data attributes controlling information presented to the user. enum OpenMPInfoType : uint32_t { // Print data arguments and attributes upon entering an OpenMP device kernel. @@ -198,4 +200,190 @@ inline uint32_t getDebugLevel() { } \ } while (false) +// New macros that will allow for more granular control over debugging output +// Each message can be classified by Component, Type and Level +// Component: The broad component of the offload runtime emitting the message. +// Type: A cross-component classification of messages +// Level: The verbosity level of the message +// +// The component is pulled from the TARGET_NAME macro, Type and Level can be +// defined for each debug message but by default they are "default" and "1" +// respectively. +// +// For liboffload and plugins, use OFFLOAD_DEBUG(...) +// For libomptarget, use OPENMP_DEBUG(...) +// Constructing messages should be done using C++ stream style syntax. +// +// Usage examples: +// OFFLOAD_DEBUG("type1", 2, "This is a level 2 message of type1"); +// OFFLOAD_DEBUG("Init", "This is a default level of the init type"); +// OPENMP_DEBUG("This is a level 1 message of the default type"); +// OFFLOAD_DEBUG("Init", 3, NumDevices << " were initialized\n"); +// OFFLOAD_DEBUG("Kernel", "Starting kernel " << KernelName << " on device " << +// DeviceId); +// +// Message output can be controlled by setting LIBOMPTARGET_DEBUG or +// LIBOFFLOAD_DEBUG environment variables. Their syntax is as follows: +// [integer]|all|[:][,[:],...] +// +// 0 : Disable all debug messages +// all : Enable all debug messages +// integer : Set the default level for all messages +// : Enable only messages of the specified type and level (more than one +// can be specified). Components are also supported as +// types. +// : Set the verbosity level for the specified type (default is 1) +// +// For very specific cases where more control is needed, use OFFLOAD_DEBUG_RAW +// or OFFLOAD_DEBUG_BASE. See below for details. + +namespace llvm::offload::debug { + +#ifdef OMPTARGET_DEBUG + +struct DebugFilter { + StringRef Type; + uint32_t Level; +}; + +struct DebugSettings { + bool Enabled = false; + uint32_t DefaultLevel = 1; + llvm::SmallVector Filters; +}; + +/// dbgs - Return a circular-buffered debug stream. +inline llvm::raw_ostream &dbgs() { + // Do one-time initialization in a thread-safe way. + static struct dbgstream { + llvm::circular_raw_ostream strm; + + dbgstream() : strm(llvm::errs(), "*** Debug Log Output ***\n", 0) {} + } thestrm; + + return thestrm.strm; +} + +inline DebugFilter parseDebugFilter(StringRef Filter) { + size_t Pos = Filter.find(':'); + if (Pos == StringRef::npos) + return {Filter, 1}; + + StringRef Type = Filter.slice(0, Pos); + uint32_t Level = 1; + if (Filter.slice(Pos + 1, Filter.size()).getAsInteger(10, Level)) + Level = 1; + + return {Type, Level}; +} + +inline DebugSettings &getDebugSettings() { + static DebugSettings Settings; + static std::once_flag Flag{}; + std::call_once(Flag, []() { + printf("Configuring debug settings\n"); + // Eventually, we probably should allow for the upper layers to set + // the debug settings directly according to their own env var + // (or other methods) settings. + // For now mantain compatibility with existing libomptarget env var + // and add a liboffload independent one. + char *Env = getenv("LIBOMPTARGET_DEBUG"); + if (!Env) { + Env = getenv("LIBOFFLOAD_DEBUG"); + if (!Env) + return; + } + + StringRef EnvRef(Env); + if (EnvRef == "0") + return; + + Settings.Enabled = true; + if (EnvRef.equals_insensitive("all")) + return; + + if (!EnvRef.getAsInteger(10, Settings.DefaultLevel)) + return; + + Settings.DefaultLevel = 1; + + SmallVector DbgTypes; + EnvRef.split(DbgTypes, ',', -1, false); + + for (auto &DT : DbgTypes) + Settings.Filters.push_back(parseDebugFilter(DT)); + }); + + return Settings; +} + +inline bool isDebugEnabled() { + return getDebugSettings().Enabled; +} + +inline bool shouldPrintDebug(const char *Component, const char *Type, uint32_t Level) { + const auto &Settings = getDebugSettings(); + if (!Settings.Enabled) + return false; + + if (Settings.Filters.empty()) + return Level <= Settings.DefaultLevel; + + for (const auto &DT : Settings.Filters) { + if (DT.Level < Level) + continue; + if (DT.Type.equals_insensitive(Type)) + return true; + if (DT.Type.equals_insensitive(Component)) + return true; + } + + return false; +} + +#define OFFLOAD_DEBUG_BASE(Component, Type, Level, ...) \ + do { \ + if (llvm::offload::debug::isDebugEnabled() && \ + llvm::offload::debug::shouldPrintDebug(Component, Type, Level)) \ + __VA_ARGS__; \ + } while (0) + +#define OFFLOAD_DEBUG_RAW(Type, Level, X) \ + OFFLOAD_DEBUG_BASE(GETNAME(TARGET_NAME), Type, Level, X) + +#define OFFLOAD_DEBUG_1(X) \ + OFFLOAD_DEBUG_BASE(GETNAME(TARGET_NAME), "default", 1, \ + llvm::offload::debug::dbgs() \ + << DEBUG_PREFIX << " --> " << X) + +#define OFFLOAD_DEBUG_2(Type, X) \ + OFFLOAD_DEBUG_BASE(GETNAME(TARGET_NAME), Type, 1, \ + llvm::offload::debug::dbgs() \ + << DEBUG_PREFIX << " --> " << X) + +#define OFFLOAD_DEBUG_3(Type, Level, X) \ + OFFLOAD_DEBUG_BASE(GETNAME(TARGET_NAME), Type, Level, \ + llvm::offload::debug::dbgs() \ + << DEBUG_PREFIX << " --> " << X) + +#define OFFLOAD_SELECT(Type, Level, X, NArgs, ...) OFFLOAD_DEBUG_##NArgs + +// To be used in liboffload and plugins +#define OFFLOAD_DEBUG(...) OFFLOAD_SELECT(__VA_ARGS__, 3, 2, 1)(__VA_ARGS__) + +// To be used in libomptarget only +#define OPENMP_DEBUG(...) OFFLOAD_DEBUG(__VA_ARGS__) + +#else + +// Don't print anything if debugging is disabled +#define OFFLOAD_DEBUG_BASE(Component, Type, Level, ...) +#define OFFLOAD_DEBUG_RAW(Type, Level, X) +#define OFFLOAD_DEBUG(...) +#define OPENMP_DEBUG(...) + +#endif + +} // namespace llvm::offload::debug + #endif // OMPTARGET_SHARED_DEBUG_H diff --git a/offload/libomptarget/OffloadRTL.cpp b/offload/libomptarget/OffloadRTL.cpp index 04bd21ec91a49..9f9ec5c8bfabe 100644 --- a/offload/libomptarget/OffloadRTL.cpp +++ b/offload/libomptarget/OffloadRTL.cpp @@ -35,7 +35,7 @@ void initRuntime() { RefCount++; if (RefCount == 1) { - DP("Init offload library!\n"); + OPENMP_DEBUG("Init offload library!\n"); #ifdef OMPT_SUPPORT // Initialize OMPT first llvm::omp::target::ompt::connectLibrary(); diff --git a/offload/libomptarget/PluginManager.cpp b/offload/libomptarget/PluginManager.cpp index c8d6b42114d0f..915fd8df80eb7 100644 --- a/offload/libomptarget/PluginManager.cpp +++ b/offload/libomptarget/PluginManager.cpp @@ -36,7 +36,7 @@ void PluginManager::init() { return; } - DP("Loading RTLs...\n"); + OPENMP_DEBUG("Init", "Loading RTLs\n"); // Attempt to create an instance of each supported plugin. #define PLUGIN_TARGET(Name) \ diff --git a/offload/plugins-nextgen/host/src/rtl.cpp b/offload/plugins-nextgen/host/src/rtl.cpp index eb4ecac9907a1..3baadfebc541c 100644 --- a/offload/plugins-nextgen/host/src/rtl.cpp +++ b/offload/plugins-nextgen/host/src/rtl.cpp @@ -451,6 +451,8 @@ struct GenELF64PluginTy final : public GenericPluginTy { if (auto Err = Plugin::check(ffi_init(), "failed to initialize libffi")) return std::move(Err); #endif + OFFLOAD_DEBUG("Init", 2, + "GenELF64 plugin detected" << NUM_DEVICES << " devices\n"); return NUM_DEVICES; } From 7bc7ce8b69a1e3b1fb4602b2ae3477d7fa3748a0 Mon Sep 17 00:00:00 2001 From: Alex Duran Date: Tue, 28 Oct 2025 16:04:18 +0100 Subject: [PATCH 2/9] formatting --- offload/include/Shared/Debug.h | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/offload/include/Shared/Debug.h b/offload/include/Shared/Debug.h index 25922d190d20e..85e1c0e19445d 100644 --- a/offload/include/Shared/Debug.h +++ b/offload/include/Shared/Debug.h @@ -282,10 +282,10 @@ inline DebugSettings &getDebugSettings() { static std::once_flag Flag{}; std::call_once(Flag, []() { printf("Configuring debug settings\n"); - // Eventually, we probably should allow for the upper layers to set - // the debug settings directly according to their own env var - // (or other methods) settings. - // For now mantain compatibility with existing libomptarget env var + // Eventually, we probably should allow the upper layers to set + // debug settings directly according to their own env var or + // other methods. + // For now, mantain compatibility with existing libomptarget env var // and add a liboffload independent one. char *Env = getenv("LIBOMPTARGET_DEBUG"); if (!Env) { @@ -317,11 +317,10 @@ inline DebugSettings &getDebugSettings() { return Settings; } -inline bool isDebugEnabled() { - return getDebugSettings().Enabled; -} +inline bool isDebugEnabled() { return getDebugSettings().Enabled; } -inline bool shouldPrintDebug(const char *Component, const char *Type, uint32_t Level) { +inline bool shouldPrintDebug(const char *Component, const char *Type, + uint32_t Level) { const auto &Settings = getDebugSettings(); if (!Settings.Enabled) return false; From c9fc54a3c8d07b34c6be74ef1e579bf1b8ffb2a5 Mon Sep 17 00:00:00 2001 From: Alex Duran Date: Wed, 29 Oct 2025 16:04:32 +0100 Subject: [PATCH 3/9] add some env var examples --- offload/include/Shared/Debug.h | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/offload/include/Shared/Debug.h b/offload/include/Shared/Debug.h index 85e1c0e19445d..3af4bb4abdd93 100644 --- a/offload/include/Shared/Debug.h +++ b/offload/include/Shared/Debug.h @@ -227,13 +227,24 @@ inline uint32_t getDebugLevel() { // [integer]|all|[:][,[:],...] // // 0 : Disable all debug messages -// all : Enable all debug messages +// all : Enable all level 1 debug messages // integer : Set the default level for all messages // : Enable only messages of the specified type and level (more than one // can be specified). Components are also supported as // types. // : Set the verbosity level for the specified type (default is 1) // +// Some examples: +// LIBOFFLOAD_DEBUG=1 (Print all messages of level 1 or lower) +// LIBOFFLOAD_DEBUG=5 (Print all messages of level 5 or lower) +// LIBOFFLOAD_DEBUG=init (Print messages of type "init" of level 1 or lower) +// LIBOFFLOAD_DEBUG=init:3,mapping:2 (Print messages of type "init" of level 3 +// or lower and messages of type "mapping" of +// level 2 or lower) +// LIBOFFLOAD_DEBUG=omptarget:4, init (Print messages from component "omptarget" of +// level 4 or lower and messages of type +// "init" of level 1 or lower) +// // For very specific cases where more control is needed, use OFFLOAD_DEBUG_RAW // or OFFLOAD_DEBUG_BASE. See below for details. From da323afd1959320804fefcc77cdaffa4f92a0864 Mon Sep 17 00:00:00 2001 From: Alex Duran Date: Tue, 4 Nov 2025 21:31:42 +0100 Subject: [PATCH 4/9] remove forgotten printf --- offload/include/Shared/Debug.h | 1 - 1 file changed, 1 deletion(-) diff --git a/offload/include/Shared/Debug.h b/offload/include/Shared/Debug.h index 3af4bb4abdd93..1fbf1c2a2645b 100644 --- a/offload/include/Shared/Debug.h +++ b/offload/include/Shared/Debug.h @@ -292,7 +292,6 @@ inline DebugSettings &getDebugSettings() { static DebugSettings Settings; static std::once_flag Flag{}; std::call_once(Flag, []() { - printf("Configuring debug settings\n"); // Eventually, we probably should allow the upper layers to set // debug settings directly according to their own env var or // other methods. From 562c93ec63e046b0a16ac96e8b58446ce5af81af Mon Sep 17 00:00:00 2001 From: Alex Duran Date: Tue, 4 Nov 2025 21:49:04 +0100 Subject: [PATCH 5/9] minor comment format fix --- offload/include/Shared/Debug.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/offload/include/Shared/Debug.h b/offload/include/Shared/Debug.h index 1fbf1c2a2645b..5d918f9afef91 100644 --- a/offload/include/Shared/Debug.h +++ b/offload/include/Shared/Debug.h @@ -241,8 +241,8 @@ inline uint32_t getDebugLevel() { // LIBOFFLOAD_DEBUG=init:3,mapping:2 (Print messages of type "init" of level 3 // or lower and messages of type "mapping" of // level 2 or lower) -// LIBOFFLOAD_DEBUG=omptarget:4, init (Print messages from component "omptarget" of -// level 4 or lower and messages of type +// LIBOFFLOAD_DEBUG=omptarget:4, init (Print messages from component "omptarget" +// of level 4 or lower and messages of type // "init" of level 1 or lower) // // For very specific cases where more control is needed, use OFFLOAD_DEBUG_RAW From b13c11633db1903e0da9f81d46fef70e9d064281 Mon Sep 17 00:00:00 2001 From: Alex Duran Date: Tue, 4 Nov 2025 21:53:26 +0100 Subject: [PATCH 6/9] nit --- offload/include/Shared/Debug.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/offload/include/Shared/Debug.h b/offload/include/Shared/Debug.h index 5d918f9afef91..5ca79040cd268 100644 --- a/offload/include/Shared/Debug.h +++ b/offload/include/Shared/Debug.h @@ -241,7 +241,7 @@ inline uint32_t getDebugLevel() { // LIBOFFLOAD_DEBUG=init:3,mapping:2 (Print messages of type "init" of level 3 // or lower and messages of type "mapping" of // level 2 or lower) -// LIBOFFLOAD_DEBUG=omptarget:4, init (Print messages from component "omptarget" +// LIBOFFLOAD_DEBUG=omptarget:4, init (Print messages from component "omptarget" // of level 4 or lower and messages of type // "init" of level 1 or lower) // From 948354b2a3f655844396d68da021f31f061a0dd1 Mon Sep 17 00:00:00 2001 From: Alex Duran Date: Tue, 11 Nov 2025 19:38:57 +0100 Subject: [PATCH 7/9] New implementation based on LDBG from Support/DebugLog --- offload/include/Shared/Debug.h | 218 ++++++++++++++++++----- offload/libomptarget/OffloadRTL.cpp | 2 +- offload/libomptarget/PluginManager.cpp | 2 +- offload/plugins-nextgen/host/src/rtl.cpp | 5 +- 4 files changed, 174 insertions(+), 53 deletions(-) diff --git a/offload/include/Shared/Debug.h b/offload/include/Shared/Debug.h index 5ca79040cd268..d7387aa8375e7 100644 --- a/offload/include/Shared/Debug.h +++ b/offload/include/Shared/Debug.h @@ -210,17 +210,30 @@ inline uint32_t getDebugLevel() { // defined for each debug message but by default they are "default" and "1" // respectively. // -// For liboffload and plugins, use OFFLOAD_DEBUG(...) -// For libomptarget, use OPENMP_DEBUG(...) // Constructing messages should be done using C++ stream style syntax. // // Usage examples: -// OFFLOAD_DEBUG("type1", 2, "This is a level 2 message of type1"); -// OFFLOAD_DEBUG("Init", "This is a default level of the init type"); -// OPENMP_DEBUG("This is a level 1 message of the default type"); -// OFFLOAD_DEBUG("Init", 3, NumDevices << " were initialized\n"); -// OFFLOAD_DEBUG("Kernel", "Starting kernel " << KernelName << " on device " << -// DeviceId); +// ODBG("type1", 2) << "This is a level 2 message of type1"; +// ODBG("Init") << "This is a default level of the init type"; +// ODBG() << "This is a level 1 message of the default type"; +// ODBG("Init", 3) << NumDevices << " were initialized"; +// ODBG("Kernel") << "Launching " << KernelName << " on device " << DeviceId; +// +// Additionally, ODBG_IF_LEVEL can be used to have parts of a stream to provide +// additional detail at different levels without needing to split the message. +// Using ODBG_RESET_LEVEL will reset the level back to the original level. +// E.g.: +// ODBG("Mapping", 2) << "Function F" +// << ODBG_IF_LEVEL(3) << " with argument value=" << Arg +// << ODBG_IF_LEVEL(4) << " and address=" << &Arg +// << ODBG_RESET_LEVEL() << " called"; +// +// Similarly the ODBG_ONLY_LEVEL can be used to print parts of a stream only at +// a specific level, e.g.: +// ODBG() << "Starting computation " +// << ODBG_ONLY_LEVEL(1) << "on a device" +// << ODBG_ONLY_LEVEL(2) << "and mapping data on device" << DeviceId; +// << ODBG_ONLY_LEVEL(3) << dumpDetailedMappingInfo(DeviceId); // // Message output can be controlled by setting LIBOMPTARGET_DEBUG or // LIBOFFLOAD_DEBUG environment variables. Their syntax is as follows: @@ -245,8 +258,8 @@ inline uint32_t getDebugLevel() { // of level 4 or lower and messages of type // "init" of level 1 or lower) // -// For very specific cases where more control is needed, use OFFLOAD_DEBUG_RAW -// or OFFLOAD_DEBUG_BASE. See below for details. +// For very specific cases where more control is needed, use ODBG_STREAM or +// ODBG_BASE. See below for details. namespace llvm::offload::debug { @@ -264,7 +277,7 @@ struct DebugSettings { }; /// dbgs - Return a circular-buffered debug stream. -inline llvm::raw_ostream &dbgs() { +[[maybe_unused]] static llvm::raw_ostream &dbgs() { // Do one-time initialization in a thread-safe way. static struct dbgstream { llvm::circular_raw_ostream strm; @@ -275,7 +288,7 @@ inline llvm::raw_ostream &dbgs() { return thestrm.strm; } -inline DebugFilter parseDebugFilter(StringRef Filter) { +[[maybe_unused]] static DebugFilter parseDebugFilter(StringRef Filter) { size_t Pos = Filter.find(':'); if (Pos == StringRef::npos) return {Filter, 1}; @@ -288,7 +301,7 @@ inline DebugFilter parseDebugFilter(StringRef Filter) { return {Type, Level}; } -inline DebugSettings &getDebugSettings() { +[[maybe_unused]] static DebugSettings &getDebugSettings() { static DebugSettings Settings; static std::once_flag Flag{}; std::call_once(Flag, []() { @@ -329,67 +342,174 @@ inline DebugSettings &getDebugSettings() { inline bool isDebugEnabled() { return getDebugSettings().Enabled; } -inline bool shouldPrintDebug(const char *Component, const char *Type, - uint32_t Level) { +[[maybe_unused]] static bool +shouldPrintDebug(const char *Component, const char *Type, uint32_t &Level) { const auto &Settings = getDebugSettings(); if (!Settings.Enabled) return false; - if (Settings.Filters.empty()) - return Level <= Settings.DefaultLevel; + if (Settings.Filters.empty()) { + if (Level <= Settings.DefaultLevel) { + Level = Settings.DefaultLevel; + return true; + } + return false; + } for (const auto &DT : Settings.Filters) { if (DT.Level < Level) continue; - if (DT.Type.equals_insensitive(Type)) - return true; - if (DT.Type.equals_insensitive(Component)) + if (DT.Type.equals_insensitive(Type) || + DT.Type.equals_insensitive(Component)) { + Level = DT.Level; return true; + } } return false; } -#define OFFLOAD_DEBUG_BASE(Component, Type, Level, ...) \ - do { \ - if (llvm::offload::debug::isDebugEnabled() && \ - llvm::offload::debug::shouldPrintDebug(Component, Type, Level)) \ - __VA_ARGS__; \ - } while (0) +/// A raw_ostream that tracks `\n` and print the prefix after each +/// newline. Based on raw_ldbg_ostream from Support/DebugLog.h +class LLVM_ABI odbg_ostream final : public raw_ostream { +public: + enum IfLevel : uint32_t; + enum OnlyLevel : uint32_t; + +private: + std::string Prefix; + raw_ostream &Os; + uint32_t BaseLevel; + bool ShouldPrefixNextString; + bool ShouldEmitNewLineOnDestruction; + + /// If the stream is muted, writes to it are ignored + bool Muted = false; + + /// Split the line on newlines and insert the prefix before each + /// newline. Forward everything to the underlying stream. + void write_impl(const char *Ptr, size_t Size) final { + if (Muted) + return; + + auto Str = StringRef(Ptr, Size); + auto Eol = Str.find('\n'); + // Handle `\n` occurring in the string, ensure to print the prefix at the + // beginning of each line. + while (Eol != StringRef::npos) { + // Take the line up to the newline (including the newline). + StringRef Line = Str.take_front(Eol + 1); + if (!Line.empty()) + writeWithPrefix(Line); + // We printed a newline, record here to print a prefix. + ShouldPrefixNextString = true; + Str = Str.drop_front(Eol + 1); + Eol = Str.find('\n'); + } + if (!Str.empty()) + writeWithPrefix(Str); + } + void emitPrefix() { Os.write(Prefix.c_str(), Prefix.size()); } + void writeWithPrefix(StringRef Str) { + if (ShouldPrefixNextString) { + emitPrefix(); + ShouldPrefixNextString = false; + } + Os.write(Str.data(), Str.size()); + } + +public: + explicit odbg_ostream(std::string Prefix, raw_ostream &Os, uint32_t BaseLevel, + bool ShouldPrefixNextString = true, + bool ShouldEmitNewLineOnDestruction = false) + : Prefix(std::move(Prefix)), Os(Os), BaseLevel(BaseLevel), + ShouldPrefixNextString(ShouldPrefixNextString), + ShouldEmitNewLineOnDestruction(ShouldEmitNewLineOnDestruction) { + SetUnbuffered(); + } + ~odbg_ostream() final { + if (ShouldEmitNewLineOnDestruction) + Os << '\n'; + } -#define OFFLOAD_DEBUG_RAW(Type, Level, X) \ - OFFLOAD_DEBUG_BASE(GETNAME(TARGET_NAME), Type, Level, X) + /// Forward the current_pos method to the underlying stream. + uint64_t current_pos() const final { return Os.tell(); } -#define OFFLOAD_DEBUG_1(X) \ - OFFLOAD_DEBUG_BASE(GETNAME(TARGET_NAME), "default", 1, \ - llvm::offload::debug::dbgs() \ - << DEBUG_PREFIX << " --> " << X) + /// Some of the `<<` operators expect an lvalue, so we trick the type + /// system. + odbg_ostream &asLvalue() { return *this; } -#define OFFLOAD_DEBUG_2(Type, X) \ - OFFLOAD_DEBUG_BASE(GETNAME(TARGET_NAME), Type, 1, \ - llvm::offload::debug::dbgs() \ - << DEBUG_PREFIX << " --> " << X) + void shouldMute(const IfLevel Filter) { Muted = Filter > BaseLevel; } + void shouldMute(const OnlyLevel Filter) { Muted = BaseLevel != Filter; } +}; -#define OFFLOAD_DEBUG_3(Type, Level, X) \ - OFFLOAD_DEBUG_BASE(GETNAME(TARGET_NAME), Type, Level, \ - llvm::offload::debug::dbgs() \ - << DEBUG_PREFIX << " --> " << X) +/// Compute the prefix for the debug log in the form of: +/// "Component --> " +[[maybe_unused]] static std::string computePrefix(StringRef Component, + StringRef DebugType) { + std::string Prefix; + raw_string_ostream OsPrefix(Prefix); + OsPrefix << Component << " --> "; + return OsPrefix.str(); +} -#define OFFLOAD_SELECT(Type, Level, X, NArgs, ...) OFFLOAD_DEBUG_##NArgs +static inline raw_ostream &operator<<(raw_ostream &Os, + const odbg_ostream::IfLevel Filter) { + odbg_ostream &Dbg = static_cast(Os); + Dbg.shouldMute(Filter); + return Dbg; +} -// To be used in liboffload and plugins -#define OFFLOAD_DEBUG(...) OFFLOAD_SELECT(__VA_ARGS__, 3, 2, 1)(__VA_ARGS__) +static inline raw_ostream &operator<<(raw_ostream &Os, + const odbg_ostream::OnlyLevel Filter) { + odbg_ostream &Dbg = static_cast(Os); + Dbg.shouldMute(Filter); + return Dbg; +} -// To be used in libomptarget only -#define OPENMP_DEBUG(...) OFFLOAD_DEBUG(__VA_ARGS__) +#define ODBG_BASE(Stream, Component, Prefix, Type, Level) \ + for (uint32_t RealLevel = (Level), \ + _c = llvm::offload::debug::isDebugEnabled() && \ + llvm::offload::debug::shouldPrintDebug( \ + (Component), (Type), RealLevel); \ + _c; _c = 0) \ + ::llvm::offload::debug::odbg_ostream{ \ + ::llvm::offload::debug::computePrefix((Prefix), (Type)), (Stream), \ + RealLevel, /*ShouldPrefixNextString=*/true, \ + /*ShouldEmitNewLineOnDestruction=*/true} \ + .asLvalue() + +#define ODBG_STREAM(Stream, Type, Level) \ + ODBG_BASE(Stream, GETNAME(TARGET_NAME), DEBUG_PREFIX, Type, Level) + +#define ODBG_0() ODBG_2("default", 1) +#define ODBG_1(Type) ODBG_2(Type, 1) +#define ODBG_2(Type, Level) \ + ODBG_STREAM(llvm::offload::debug::dbgs(), Type, Level) +#define ODBG_SELECT(Type, Level, NArgs, ...) ODBG_##NArgs + +#define ODBG_IF_LEVEL(Level) \ + static_cast(Level) +#define ODBG_ONLY_LEVEL(Level) \ + static_cast(Level) +#define ODBG_RESET_LEVEL() \ + static_cast(0) + +#define ODBG(...) ODBG_SELECT(__VA_ARGS__ __VA_OPT__(, ) 2, 1, 0)(__VA_ARGS__) #else +#define ODBG_NULL \ + for (bool _c = false; _c; _c = false) \ + ::llvm::nulls() + // Don't print anything if debugging is disabled -#define OFFLOAD_DEBUG_BASE(Component, Type, Level, ...) -#define OFFLOAD_DEBUG_RAW(Type, Level, X) -#define OFFLOAD_DEBUG(...) -#define OPENMP_DEBUG(...) +#define ODBG_BASE(Stream, Component, Prefix, Type, Level) ODBG_NULL +#define ODBG_STREAM(Stream, Type, Level) ODBG_NULL +#define ODBG_IF_LEVEL(Level) 0 +#define ODBG_ONLY_LEVEL(Level) 0 +#define ODBG_RESET_LEVEL() 0 +#define ODBG(...) ODBG_NULL #endif diff --git a/offload/libomptarget/OffloadRTL.cpp b/offload/libomptarget/OffloadRTL.cpp index 9f9ec5c8bfabe..0ae325bf496d9 100644 --- a/offload/libomptarget/OffloadRTL.cpp +++ b/offload/libomptarget/OffloadRTL.cpp @@ -35,7 +35,7 @@ void initRuntime() { RefCount++; if (RefCount == 1) { - OPENMP_DEBUG("Init offload library!\n"); + ODBG() << "Init offload library!"; #ifdef OMPT_SUPPORT // Initialize OMPT first llvm::omp::target::ompt::connectLibrary(); diff --git a/offload/libomptarget/PluginManager.cpp b/offload/libomptarget/PluginManager.cpp index 915fd8df80eb7..d9db9b6450789 100644 --- a/offload/libomptarget/PluginManager.cpp +++ b/offload/libomptarget/PluginManager.cpp @@ -36,7 +36,7 @@ void PluginManager::init() { return; } - OPENMP_DEBUG("Init", "Loading RTLs\n"); + ODBG("Init") << "Loading RTLs"; // Attempt to create an instance of each supported plugin. #define PLUGIN_TARGET(Name) \ diff --git a/offload/plugins-nextgen/host/src/rtl.cpp b/offload/plugins-nextgen/host/src/rtl.cpp index 3baadfebc541c..d717aef629e60 100644 --- a/offload/plugins-nextgen/host/src/rtl.cpp +++ b/offload/plugins-nextgen/host/src/rtl.cpp @@ -451,8 +451,9 @@ struct GenELF64PluginTy final : public GenericPluginTy { if (auto Err = Plugin::check(ffi_init(), "failed to initialize libffi")) return std::move(Err); #endif - OFFLOAD_DEBUG("Init", 2, - "GenELF64 plugin detected" << NUM_DEVICES << " devices\n"); + ODBG("Init") << "GenELF64 plugin detected " + << ODBG_IF_LEVEL(2) << NUM_DEVICES << " " + << ODBG_RESET_LEVEL() << "devices"; return NUM_DEVICES; } From 04e44a7916ca4b9ff48f48bd962ba29ae3cfe323 Mon Sep 17 00:00:00 2001 From: Alex Duran Date: Thu, 20 Nov 2025 06:16:51 +0100 Subject: [PATCH 8/9] minor refactoring, format --- offload/include/Shared/Debug.h | 11 ++++++----- offload/plugins-nextgen/host/src/rtl.cpp | 5 ++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/offload/include/Shared/Debug.h b/offload/include/Shared/Debug.h index d7387aa8375e7..668887309214e 100644 --- a/offload/include/Shared/Debug.h +++ b/offload/include/Shared/Debug.h @@ -42,6 +42,7 @@ #include #include +#include "llvm/ADT/StringExtras.h" #include "llvm/Support/circular_raw_ostream.h" /// 32-Bit field data attributes controlling information presented to the user. @@ -330,11 +331,11 @@ struct DebugSettings { Settings.DefaultLevel = 1; - SmallVector DbgTypes; - EnvRef.split(DbgTypes, ',', -1, false); - - for (auto &DT : DbgTypes) - Settings.Filters.push_back(parseDebugFilter(DT)); + for (auto &FilterSpec : llvm::split(EnvRef, ',')) { + if (FilterSpec.empty()) + continue; + Settings.Filters.push_back(parseDebugFilter(FilterSpec)); + } }); return Settings; diff --git a/offload/plugins-nextgen/host/src/rtl.cpp b/offload/plugins-nextgen/host/src/rtl.cpp index d717aef629e60..70ff135cfe3df 100644 --- a/offload/plugins-nextgen/host/src/rtl.cpp +++ b/offload/plugins-nextgen/host/src/rtl.cpp @@ -451,9 +451,8 @@ struct GenELF64PluginTy final : public GenericPluginTy { if (auto Err = Plugin::check(ffi_init(), "failed to initialize libffi")) return std::move(Err); #endif - ODBG("Init") << "GenELF64 plugin detected " - << ODBG_IF_LEVEL(2) << NUM_DEVICES << " " - << ODBG_RESET_LEVEL() << "devices"; + ODBG("Init") << "GenELF64 plugin detected " << ODBG_IF_LEVEL(2) + << NUM_DEVICES << " " << ODBG_RESET_LEVEL() << "devices"; return NUM_DEVICES; } From a045ce80f75614660c2a11971eef3d9cb8fc6bbf Mon Sep 17 00:00:00 2001 From: Alex Duran Date: Thu, 20 Nov 2025 06:29:15 +0100 Subject: [PATCH 9/9] simplify description --- offload/include/Shared/Debug.h | 89 ++++++++++------------------------ 1 file changed, 26 insertions(+), 63 deletions(-) diff --git a/offload/include/Shared/Debug.h b/offload/include/Shared/Debug.h index 668887309214e..41613a37c3548 100644 --- a/offload/include/Shared/Debug.h +++ b/offload/include/Shared/Debug.h @@ -201,67 +201,6 @@ inline uint32_t getDebugLevel() { } \ } while (false) -// New macros that will allow for more granular control over debugging output -// Each message can be classified by Component, Type and Level -// Component: The broad component of the offload runtime emitting the message. -// Type: A cross-component classification of messages -// Level: The verbosity level of the message -// -// The component is pulled from the TARGET_NAME macro, Type and Level can be -// defined for each debug message but by default they are "default" and "1" -// respectively. -// -// Constructing messages should be done using C++ stream style syntax. -// -// Usage examples: -// ODBG("type1", 2) << "This is a level 2 message of type1"; -// ODBG("Init") << "This is a default level of the init type"; -// ODBG() << "This is a level 1 message of the default type"; -// ODBG("Init", 3) << NumDevices << " were initialized"; -// ODBG("Kernel") << "Launching " << KernelName << " on device " << DeviceId; -// -// Additionally, ODBG_IF_LEVEL can be used to have parts of a stream to provide -// additional detail at different levels without needing to split the message. -// Using ODBG_RESET_LEVEL will reset the level back to the original level. -// E.g.: -// ODBG("Mapping", 2) << "Function F" -// << ODBG_IF_LEVEL(3) << " with argument value=" << Arg -// << ODBG_IF_LEVEL(4) << " and address=" << &Arg -// << ODBG_RESET_LEVEL() << " called"; -// -// Similarly the ODBG_ONLY_LEVEL can be used to print parts of a stream only at -// a specific level, e.g.: -// ODBG() << "Starting computation " -// << ODBG_ONLY_LEVEL(1) << "on a device" -// << ODBG_ONLY_LEVEL(2) << "and mapping data on device" << DeviceId; -// << ODBG_ONLY_LEVEL(3) << dumpDetailedMappingInfo(DeviceId); -// -// Message output can be controlled by setting LIBOMPTARGET_DEBUG or -// LIBOFFLOAD_DEBUG environment variables. Their syntax is as follows: -// [integer]|all|[:][,[:],...] -// -// 0 : Disable all debug messages -// all : Enable all level 1 debug messages -// integer : Set the default level for all messages -// : Enable only messages of the specified type and level (more than one -// can be specified). Components are also supported as -// types. -// : Set the verbosity level for the specified type (default is 1) -// -// Some examples: -// LIBOFFLOAD_DEBUG=1 (Print all messages of level 1 or lower) -// LIBOFFLOAD_DEBUG=5 (Print all messages of level 5 or lower) -// LIBOFFLOAD_DEBUG=init (Print messages of type "init" of level 1 or lower) -// LIBOFFLOAD_DEBUG=init:3,mapping:2 (Print messages of type "init" of level 3 -// or lower and messages of type "mapping" of -// level 2 or lower) -// LIBOFFLOAD_DEBUG=omptarget:4, init (Print messages from component "omptarget" -// of level 4 or lower and messages of type -// "init" of level 1 or lower) -// -// For very specific cases where more control is needed, use ODBG_STREAM or -// ODBG_BASE. See below for details. - namespace llvm::offload::debug { #ifdef OMPTARGET_DEBUG @@ -489,15 +428,39 @@ static inline raw_ostream &operator<<(raw_ostream &Os, ODBG_STREAM(llvm::offload::debug::dbgs(), Type, Level) #define ODBG_SELECT(Type, Level, NArgs, ...) ODBG_##NArgs +// Print a debug message of a certain type and verbosity level. If no type +// or level is provided, "default" and "1" are assumed respectively. +// Usage examples: +// ODBG("type1", 2) << "This is a level 2 message of type1"; +// ODBG("Init") << "This is a default level of the init type"; +// ODBG() << "This is a level 1 message of the default type"; +// ODBG("Init", 3) << NumDevices << " were initialized"; +// ODBG("Kernel") << "Launching " << KernelName << " on device " << DeviceId; +#define ODBG(...) ODBG_SELECT(__VA_ARGS__ __VA_OPT__(, ) 2, 1, 0)(__VA_ARGS__) + +// Filter the next elements in the debug stream if the current debug level is +// lower than specified level. Example: +// ODBG("Mapping", 2) << "level 2 info " +// << ODBG_IF_LEVEL(3) << " level 3 info" << Arg +// << ODBG_IF_LEVEL(4) << " level 4 info" << &Arg +// << ODBG_RESET_LEVEL() << " more level 2 info"; #define ODBG_IF_LEVEL(Level) \ static_cast(Level) + +// Filter the next elements in the debug stream if the current debug level is +// not exactly the specified level. Example: +// ODBG() << "Starting computation " +// << ODBG_ONLY_LEVEL(1) << "on a device" +// << ODBG_ONLY_LEVEL(2) << "and mapping data on device" << DeviceId; +// << ODBG_ONLY_LEVEL(3) << dumpDetailedMappingInfo(DeviceId); #define ODBG_ONLY_LEVEL(Level) \ static_cast(Level) + +// Reset the level back to the original level after ODBG_IF_LEVEL or +// ODBG_ONLY_LEVEL have been used #define ODBG_RESET_LEVEL() \ static_cast(0) -#define ODBG(...) ODBG_SELECT(__VA_ARGS__ __VA_OPT__(, ) 2, 1, 0)(__VA_ARGS__) - #else #define ODBG_NULL \