| //===-- Shared/Debug.h - Target independent OpenMP target RTL -- C++ ------===// | |
| // | |
| // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. | |
| // See https://llvm.org/LICENSE.txt for license information. | |
| // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // Routines used to provide debug messages and information from libomptarget | |
| // and plugin RTLs to the user. | |
| // | |
| // Each plugin RTL and libomptarget define TARGET_NAME and DEBUG_PREFIX for use | |
| // when sending messages to the user. These indicate which RTL sent the message | |
| // | |
| // Debug and information messages are controlled by the environment variables | |
| // LIBOMPTARGET_DEBUG and LIBOMPTARGET_INFO which is set upon initialization | |
| // of libomptarget or the plugin RTL. | |
| // | |
| // To printf a pointer in hex with a fixed width of 16 digits and a leading 0x, | |
| // use printf("ptr=" DPxMOD "...\n", DPxPTR(ptr)); | |
| // | |
| // DPxMOD expands to: | |
| // "0x%0*" PRIxPTR | |
| // where PRIxPTR expands to an appropriate modifier for the type uintptr_t on a | |
| // specific platform, e.g. "lu" if uintptr_t is typedef'd as unsigned long: | |
| // "0x%0*lu" | |
| // | |
| // Ultimately, the whole statement expands to: | |
| // printf("ptr=0x%0*lu...\n", // the 0* modifier expects an extra argument | |
| // // specifying the width of the output | |
| // (int)(2*sizeof(uintptr_t)), // the extra argument specifying the width | |
| // // 8 digits for 32bit systems | |
| // // 16 digits for 64bit | |
| // (uintptr_t) ptr); | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #ifndef OMPTARGET_SHARED_DEBUG_H | |
| #define OMPTARGET_SHARED_DEBUG_H | |
| #include <atomic> | |
| #include <cstdarg> | |
| #include <mutex> | |
| #include <string> | |
| #include "llvm/ADT/StringExtras.h" | |
| #include "llvm/Support/Format.h" | |
| #include "llvm/Support/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. | |
| OMP_INFOTYPE_KERNEL_ARGS = 0x0001, | |
| // Indicate when an address already exists in the device mapping table. | |
| OMP_INFOTYPE_MAPPING_EXISTS = 0x0002, | |
| // Dump the contents of the device pointer map at kernel exit or failure. | |
| OMP_INFOTYPE_DUMP_TABLE = 0x0004, | |
| // Indicate when an address is added to the device mapping table. | |
| OMP_INFOTYPE_MAPPING_CHANGED = 0x0008, | |
| // Print kernel information from target device plugins. | |
| OMP_INFOTYPE_PLUGIN_KERNEL = 0x0010, | |
| // Print whenever data is transferred to the device | |
| OMP_INFOTYPE_DATA_TRANSFER = 0x0020, | |
| // Print whenever data does not have a viable device counterpart. | |
| OMP_INFOTYPE_EMPTY_MAPPING = 0x0040, | |
| // Enable every flag. | |
| OMP_INFOTYPE_ALL = 0xffffffff, | |
| }; | |
| inline std::atomic<uint32_t> &getInfoLevelInternal() { | |
| static std::atomic<uint32_t> InfoLevel; | |
| static std::once_flag Flag{}; | |
| std::call_once(Flag, []() { | |
| if (char *EnvStr = getenv("LIBOMPTARGET_INFO")) | |
| InfoLevel.store(std::stoi(EnvStr)); | |
| }); | |
| return InfoLevel; | |
| } | |
| inline uint32_t getInfoLevel() { return getInfoLevelInternal().load(); } | |
| #undef USED | |
| #undef GCC_VERSION | |
| #ifndef __STDC_FORMAT_MACROS | |
| #define __STDC_FORMAT_MACROS | |
| #endif | |
| #include <inttypes.h> | |
| #undef __STDC_FORMAT_MACROS | |
| #define DPxMOD "0x%0*" PRIxPTR | |
| #define DPxPTR(ptr) ((int)(2 * sizeof(uintptr_t))), ((uintptr_t)(ptr)) | |
| #define GETNAME2(name) #name | |
| #define GETNAME(name) GETNAME2(name) | |
| /// Print a generic message string from libomptarget or a plugin RTL | |
| #define MESSAGE0(_str) \ | |
| do { \ | |
| fprintf(stderr, GETNAME(TARGET_NAME) " message: %s\n", _str); \ | |
| } while (0) | |
| /// Print a printf formatting string message from libomptarget or a plugin RTL | |
| #define MESSAGE(_str, ...) \ | |
| do { \ | |
| fprintf(stderr, GETNAME(TARGET_NAME) " message: " _str "\n", __VA_ARGS__); \ | |
| } while (0) | |
| /// Print fatal error message with an error string and error identifier | |
| #define FATAL_MESSAGE0(_num, _str) \ | |
| do { \ | |
| fprintf(stderr, GETNAME(TARGET_NAME) " fatal error %d: %s\n", (int)_num, \ | |
| _str); \ | |
| abort(); \ | |
| } while (0) | |
| /// Print fatal error message with a printf string and error identifier | |
| #define FATAL_MESSAGE(_num, _str, ...) \ | |
| do { \ | |
| fprintf(stderr, GETNAME(TARGET_NAME) " fatal error %d: " _str "\n", \ | |
| (int)_num, __VA_ARGS__); \ | |
| abort(); \ | |
| } while (0) | |
| /// Print a generic error string from libomptarget or a plugin RTL | |
| #define FAILURE_MESSAGE(...) \ | |
| do { \ | |
| fprintf(stderr, GETNAME(TARGET_NAME) " error: "); \ | |
| fprintf(stderr, __VA_ARGS__); \ | |
| } while (0) | |
| /// Print a generic information string used if LIBOMPTARGET_INFO=1 | |
| #define INFO_MESSAGE(_num, ...) INFO_MESSAGE_TO(stderr, _num, __VA_ARGS__) | |
| #define INFO_MESSAGE_TO(_stdDst, _num, ...) \ | |
| do { \ | |
| fprintf(_stdDst, GETNAME(TARGET_NAME) " device %d info: ", (int)_num); \ | |
| fprintf(_stdDst, __VA_ARGS__); \ | |
| } while (0) | |
| /// Emit a message giving the user extra information about the runtime if | |
| #define INFO(_flags, _id, ...) \ | |
| do { \ | |
| if (::llvm::offload::debug::isDebugEnabled()) { \ | |
| INFO_DEBUG_INT(_flags, _id, __VA_ARGS__); \ | |
| } else if (getInfoLevel() & _flags) { \ | |
| INFO_MESSAGE(_id, __VA_ARGS__); \ | |
| } \ | |
| } while (false) | |
| #define DUMP_INFO(toStdOut, _flags, _id, ...) \ | |
| do { \ | |
| if (toStdOut) { \ | |
| INFO_MESSAGE_TO(stdout, _id, __VA_ARGS__); \ | |
| } else { \ | |
| INFO(_flags, _id, __VA_ARGS__); \ | |
| } \ | |
| } while (false) | |
| namespace llvm::offload::debug { | |
| /// 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; | |
| bool NeedEndNewLine = false; | |
| /// Buffer to reduce interference between different threads | |
| /// writing at the same time to the underlying stream. | |
| static constexpr size_t BufferSize = 256; | |
| llvm::SmallString<BufferSize> Buffer; | |
| // Stream to write into Buffer. Its flushed to Os upon destruction. | |
| llvm::raw_svector_ostream BufferStrm; | |
| /// 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; | |
| NeedEndNewLine = false; | |
| 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); | |
| NeedEndNewLine = true; | |
| } | |
| } | |
| void emitPrefix() { BufferStrm.write(Prefix.c_str(), Prefix.size()); } | |
| void writeWithPrefix(StringRef Str) { | |
| if (ShouldPrefixNextString) { | |
| emitPrefix(); | |
| ShouldPrefixNextString = false; | |
| } | |
| BufferStrm.write(Str.data(), Str.size()); | |
| } | |
| public: | |
| explicit odbg_ostream(std::string Prefix, raw_ostream &Os, uint32_t BaseLevel, | |
| bool ShouldPrefixNextString = true, | |
| bool ShouldEmitNewLineOnDestruction = true) | |
| : Prefix(std::move(Prefix)), Os(Os), BaseLevel(BaseLevel), | |
| ShouldPrefixNextString(ShouldPrefixNextString), | |
| ShouldEmitNewLineOnDestruction(ShouldEmitNewLineOnDestruction), | |
| BufferStrm(Buffer) { | |
| SetUnbuffered(); | |
| } | |
| ~odbg_ostream() final { | |
| if (ShouldEmitNewLineOnDestruction && NeedEndNewLine) | |
| BufferStrm << '\n'; | |
| Os << BufferStrm.str(); | |
| } | |
| odbg_ostream(const odbg_ostream &) = delete; | |
| odbg_ostream &operator=(const odbg_ostream &) = delete; | |
| odbg_ostream(odbg_ostream &&other) : Os(other.Os), BufferStrm(Buffer) { | |
| Prefix = std::move(other.Prefix); | |
| BaseLevel = other.BaseLevel; | |
| ShouldPrefixNextString = other.ShouldPrefixNextString; | |
| ShouldEmitNewLineOnDestruction = other.ShouldEmitNewLineOnDestruction; | |
| NeedEndNewLine = other.NeedEndNewLine; | |
| Muted = other.Muted; | |
| BufferStrm << other.BufferStrm.str(); | |
| } | |
| /// Forward the current_pos method to the underlying stream. | |
| uint64_t current_pos() const final { return BufferStrm.tell(); } | |
| /// Some of the `<<` operators expect an lvalue, so we trick the type | |
| /// system. | |
| odbg_ostream &asLvalue() { return *this; } | |
| void shouldMute(const IfLevel Filter) { Muted = Filter > BaseLevel; } | |
| void shouldMute(const OnlyLevel Filter) { Muted = BaseLevel != Filter; } | |
| }; | |
| /// dbgs - Return the debug stream for offload debugging (just llvm::errs()). | |
| [[maybe_unused]] static llvm::raw_ostream &dbgs() { return llvm::errs(); } | |
| #ifdef OMPTARGET_DEBUG | |
| struct DebugFilter { | |
| StringRef Type; | |
| uint32_t Level; | |
| }; | |
| struct DebugSettings { | |
| bool Enabled = false; | |
| uint32_t DefaultLevel = 1; | |
| // Types/Components in this list are not printed when debug is enabled | |
| // unless they are explicitly requested by the user in IncludeFilters. | |
| llvm::SmallVector<StringRef> ExcludeFilters; | |
| // Types/Components in this list are printed when debug is enabled if | |
| // the debug level is equal or higher than the specified level. | |
| llvm::SmallVector<DebugFilter> IncludeFilters; | |
| }; | |
| [[maybe_unused]] static 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}; | |
| } | |
| [[maybe_unused]] static 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; | |
| // Messages with Type/Components added to the exclude list are not | |
| // not printed when debug is enabled unless they are explicitly | |
| // requested by the user. | |
| // Eventually, this should be configured from the upper layers but | |
| // for now we can hardcode some excluded types here like: | |
| // Settings.ExcludeFilters.push_back(Type); | |
| if (!EnvRef.getAsInteger(10, Settings.DefaultLevel)) | |
| return; | |
| Settings.DefaultLevel = 1; | |
| for (auto &FilterSpec : llvm::split(EnvRef, ',')) { | |
| if (FilterSpec.empty()) | |
| continue; | |
| DebugFilter Filter = parseDebugFilter(FilterSpec); | |
| // Remove from ExcludeFilters if present | |
| Settings.ExcludeFilters.erase( | |
| std::remove_if(Settings.ExcludeFilters.begin(), | |
| Settings.ExcludeFilters.end(), | |
| [&](StringRef OutType) { | |
| return OutType.equals_insensitive(Filter.Type); | |
| }), | |
| Settings.ExcludeFilters.end()); | |
| Settings.IncludeFilters.push_back(Filter); | |
| } | |
| }); | |
| return Settings; | |
| } | |
| inline bool isDebugEnabled() { return getDebugSettings().Enabled; } | |
| [[maybe_unused]] static bool | |
| shouldPrintDebug(const char *Component, const char *Type, uint32_t &Level) { | |
| const auto &Settings = getDebugSettings(); | |
| if (!Settings.Enabled) | |
| return false; | |
| for (const auto &Filter : Settings.ExcludeFilters) { | |
| if (Filter.equals_insensitive(Type) || Filter.equals_insensitive(Component)) | |
| return false; | |
| } | |
| if (Settings.IncludeFilters.empty()) { | |
| if (Level <= Settings.DefaultLevel) { | |
| Level = Settings.DefaultLevel; | |
| return true; | |
| } | |
| return false; | |
| } | |
| for (const auto &DT : Settings.IncludeFilters) { | |
| if (DT.Level < Level) | |
| continue; | |
| if (DT.Type.equals_insensitive("all") || DT.Type.equals_insensitive(Type) || | |
| DT.Type.equals_insensitive(Component)) { | |
| Level = DT.Level; | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| /// 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(); | |
| } | |
| static inline raw_ostream &operator<<(raw_ostream &Os, | |
| const odbg_ostream::IfLevel Filter) { | |
| odbg_ostream &Dbg = static_cast<odbg_ostream &>(Os); | |
| Dbg.shouldMute(Filter); | |
| return Dbg; | |
| } | |
| static inline raw_ostream &operator<<(raw_ostream &Os, | |
| const odbg_ostream::OnlyLevel Filter) { | |
| odbg_ostream &Dbg = static_cast<odbg_ostream &>(Os); | |
| Dbg.shouldMute(Filter); | |
| return Dbg; | |
| } | |
| #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 | |
| // 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<llvm::offload::debug::odbg_ostream::IfLevel>(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<llvm::offload::debug::odbg_ostream::OnlyLevel>(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<llvm::offload::debug::odbg_ostream::IfLevel>(0) | |
| // helper templates to support lambdas with different number of arguments | |
| template <typename LambdaTy> struct LambdaHelper { | |
| template <typename FuncTy, typename RetTy, typename... Args> | |
| static constexpr size_t CountArgs(RetTy (FuncTy::*)(Args...)) { | |
| return sizeof...(Args); | |
| } | |
| template <typename FuncTy, typename RetTy, typename... Args> | |
| static constexpr size_t CountArgs(RetTy (FuncTy::*)(Args...) const) { | |
| return sizeof...(Args); | |
| } | |
| static constexpr size_t NArgs = CountArgs(&LambdaTy::operator()); | |
| }; | |
| template <typename LambdaTy> struct LambdaOs : public LambdaHelper<LambdaTy> { | |
| static void dispatch(LambdaTy func, llvm::raw_ostream &Os, uint32_t Level) { | |
| if constexpr (LambdaHelper<LambdaTy>::NArgs == 2) | |
| func(Os, Level); | |
| else | |
| func(Os); | |
| } | |
| }; | |
| #define ODBG_OS_BASE(Stream, Component, Prefix, Type, Level, Callback) \ | |
| if (::llvm::offload::debug::isDebugEnabled()) { \ | |
| uint32_t RealLevel = (Level); \ | |
| if (::llvm::offload::debug::shouldPrintDebug((Component), (Type), \ | |
| RealLevel)) { \ | |
| ::llvm::offload::debug::odbg_ostream OS{ \ | |
| ::llvm::offload::debug::computePrefix((Prefix), (Type)), (Stream), \ | |
| RealLevel, /*ShouldPrefixNextString=*/true, \ | |
| /*ShouldEmitNewLineOnDestruction=*/true}; \ | |
| auto F = Callback; \ | |
| ::llvm::offload::debug::LambdaOs<decltype(F)>::dispatch(F, OS, \ | |
| RealLevel); \ | |
| } \ | |
| } | |
| #define ODBG_OS_STREAM(Stream, Type, Level, Callback) \ | |
| ODBG_OS_BASE(Stream, GETNAME(TARGET_NAME), DEBUG_PREFIX, Type, Level, \ | |
| Callback) | |
| #define ODBG_OS_3(Type, Level, Callback) \ | |
| ODBG_OS_STREAM(llvm::offload::debug::dbgs(), Type, Level, Callback) | |
| #define ODBG_OS_2(Type, Callback) ODBG_OS_3(Type, 1, Callback) | |
| #define ODBG_OS_1(Callback) ODBG_OS_2("default", Callback) | |
| #define ODBG_OS_SELECT(Type, Level, Callback, NArgs, ...) ODBG_OS_##NArgs | |
| // Print a debug message of a certain type and verbosity level using a callback | |
| // to emit the message. If no type or level is provided, "default" and "1 are | |
| // assumed respectively. | |
| #define ODBG_OS(...) \ | |
| ODBG_OS_SELECT(__VA_ARGS__ __VA_OPT__(, ) 3, 2, 1)(__VA_ARGS__) | |
| // helper templates to support lambdas with different number of arguments | |
| template <typename LambdaTy> struct LambdaIf : public LambdaHelper<LambdaTy> { | |
| static void dispatch(LambdaTy func, uint32_t Level) { | |
| if constexpr (LambdaHelper<LambdaTy>::NArgs == 1) | |
| func(Level); | |
| else | |
| func(); | |
| } | |
| }; | |
| #define ODBG_IF_BASE(Type, Level, Callback) \ | |
| if (::llvm::offload::debug::isDebugEnabled()) { \ | |
| uint32_t RealLevel = (Level); \ | |
| if (::llvm::offload::debug::shouldPrintDebug(GETNAME(TARGET_NAME), (Type), \ | |
| RealLevel)) { \ | |
| auto F = Callback; \ | |
| ::llvm::offload::debug::LambdaIf<decltype(F)>::dispatch(F, RealLevel); \ | |
| } \ | |
| } | |
| #define ODBG_IF_3(Type, Level, Callback) ODBG_IF_BASE(Type, Level, Callback) | |
| #define ODBG_IF_2(Type, Callback) ODBG_IF_3(Type, 1, Callback) | |
| #define ODBG_IF_1(Callback) ODBG_IF_2("default", Callback) | |
| #define ODBG_IF_SELECT(Type, Level, Callback, NArgs, ...) ODBG_IF_##NArgs | |
| #define ODBG_IF(...) \ | |
| ODBG_IF_SELECT(__VA_ARGS__ __VA_OPT__(, ) 3, 2, 1)(__VA_ARGS__) | |
| #else | |
| inline bool isDebugEnabled() { return false; } | |
| #define ODBG_NULL \ | |
| for (bool _c = false; _c; _c = false) \ | |
| ::llvm::nulls() | |
| // Don't print anything if debugging is disabled | |
| #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 | |
| #define ODBG_OS_BASE(Stream, Component, Prefix, Type, Level, Callback) | |
| #define ODBG_OS_STREAM(Stream, Type, Level, Callback) | |
| #define ODBG_OS(...) | |
| #define ODBG_IF_BASE(Type, Level, Callback) | |
| #define ODBG_IF(...) | |
| #endif | |
| // Common debug types in offload. | |
| constexpr const char *OLDT_Init = "Init"; | |
| constexpr const char *OLDT_Kernel = "Kernel"; | |
| constexpr const char *OLDT_DataTransfer = "DataTransfer"; | |
| constexpr const char *OLDT_Sync = "Sync"; | |
| constexpr const char *OLDT_Deinit = "Deinit"; | |
| constexpr const char *OLDT_Error = "Error"; | |
| constexpr const char *OLDT_Device = "Device"; | |
| constexpr const char *OLDT_Interface = "Interface"; | |
| constexpr const char *OLDT_Alloc = "Alloc"; | |
| constexpr const char *OLDT_Tool = "Tool"; | |
| constexpr const char *OLDT_Module = "Module"; | |
| } // namespace llvm::offload::debug | |
| namespace llvm::omp::target::debug { | |
| using namespace llvm::offload::debug; | |
| enum OmpDebugLevel : uint32_t { | |
| ODL_Default = 1, | |
| ODL_Error = ODL_Default, | |
| ODL_Detailed = 2, | |
| ODL_Verbose = 3, | |
| ODL_VeryVerbose = 4, | |
| ODL_Dumping = 5 | |
| }; | |
| /* Debug types to use in libomptarget */ | |
| constexpr const char *ODT_Init = OLDT_Init; | |
| constexpr const char *ODT_Mapping = "Mapping"; | |
| constexpr const char *ODT_Kernel = OLDT_Kernel; | |
| constexpr const char *ODT_DataTransfer = OLDT_DataTransfer; | |
| constexpr const char *ODT_Sync = OLDT_Sync; | |
| constexpr const char *ODT_Deinit = OLDT_Deinit; | |
| constexpr const char *ODT_Error = OLDT_Error; | |
| constexpr const char *ODT_KernelArgs = "KernelArgs"; | |
| constexpr const char *ODT_MappingExists = "MappingExists"; | |
| constexpr const char *ODT_DumpTable = "DumpTable"; | |
| constexpr const char *ODT_MappingChanged = "MappingChanged"; | |
| constexpr const char *ODT_PluginKernel = "PluginKernel"; | |
| constexpr const char *ODT_EmptyMapping = "EmptyMapping"; | |
| constexpr const char *ODT_Device = OLDT_Device; | |
| constexpr const char *ODT_Interface = OLDT_Interface; | |
| constexpr const char *ODT_Alloc = OLDT_Alloc; | |
| constexpr const char *ODT_Tool = OLDT_Tool; | |
| constexpr const char *ODT_Module = OLDT_Module; | |
| constexpr const char *ODT_Interop = "Interop"; | |
| static inline odbg_ostream reportErrorStream() { | |
| #ifdef OMPTARGET_DEBUG | |
| if (::llvm::offload::debug::isDebugEnabled()) { | |
| uint32_t RealLevel = ODL_Error; | |
| if (::llvm::offload::debug::shouldPrintDebug(GETNAME(TARGET_NAME), | |
| (ODT_Error), RealLevel)) | |
| return odbg_ostream{ | |
| ::llvm::offload::debug::computePrefix(DEBUG_PREFIX, ODT_Error), | |
| ::llvm::offload::debug::dbgs(), RealLevel}; | |
| else | |
| return odbg_ostream{"", ::llvm::nulls(), 1}; | |
| } | |
| #endif | |
| return odbg_ostream{GETNAME(TARGET_NAME) " error: ", | |
| ::llvm::offload::debug::dbgs(), ODL_Error}; | |
| } | |
| #ifdef OMPTARGET_DEBUG | |
| // Deprecated debug print macros | |
| [[maybe_unused]] static std::string formatToStr(const char *format, ...) { | |
| va_list args; | |
| va_start(args, format); | |
| size_t len = std::vsnprintf(NULL, 0, format, args); | |
| va_end(args); | |
| llvm::SmallVector<char, 128> vec(len + 1); | |
| va_start(args, format); | |
| std::vsnprintf(&vec[0], len + 1, format, args); | |
| va_end(args); | |
| return &vec[0]; | |
| } | |
| // helper macro to support old DP and REPORT macros with printf syntax | |
| #define FORMAT_TO_STR(Format, ...) \ | |
| ::llvm::omp::target::debug::formatToStr(Format __VA_OPT__(, ) __VA_ARGS__) | |
| template <uint32_t InfoId> static constexpr const char *InfoIdToODT() { | |
| constexpr auto getId = []() { | |
| switch (InfoId) { | |
| case OMP_INFOTYPE_KERNEL_ARGS: | |
| return "KernelArgs"; | |
| case OMP_INFOTYPE_MAPPING_EXISTS: | |
| return "MappingExists"; | |
| case OMP_INFOTYPE_DUMP_TABLE: | |
| return "DumpTable"; | |
| case OMP_INFOTYPE_MAPPING_CHANGED: | |
| return "MappingChanged"; | |
| case OMP_INFOTYPE_PLUGIN_KERNEL: | |
| return "PluginKernel"; | |
| case OMP_INFOTYPE_DATA_TRANSFER: | |
| return "DataTransfer"; | |
| case OMP_INFOTYPE_EMPTY_MAPPING: | |
| return "EmptyMapping"; | |
| case OMP_INFOTYPE_ALL: | |
| return "Default"; | |
| } | |
| return static_cast<const char *>(nullptr); | |
| }; | |
| constexpr const char *result = getId(); | |
| static_assert(result != nullptr, "Unknown InfoId being used"); | |
| return result; | |
| } | |
| // Transform the INFO id to the corresponding debug type and print the message | |
| #define INFO_DEBUG_INT(_flags, _id, ...) \ | |
| ODBG(::llvm::omp::target::debug::InfoIdToODT<_flags>()) \ | |
| << FORMAT_TO_STR(__VA_ARGS__); | |
| // Define default format for pointers | |
| static inline raw_ostream &operator<<(raw_ostream &Os, void *Ptr) { | |
| Os << ::llvm::format(DPxMOD, DPxPTR(Ptr)); | |
| return Os; | |
| } | |
| #else | |
| #define INFO_DEBUG_INT(_flags, _id, ...) \ | |
| { \ | |
| } | |
| #endif // OMPTARGET_DEBUG | |
| // New REPORT macro in the same style as ODBG | |
| #define REPORT() ::llvm::omp::target::debug::reportErrorStream() | |
| } // namespace llvm::omp::target::debug | |
| #endif // OMPTARGET_SHARED_DEBUG_H |