Allow a to define a set of Types that are not shown by default when doing default debug loggin (e.g., LIBOMPTARGET_DEBUG=All). Users can enable output of those types of messages by explicitly adding them to LIBOMPTARGET_DEBUG. Used to implement: #180545 --------- Co-authored-by: Michael Klemm <michael.klemm@amd.com>
702 lines
27 KiB
C++
702 lines
27 KiB
C++
//===-- 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
|