Skip to content

Commit 66ddc9b

Browse files
authored
[OFFLOAD] Add support for more fine grained debug messages control (#165416)
This PR introduces new debug macros that allow a more fined control of which debug message to output and introduce C++ stream style for debug messages. Changing existing messages (except a few that I changed for testing) will come in subsequent PRs. I also think that we should make debug enabling OpenMP agnostic but, for now, I prioritized maintaing the current libomptarget behavior for now, and we might need more changes further down the line as we we decouple libomptarget.
1 parent a070240 commit 66ddc9b

File tree

4 files changed

+285
-2
lines changed

4 files changed

+285
-2
lines changed

offload/include/Shared/Debug.h

Lines changed: 281 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,9 @@
4242
#include <mutex>
4343
#include <string>
4444

45+
#include "llvm/ADT/StringExtras.h"
46+
#include "llvm/Support/circular_raw_ostream.h"
47+
4548
/// 32-Bit field data attributes controlling information presented to the user.
4649
enum OpenMPInfoType : uint32_t {
4750
// Print data arguments and attributes upon entering an OpenMP device kernel.
@@ -198,4 +201,282 @@ inline uint32_t getDebugLevel() {
198201
} \
199202
} while (false)
200203

204+
namespace llvm::offload::debug {
205+
206+
#ifdef OMPTARGET_DEBUG
207+
208+
struct DebugFilter {
209+
StringRef Type;
210+
uint32_t Level;
211+
};
212+
213+
struct DebugSettings {
214+
bool Enabled = false;
215+
uint32_t DefaultLevel = 1;
216+
llvm::SmallVector<DebugFilter> Filters;
217+
};
218+
219+
/// dbgs - Return a circular-buffered debug stream.
220+
[[maybe_unused]] static llvm::raw_ostream &dbgs() {
221+
// Do one-time initialization in a thread-safe way.
222+
static struct dbgstream {
223+
llvm::circular_raw_ostream strm;
224+
225+
dbgstream() : strm(llvm::errs(), "*** Debug Log Output ***\n", 0) {}
226+
} thestrm;
227+
228+
return thestrm.strm;
229+
}
230+
231+
[[maybe_unused]] static DebugFilter parseDebugFilter(StringRef Filter) {
232+
size_t Pos = Filter.find(':');
233+
if (Pos == StringRef::npos)
234+
return {Filter, 1};
235+
236+
StringRef Type = Filter.slice(0, Pos);
237+
uint32_t Level = 1;
238+
if (Filter.slice(Pos + 1, Filter.size()).getAsInteger(10, Level))
239+
Level = 1;
240+
241+
return {Type, Level};
242+
}
243+
244+
[[maybe_unused]] static DebugSettings &getDebugSettings() {
245+
static DebugSettings Settings;
246+
static std::once_flag Flag{};
247+
std::call_once(Flag, []() {
248+
// Eventually, we probably should allow the upper layers to set
249+
// debug settings directly according to their own env var or
250+
// other methods.
251+
// For now, mantain compatibility with existing libomptarget env var
252+
// and add a liboffload independent one.
253+
char *Env = getenv("LIBOMPTARGET_DEBUG");
254+
if (!Env) {
255+
Env = getenv("LIBOFFLOAD_DEBUG");
256+
if (!Env)
257+
return;
258+
}
259+
260+
StringRef EnvRef(Env);
261+
if (EnvRef == "0")
262+
return;
263+
264+
Settings.Enabled = true;
265+
if (EnvRef.equals_insensitive("all"))
266+
return;
267+
268+
if (!EnvRef.getAsInteger(10, Settings.DefaultLevel))
269+
return;
270+
271+
Settings.DefaultLevel = 1;
272+
273+
for (auto &FilterSpec : llvm::split(EnvRef, ',')) {
274+
if (FilterSpec.empty())
275+
continue;
276+
Settings.Filters.push_back(parseDebugFilter(FilterSpec));
277+
}
278+
});
279+
280+
return Settings;
281+
}
282+
283+
inline bool isDebugEnabled() { return getDebugSettings().Enabled; }
284+
285+
[[maybe_unused]] static bool
286+
shouldPrintDebug(const char *Component, const char *Type, uint32_t &Level) {
287+
const auto &Settings = getDebugSettings();
288+
if (!Settings.Enabled)
289+
return false;
290+
291+
if (Settings.Filters.empty()) {
292+
if (Level <= Settings.DefaultLevel) {
293+
Level = Settings.DefaultLevel;
294+
return true;
295+
}
296+
return false;
297+
}
298+
299+
for (const auto &DT : Settings.Filters) {
300+
if (DT.Level < Level)
301+
continue;
302+
if (DT.Type.equals_insensitive(Type) ||
303+
DT.Type.equals_insensitive(Component)) {
304+
Level = DT.Level;
305+
return true;
306+
}
307+
}
308+
309+
return false;
310+
}
311+
312+
/// A raw_ostream that tracks `\n` and print the prefix after each
313+
/// newline. Based on raw_ldbg_ostream from Support/DebugLog.h
314+
class LLVM_ABI odbg_ostream final : public raw_ostream {
315+
public:
316+
enum IfLevel : uint32_t;
317+
enum OnlyLevel : uint32_t;
318+
319+
private:
320+
std::string Prefix;
321+
raw_ostream &Os;
322+
uint32_t BaseLevel;
323+
bool ShouldPrefixNextString;
324+
bool ShouldEmitNewLineOnDestruction;
325+
326+
/// If the stream is muted, writes to it are ignored
327+
bool Muted = false;
328+
329+
/// Split the line on newlines and insert the prefix before each
330+
/// newline. Forward everything to the underlying stream.
331+
void write_impl(const char *Ptr, size_t Size) final {
332+
if (Muted)
333+
return;
334+
335+
auto Str = StringRef(Ptr, Size);
336+
auto Eol = Str.find('\n');
337+
// Handle `\n` occurring in the string, ensure to print the prefix at the
338+
// beginning of each line.
339+
while (Eol != StringRef::npos) {
340+
// Take the line up to the newline (including the newline).
341+
StringRef Line = Str.take_front(Eol + 1);
342+
if (!Line.empty())
343+
writeWithPrefix(Line);
344+
// We printed a newline, record here to print a prefix.
345+
ShouldPrefixNextString = true;
346+
Str = Str.drop_front(Eol + 1);
347+
Eol = Str.find('\n');
348+
}
349+
if (!Str.empty())
350+
writeWithPrefix(Str);
351+
}
352+
void emitPrefix() { Os.write(Prefix.c_str(), Prefix.size()); }
353+
void writeWithPrefix(StringRef Str) {
354+
if (ShouldPrefixNextString) {
355+
emitPrefix();
356+
ShouldPrefixNextString = false;
357+
}
358+
Os.write(Str.data(), Str.size());
359+
}
360+
361+
public:
362+
explicit odbg_ostream(std::string Prefix, raw_ostream &Os, uint32_t BaseLevel,
363+
bool ShouldPrefixNextString = true,
364+
bool ShouldEmitNewLineOnDestruction = false)
365+
: Prefix(std::move(Prefix)), Os(Os), BaseLevel(BaseLevel),
366+
ShouldPrefixNextString(ShouldPrefixNextString),
367+
ShouldEmitNewLineOnDestruction(ShouldEmitNewLineOnDestruction) {
368+
SetUnbuffered();
369+
}
370+
~odbg_ostream() final {
371+
if (ShouldEmitNewLineOnDestruction)
372+
Os << '\n';
373+
}
374+
375+
/// Forward the current_pos method to the underlying stream.
376+
uint64_t current_pos() const final { return Os.tell(); }
377+
378+
/// Some of the `<<` operators expect an lvalue, so we trick the type
379+
/// system.
380+
odbg_ostream &asLvalue() { return *this; }
381+
382+
void shouldMute(const IfLevel Filter) { Muted = Filter > BaseLevel; }
383+
void shouldMute(const OnlyLevel Filter) { Muted = BaseLevel != Filter; }
384+
};
385+
386+
/// Compute the prefix for the debug log in the form of:
387+
/// "Component --> "
388+
[[maybe_unused]] static std::string computePrefix(StringRef Component,
389+
StringRef DebugType) {
390+
std::string Prefix;
391+
raw_string_ostream OsPrefix(Prefix);
392+
OsPrefix << Component << " --> ";
393+
return OsPrefix.str();
394+
}
395+
396+
static inline raw_ostream &operator<<(raw_ostream &Os,
397+
const odbg_ostream::IfLevel Filter) {
398+
odbg_ostream &Dbg = static_cast<odbg_ostream &>(Os);
399+
Dbg.shouldMute(Filter);
400+
return Dbg;
401+
}
402+
403+
static inline raw_ostream &operator<<(raw_ostream &Os,
404+
const odbg_ostream::OnlyLevel Filter) {
405+
odbg_ostream &Dbg = static_cast<odbg_ostream &>(Os);
406+
Dbg.shouldMute(Filter);
407+
return Dbg;
408+
}
409+
410+
#define ODBG_BASE(Stream, Component, Prefix, Type, Level) \
411+
for (uint32_t RealLevel = (Level), \
412+
_c = llvm::offload::debug::isDebugEnabled() && \
413+
llvm::offload::debug::shouldPrintDebug( \
414+
(Component), (Type), RealLevel); \
415+
_c; _c = 0) \
416+
::llvm::offload::debug::odbg_ostream{ \
417+
::llvm::offload::debug::computePrefix((Prefix), (Type)), (Stream), \
418+
RealLevel, /*ShouldPrefixNextString=*/true, \
419+
/*ShouldEmitNewLineOnDestruction=*/true} \
420+
.asLvalue()
421+
422+
#define ODBG_STREAM(Stream, Type, Level) \
423+
ODBG_BASE(Stream, GETNAME(TARGET_NAME), DEBUG_PREFIX, Type, Level)
424+
425+
#define ODBG_0() ODBG_2("default", 1)
426+
#define ODBG_1(Type) ODBG_2(Type, 1)
427+
#define ODBG_2(Type, Level) \
428+
ODBG_STREAM(llvm::offload::debug::dbgs(), Type, Level)
429+
#define ODBG_SELECT(Type, Level, NArgs, ...) ODBG_##NArgs
430+
431+
// Print a debug message of a certain type and verbosity level. If no type
432+
// or level is provided, "default" and "1" are assumed respectively.
433+
// Usage examples:
434+
// ODBG("type1", 2) << "This is a level 2 message of type1";
435+
// ODBG("Init") << "This is a default level of the init type";
436+
// ODBG() << "This is a level 1 message of the default type";
437+
// ODBG("Init", 3) << NumDevices << " were initialized";
438+
// ODBG("Kernel") << "Launching " << KernelName << " on device " << DeviceId;
439+
#define ODBG(...) ODBG_SELECT(__VA_ARGS__ __VA_OPT__(, ) 2, 1, 0)(__VA_ARGS__)
440+
441+
// Filter the next elements in the debug stream if the current debug level is
442+
// lower than specified level. Example:
443+
// ODBG("Mapping", 2) << "level 2 info "
444+
// << ODBG_IF_LEVEL(3) << " level 3 info" << Arg
445+
// << ODBG_IF_LEVEL(4) << " level 4 info" << &Arg
446+
// << ODBG_RESET_LEVEL() << " more level 2 info";
447+
#define ODBG_IF_LEVEL(Level) \
448+
static_cast<llvm::offload::debug::odbg_ostream::IfLevel>(Level)
449+
450+
// Filter the next elements in the debug stream if the current debug level is
451+
// not exactly the specified level. Example:
452+
// ODBG() << "Starting computation "
453+
// << ODBG_ONLY_LEVEL(1) << "on a device"
454+
// << ODBG_ONLY_LEVEL(2) << "and mapping data on device" << DeviceId;
455+
// << ODBG_ONLY_LEVEL(3) << dumpDetailedMappingInfo(DeviceId);
456+
#define ODBG_ONLY_LEVEL(Level) \
457+
static_cast<llvm::offload::debug::odbg_ostream::OnlyLevel>(Level)
458+
459+
// Reset the level back to the original level after ODBG_IF_LEVEL or
460+
// ODBG_ONLY_LEVEL have been used
461+
#define ODBG_RESET_LEVEL() \
462+
static_cast<llvm::offload::debug::odbg_ostream::IfLevel>(0)
463+
464+
#else
465+
466+
#define ODBG_NULL \
467+
for (bool _c = false; _c; _c = false) \
468+
::llvm::nulls()
469+
470+
// Don't print anything if debugging is disabled
471+
#define ODBG_BASE(Stream, Component, Prefix, Type, Level) ODBG_NULL
472+
#define ODBG_STREAM(Stream, Type, Level) ODBG_NULL
473+
#define ODBG_IF_LEVEL(Level) 0
474+
#define ODBG_ONLY_LEVEL(Level) 0
475+
#define ODBG_RESET_LEVEL() 0
476+
#define ODBG(...) ODBG_NULL
477+
478+
#endif
479+
480+
} // namespace llvm::offload::debug
481+
201482
#endif // OMPTARGET_SHARED_DEBUG_H

offload/libomptarget/OffloadRTL.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ void initRuntime() {
3535

3636
RefCount++;
3737
if (RefCount == 1) {
38-
DP("Init offload library!\n");
38+
ODBG() << "Init offload library!";
3939
#ifdef OMPT_SUPPORT
4040
// Initialize OMPT first
4141
llvm::omp::target::ompt::connectLibrary();

offload/libomptarget/PluginManager.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ void PluginManager::init() {
3636
return;
3737
}
3838

39-
DP("Loading RTLs...\n");
39+
ODBG("Init") << "Loading RTLs";
4040

4141
// Attempt to create an instance of each supported plugin.
4242
#define PLUGIN_TARGET(Name) \

offload/plugins-nextgen/host/src/rtl.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,8 @@ struct GenELF64PluginTy final : public GenericPluginTy {
443443
if (auto Err = Plugin::check(ffi_init(), "failed to initialize libffi"))
444444
return std::move(Err);
445445
#endif
446+
ODBG("Init") << "GenELF64 plugin detected " << ODBG_IF_LEVEL(2)
447+
<< NUM_DEVICES << " " << ODBG_RESET_LEVEL() << "devices";
446448

447449
return NUM_DEVICES;
448450
}

0 commit comments

Comments
 (0)