brintos

brintos / llvm-project-archived public Read only

0
0
Text · 18.3 KiB · 41613a3 Raw
483 lines · c
1//===-- Shared/Debug.h - Target independent OpenMP target RTL -- C++ ------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// Routines used to provide debug messages and information from libomptarget10// and plugin RTLs to the user.11//12// Each plugin RTL and libomptarget define TARGET_NAME and DEBUG_PREFIX for use13// when sending messages to the user. These indicate which RTL sent the message14//15// Debug and information messages are controlled by the environment variables16// LIBOMPTARGET_DEBUG and LIBOMPTARGET_INFO which is set upon initialization17// of libomptarget or the plugin RTL.18//19// To printf a pointer in hex with a fixed width of 16 digits and a leading 0x,20// use printf("ptr=" DPxMOD "...\n", DPxPTR(ptr));21//22// DPxMOD expands to:23//   "0x%0*" PRIxPTR24// where PRIxPTR expands to an appropriate modifier for the type uintptr_t on a25// specific platform, e.g. "lu" if uintptr_t is typedef'd as unsigned long:26//   "0x%0*lu"27//28// Ultimately, the whole statement expands to:29//   printf("ptr=0x%0*lu...\n",  // the 0* modifier expects an extra argument30//                               // specifying the width of the output31//   (int)(2*sizeof(uintptr_t)), // the extra argument specifying the width32//                               // 8 digits for 32bit systems33//                               // 16 digits for 64bit34//   (uintptr_t) ptr);35//36//===----------------------------------------------------------------------===//37 38#ifndef OMPTARGET_SHARED_DEBUG_H39#define OMPTARGET_SHARED_DEBUG_H40 41#include <atomic>42#include <mutex>43#include <string>44 45#include "llvm/ADT/StringExtras.h"46#include "llvm/Support/circular_raw_ostream.h"47 48/// 32-Bit field data attributes controlling information presented to the user.49enum OpenMPInfoType : uint32_t {50  // Print data arguments and attributes upon entering an OpenMP device kernel.51  OMP_INFOTYPE_KERNEL_ARGS = 0x0001,52  // Indicate when an address already exists in the device mapping table.53  OMP_INFOTYPE_MAPPING_EXISTS = 0x0002,54  // Dump the contents of the device pointer map at kernel exit or failure.55  OMP_INFOTYPE_DUMP_TABLE = 0x0004,56  // Indicate when an address is added to the device mapping table.57  OMP_INFOTYPE_MAPPING_CHANGED = 0x0008,58  // Print kernel information from target device plugins.59  OMP_INFOTYPE_PLUGIN_KERNEL = 0x0010,60  // Print whenever data is transferred to the device61  OMP_INFOTYPE_DATA_TRANSFER = 0x0020,62  // Print whenever data does not have a viable device counterpart.63  OMP_INFOTYPE_EMPTY_MAPPING = 0x0040,64  // Enable every flag.65  OMP_INFOTYPE_ALL = 0xffffffff,66};67 68inline std::atomic<uint32_t> &getInfoLevelInternal() {69  static std::atomic<uint32_t> InfoLevel;70  static std::once_flag Flag{};71  std::call_once(Flag, []() {72    if (char *EnvStr = getenv("LIBOMPTARGET_INFO"))73      InfoLevel.store(std::stoi(EnvStr));74  });75 76  return InfoLevel;77}78 79inline uint32_t getInfoLevel() { return getInfoLevelInternal().load(); }80 81inline uint32_t getDebugLevel() {82  static uint32_t DebugLevel = 0;83  static std::once_flag Flag{};84  std::call_once(Flag, []() {85    if (char *EnvStr = getenv("LIBOMPTARGET_DEBUG"))86      DebugLevel = std::stoi(EnvStr);87  });88 89  return DebugLevel;90}91 92#undef USED93#undef GCC_VERSION94 95#ifndef __STDC_FORMAT_MACROS96#define __STDC_FORMAT_MACROS97#endif98#include <inttypes.h>99#undef __STDC_FORMAT_MACROS100 101#define DPxMOD "0x%0*" PRIxPTR102#define DPxPTR(ptr) ((int)(2 * sizeof(uintptr_t))), ((uintptr_t)(ptr))103#define GETNAME2(name) #name104#define GETNAME(name) GETNAME2(name)105 106/// Print a generic message string from libomptarget or a plugin RTL107#define MESSAGE0(_str)                                                         \108  do {                                                                         \109    fprintf(stderr, GETNAME(TARGET_NAME) " message: %s\n", _str);              \110  } while (0)111 112/// Print a printf formatting string message from libomptarget or a plugin RTL113#define MESSAGE(_str, ...)                                                     \114  do {                                                                         \115    fprintf(stderr, GETNAME(TARGET_NAME) " message: " _str "\n", __VA_ARGS__); \116  } while (0)117 118/// Print fatal error message with an error string and error identifier119#define FATAL_MESSAGE0(_num, _str)                                             \120  do {                                                                         \121    fprintf(stderr, GETNAME(TARGET_NAME) " fatal error %d: %s\n", (int)_num,   \122            _str);                                                             \123    abort();                                                                   \124  } while (0)125 126/// Print fatal error message with a printf string and error identifier127#define FATAL_MESSAGE(_num, _str, ...)                                         \128  do {                                                                         \129    fprintf(stderr, GETNAME(TARGET_NAME) " fatal error %d: " _str "\n",        \130            (int)_num, __VA_ARGS__);                                           \131    abort();                                                                   \132  } while (0)133 134/// Print a generic error string from libomptarget or a plugin RTL135#define FAILURE_MESSAGE(...)                                                   \136  do {                                                                         \137    fprintf(stderr, GETNAME(TARGET_NAME) " error: ");                          \138    fprintf(stderr, __VA_ARGS__);                                              \139  } while (0)140 141/// Print a generic information string used if LIBOMPTARGET_INFO=1142#define INFO_MESSAGE(_num, ...) INFO_MESSAGE_TO(stderr, _num, __VA_ARGS__)143 144#define INFO_MESSAGE_TO(_stdDst, _num, ...)                                    \145  do {                                                                         \146    fprintf(_stdDst, GETNAME(TARGET_NAME) " device %d info: ", (int)_num);     \147    fprintf(_stdDst, __VA_ARGS__);                                             \148  } while (0)149 150// Debugging messages151#ifdef OMPTARGET_DEBUG152#include <stdio.h>153 154#define DEBUGP(prefix, ...)                                                    \155  {                                                                            \156    fprintf(stderr, "%s --> ", prefix);                                        \157    fprintf(stderr, __VA_ARGS__);                                              \158  }159 160/// Emit a message for debugging161#define DP(...)                                                                \162  do {                                                                         \163    if (getDebugLevel() > 0) {                                                 \164      DEBUGP(DEBUG_PREFIX, __VA_ARGS__);                                       \165    }                                                                          \166  } while (false)167 168/// Emit a message for debugging or failure if debugging is disabled169#define REPORT(...)                                                            \170  do {                                                                         \171    if (getDebugLevel() > 0) {                                                 \172      DP(__VA_ARGS__);                                                         \173    } else {                                                                   \174      FAILURE_MESSAGE(__VA_ARGS__);                                            \175    }                                                                          \176  } while (false)177#else178#define DEBUGP(prefix, ...)                                                    \179  {}180#define DP(...)                                                                \181  {}182#define REPORT(...) FAILURE_MESSAGE(__VA_ARGS__);183#endif // OMPTARGET_DEBUG184 185/// Emit a message giving the user extra information about the runtime if186#define INFO(_flags, _id, ...)                                                 \187  do {                                                                         \188    if (getDebugLevel() > 0) {                                                 \189      DEBUGP(DEBUG_PREFIX, __VA_ARGS__);                                       \190    } else if (getInfoLevel() & _flags) {                                      \191      INFO_MESSAGE(_id, __VA_ARGS__);                                          \192    }                                                                          \193  } while (false)194 195#define DUMP_INFO(toStdOut, _flags, _id, ...)                                  \196  do {                                                                         \197    if (toStdOut) {                                                            \198      INFO_MESSAGE_TO(stdout, _id, __VA_ARGS__);                               \199    } else {                                                                   \200      INFO(_flags, _id, __VA_ARGS__);                                          \201    }                                                                          \202  } while (false)203 204namespace llvm::offload::debug {205 206#ifdef OMPTARGET_DEBUG207 208struct DebugFilter {209  StringRef Type;210  uint32_t Level;211};212 213struct 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 set249    // debug settings directly according to their own env var or250    // other methods.251    // For now, mantain compatibility with existing libomptarget env var252    // 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 283inline bool isDebugEnabled() { return getDebugSettings().Enabled; }284 285[[maybe_unused]] static bool286shouldPrintDebug(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 each313/// newline. Based on raw_ldbg_ostream from Support/DebugLog.h314class LLVM_ABI odbg_ostream final : public raw_ostream {315public:316  enum IfLevel : uint32_t;317  enum OnlyLevel : uint32_t;318 319private: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 ignored327  bool Muted = false;328 329  /// Split the line on newlines and insert the prefix before each330  /// 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 the338    // 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 361public: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 type379  /// 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 396static 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 403static 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_##NArgs430 431// Print a debug message of a certain type and verbosity level. If no type432// 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 is442// lower than  specified level. Example:443// ODBG("Mapping", 2) << "level 2 info "444//   << ODBG_IF_LEVEL(3) << " level 3 info" << Arg445//   << ODBG_IF_LEVEL(4) << " level 4 info" << &Arg446//   << 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 is451// 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 or460// ODBG_ONLY_LEVEL have been used461#define ODBG_RESET_LEVEL()                                                     \462  static_cast<llvm::offload::debug::odbg_ostream::IfLevel>(0)463 464#else465 466#define ODBG_NULL                                                              \467  for (bool _c = false; _c; _c = false)                                        \468  ::llvm::nulls()469 470// Don't print anything if debugging is disabled471#define ODBG_BASE(Stream, Component, Prefix, Type, Level) ODBG_NULL472#define ODBG_STREAM(Stream, Type, Level) ODBG_NULL473#define ODBG_IF_LEVEL(Level) 0474#define ODBG_ONLY_LEVEL(Level) 0475#define ODBG_RESET_LEVEL() 0476#define ODBG(...) ODBG_NULL477 478#endif479 480} // namespace llvm::offload::debug481 482#endif // OMPTARGET_SHARED_DEBUG_H483