//===-- 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 #include #include #include #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 &getInfoLevelInternal() { static std::atomic 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 #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 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 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 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(Os); Dbg.shouldMute(Filter); return Dbg; } static inline raw_ostream &operator<<(raw_ostream &Os, const odbg_ostream::OnlyLevel Filter) { odbg_ostream &Dbg = static_cast(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(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) // helper templates to support lambdas with different number of arguments template struct LambdaHelper { template static constexpr size_t CountArgs(RetTy (FuncTy::*)(Args...)) { return sizeof...(Args); } template static constexpr size_t CountArgs(RetTy (FuncTy::*)(Args...) const) { return sizeof...(Args); } static constexpr size_t NArgs = CountArgs(&LambdaTy::operator()); }; template struct LambdaOs : public LambdaHelper { static void dispatch(LambdaTy func, llvm::raw_ostream &Os, uint32_t Level) { if constexpr (LambdaHelper::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::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 struct LambdaIf : public LambdaHelper { static void dispatch(LambdaTy func, uint32_t Level) { if constexpr (LambdaHelper::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::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 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 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(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