diff --git a/offload/include/Shared/Debug.h b/offload/include/Shared/Debug.h index 7c3db8dbf119f..5d918f9afef91 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,199 @@ 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 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. + +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, []() { + // 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) { + 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; }