379 lines · c
1//===- ErrorReporting.h - Helper to provide nice error messages ----- 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//===----------------------------------------------------------------------===//10 11#ifndef OFFLOAD_PLUGINS_NEXTGEN_COMMON_ERROR_REPORTING_H12#define OFFLOAD_PLUGINS_NEXTGEN_COMMON_ERROR_REPORTING_H13 14#include "PluginInterface.h"15#include "Shared/EnvironmentVar.h"16 17#include "llvm/ADT/STLExtras.h"18#include "llvm/ADT/SmallString.h"19#include "llvm/ADT/StringRef.h"20#include "llvm/Frontend/OpenMP/OMP.h"21#include "llvm/Support/ErrorHandling.h"22#include "llvm/Support/WithColor.h"23#include "llvm/Support/raw_ostream.h"24 25#include <cstdint>26#include <cstdio>27#include <cstdlib>28#include <functional>29#include <optional>30#include <string>31#include <unistd.h>32 33namespace llvm {34namespace omp {35namespace target {36namespace plugin {37 38class ErrorReporter {39 40 enum ColorTy {41 Yellow = int(HighlightColor::Address),42 Green = int(HighlightColor::String),43 DarkBlue = int(HighlightColor::Tag),44 Cyan = int(HighlightColor::Attribute),45 DarkPurple = int(HighlightColor::Enumerator),46 DarkRed = int(HighlightColor::Macro),47 BoldRed = int(HighlightColor::Error),48 BoldLightPurple = int(HighlightColor::Warning),49 BoldDarkGrey = int(HighlightColor::Note),50 BoldLightBlue = int(HighlightColor::Remark),51 };52 53 /// The banner printed at the beginning of an error report.54 static constexpr auto ErrorBanner = "OFFLOAD ERROR: ";55 56 /// Return the device id as string, or n/a if not available.57 static std::string getDeviceIdStr(GenericDeviceTy *Device) {58 return Device ? std::to_string(Device->getDeviceId()) : "n/a";59 }60 61 /// Return a nice name for an TargetAllocTy.62 static StringRef getAllocTyName(TargetAllocTy Kind) {63 switch (Kind) {64 case TARGET_ALLOC_DEFAULT:65 case TARGET_ALLOC_DEVICE:66 return "device memory";67 case TARGET_ALLOC_HOST:68 return "pinned host memory";69 case TARGET_ALLOC_SHARED:70 return "managed memory";71 break;72 }73 llvm_unreachable("Unknown target alloc kind");74 }75 76#pragma clang diagnostic push77#pragma clang diagnostic ignored "-Wgcc-compat"78#pragma clang diagnostic ignored "-Wformat-security"79 /// Print \p Format, instantiated with \p Args to stderr.80 /// TODO: Allow redirection into a file stream.81 template <typename... ArgsTy>82#ifdef __clang__ // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=7795883 [[gnu::format(__printf__, 1, 2)]]84#endif85 static void print(const char *Format, ArgsTy &&...Args) {86 raw_fd_ostream OS(STDERR_FILENO, false);87 OS << llvm::format(Format, Args...);88 }89 90 /// Print \p Format, instantiated with \p Args to stderr, but colored.91 /// TODO: Allow redirection into a file stream.92 template <typename... ArgsTy>93#ifdef __clang__ // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=7795894 [[gnu::format(__printf__, 2, 3)]]95#endif96 static void print(ColorTy Color, const char *Format, ArgsTy &&...Args) {97 raw_fd_ostream OS(STDERR_FILENO, false);98 WithColor(OS, HighlightColor(Color)) << llvm::format(Format, Args...);99 }100 101 /// Print \p Format, instantiated with \p Args to stderr, but colored and with102 /// a banner.103 /// TODO: Allow redirection into a file stream.104 template <typename... ArgsTy>105#ifdef __clang__ // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=77958106 [[gnu::format(__printf__, 1, 2)]]107#endif108 static void reportError(const char *Format, ArgsTy &&...Args) {109 print(BoldRed, "%s", ErrorBanner);110 print(BoldRed, Format, Args...);111 print("\n");112 }113#pragma clang diagnostic pop114 115 static void reportError(const char *Str) { reportError("%s", Str); }116 static void print(const char *Str) { print("%s", Str); }117 static void print(StringRef Str) { print("%s", Str.str().c_str()); }118 static void print(ColorTy Color, const char *Str) { print(Color, "%s", Str); }119 static void print(ColorTy Color, StringRef Str) {120 print(Color, "%s", Str.str().c_str());121 }122 123 /// Pretty print a stack trace.124 static void reportStackTrace(StringRef StackTrace) {125 if (StackTrace.empty())126 return;127 128 SmallVector<StringRef> Lines, Parts;129 StackTrace.split(Lines, "\n", /*MaxSplit=*/-1, /*KeepEmpty=*/false);130 int Start = Lines.empty() || !Lines[0].contains("PrintStackTrace") ? 0 : 1;131 unsigned NumDigits =132 (int)(floor(log10(Lines.size() - Start - /*0*/ 1)) + 1);133 for (int I = Start, E = Lines.size(); I < E; ++I) {134 auto Line = Lines[I];135 Parts.clear();136 Line = Line.drop_while([](char C) { return std::isspace(C); });137 Line.split(Parts, " ", /*MaxSplit=*/2);138 if (Parts.size() != 3 || Parts[0].size() < 2 || Parts[0][0] != '#') {139 print("%s\n", Line.str().c_str());140 continue;141 }142 unsigned FrameIdx = std::stoi(Parts[0].drop_front(1).str());143 if (Start)144 FrameIdx -= 1;145 print(DarkPurple, " %s", Parts[0].take_front().str().c_str());146 print(Green, "%*u", NumDigits, FrameIdx);147 print(BoldLightBlue, " %s", Parts[1].str().c_str());148 print(" %s\n", Parts[2].str().c_str());149 }150 print("\n");151 }152 153 /// Report information about an allocation associated with \p ATI.154 static void reportAllocationInfo(AllocationTraceInfoTy *ATI) {155 if (!ATI)156 return;157 158 if (!ATI->DeallocationTrace.empty()) {159 print(BoldLightPurple, "Last deallocation:\n");160 reportStackTrace(ATI->DeallocationTrace);161 }162 163 if (ATI->HostPtr)164 print(BoldLightPurple,165 "Last allocation of size %lu for host pointer %p -> device pointer "166 "%p:\n",167 ATI->Size, ATI->HostPtr, ATI->DevicePtr);168 else169 print(BoldLightPurple,170 "Last allocation of size %lu -> device pointer %p:\n", ATI->Size,171 ATI->DevicePtr);172 reportStackTrace(ATI->AllocationTrace);173 if (!ATI->LastAllocationInfo)174 return;175 176 unsigned I = 0;177 print(BoldLightPurple, "Prior allocations with the same base pointer:");178 while (ATI->LastAllocationInfo) {179 print("\n");180 ATI = ATI->LastAllocationInfo;181 print(BoldLightPurple, " #%u Prior deallocation of size %lu:\n", I,182 ATI->Size);183 reportStackTrace(ATI->DeallocationTrace);184 if (ATI->HostPtr)185 print(186 BoldLightPurple,187 " #%u Prior allocation for host pointer %p -> device pointer %p:\n",188 I, ATI->HostPtr, ATI->DevicePtr);189 else190 print(BoldLightPurple, " #%u Prior allocation -> device pointer %p:\n",191 I, ATI->DevicePtr);192 reportStackTrace(ATI->AllocationTrace);193 ++I;194 }195 }196 197 /// End the execution of the program.198 static void abortExecution() { abort(); }199 200public:201#define DEALLOCATION_ERROR(Format, ...) \202 reportError(Format, __VA_ARGS__); \203 reportStackTrace(StackTrace); \204 reportAllocationInfo(ATI); \205 abortExecution();206 207 static void reportDeallocationOfNonAllocatedPtr(void *DevicePtr,208 TargetAllocTy Kind,209 AllocationTraceInfoTy *ATI,210 std::string &StackTrace) {211 DEALLOCATION_ERROR("deallocation of non-allocated %s: %p",212 getAllocTyName(Kind).data(), DevicePtr);213 }214 215 static void reportDeallocationOfDeallocatedPtr(void *DevicePtr,216 TargetAllocTy Kind,217 AllocationTraceInfoTy *ATI,218 std::string &StackTrace) {219 DEALLOCATION_ERROR("double-free of %s: %p", getAllocTyName(Kind).data(),220 DevicePtr);221 }222 223 static void reportDeallocationOfWrongPtrKind(void *DevicePtr,224 TargetAllocTy Kind,225 AllocationTraceInfoTy *ATI,226 std::string &StackTrace) {227 DEALLOCATION_ERROR("deallocation requires %s but allocation was %s: %p",228 getAllocTyName(Kind).data(),229 getAllocTyName(ATI->Kind).data(), DevicePtr);230#undef DEALLOCATION_ERROR231 }232 233 static void reportMemoryAccessError(GenericDeviceTy &Device, void *DevicePtr,234 std::string &ErrorStr, bool Abort) {235 reportError(ErrorStr.c_str());236 237 if (!Device.OMPX_TrackAllocationTraces) {238 print(Yellow, "Use '%s=true' to track device allocations\n",239 Device.OMPX_TrackAllocationTraces.getName().data());240 if (Abort)241 abortExecution();242 return;243 }244 uintptr_t Distance = false;245 auto *ATI =246 Device.getClosestAllocationTraceInfoForAddr(DevicePtr, Distance);247 if (!ATI) {248 print(Cyan,249 "No host-issued allocations; device pointer %p might be "250 "a global, stack, or shared location\n",251 DevicePtr);252 if (Abort)253 abortExecution();254 return;255 }256 if (!Distance) {257 print(Cyan, "Device pointer %p points into%s host-issued allocation:\n",258 DevicePtr, ATI->DeallocationTrace.empty() ? "" : " prior");259 reportAllocationInfo(ATI);260 if (Abort)261 abortExecution();262 return;263 }264 265 bool IsClose = Distance < (1L << 29L /*512MB=*/);266 print(Cyan,267 "Device pointer %p does not point into any (current or prior) "268 "host-issued allocation%s.\n",269 DevicePtr,270 IsClose ? "" : " (might be a global, stack, or shared location)");271 if (IsClose) {272 print(Cyan,273 "Closest host-issued allocation (distance %" PRIuPTR274 " byte%s; might be by page):\n",275 Distance, Distance > 1 ? "s" : "");276 reportAllocationInfo(ATI);277 }278 if (Abort)279 abortExecution();280 }281 282 /// Report that a kernel encountered a trap instruction.283 static void reportTrapInKernel(284 GenericDeviceTy &Device, KernelTraceInfoRecordTy &KTIR,285 std::function<bool(__tgt_async_info &)> AsyncInfoWrapperMatcher) {286 assert(AsyncInfoWrapperMatcher && "A matcher is required");287 288 uint32_t Idx = 0;289 for (uint32_t I = 0, E = KTIR.size(); I < E; ++I) {290 auto KTI = KTIR.getKernelTraceInfo(I);291 if (KTI.Kernel == nullptr)292 break;293 // Skip kernels issued in other queues.294 if (KTI.AsyncInfo && !(AsyncInfoWrapperMatcher(*KTI.AsyncInfo)))295 continue;296 Idx = I;297 break;298 }299 300 auto KTI = KTIR.getKernelTraceInfo(Idx);301 if (KTI.AsyncInfo && (AsyncInfoWrapperMatcher(*KTI.AsyncInfo))) {302 auto PrettyKernelName =303 llvm::omp::prettifyFunctionName(KTI.Kernel->getName());304 reportError("Kernel '%s'", PrettyKernelName.c_str());305 }306 reportError("execution interrupted by hardware trap instruction");307 if (KTI.AsyncInfo && (AsyncInfoWrapperMatcher(*KTI.AsyncInfo))) {308 if (!KTI.LaunchTrace.empty())309 reportStackTrace(KTI.LaunchTrace);310 else311 print(Yellow, "Use '%s=1' to show the stack trace of the kernel\n",312 Device.OMPX_TrackNumKernelLaunches.getName().data());313 }314 abort();315 }316 317 /// Report the kernel traces taken from \p KTIR, up to318 /// OFFLOAD_TRACK_NUM_KERNEL_LAUNCH_TRACES many.319 static void reportKernelTraces(GenericDeviceTy &Device,320 KernelTraceInfoRecordTy &KTIR) {321 uint32_t NumKTIs = 0;322 for (uint32_t I = 0, E = KTIR.size(); I < E; ++I) {323 auto KTI = KTIR.getKernelTraceInfo(I);324 if (KTI.Kernel == nullptr)325 break;326 ++NumKTIs;327 }328 if (NumKTIs == 0) {329 print(BoldRed, "No kernel launches known\n");330 return;331 }332 333 uint32_t TracesToShow =334 std::min(Device.OMPX_TrackNumKernelLaunches.get(), NumKTIs);335 if (TracesToShow == 0) {336 if (NumKTIs == 1)337 print(BoldLightPurple, "Display only launched kernel:\n");338 else339 print(BoldLightPurple, "Display last %u kernels launched:\n", NumKTIs);340 } else {341 if (NumKTIs == 1)342 print(BoldLightPurple, "Display kernel launch trace:\n");343 else344 print(BoldLightPurple,345 "Display %u of the %u last kernel launch traces:\n", TracesToShow,346 NumKTIs);347 }348 349 for (uint32_t Idx = 0, I = 0; I < NumKTIs; ++Idx) {350 auto KTI = KTIR.getKernelTraceInfo(Idx);351 auto PrettyKernelName =352 llvm::omp::prettifyFunctionName(KTI.Kernel->getName());353 if (NumKTIs == 1)354 print(BoldLightPurple, "Kernel '%s'\n", PrettyKernelName.c_str());355 else356 print(BoldLightPurple, "Kernel %d: '%s'\n", I,357 PrettyKernelName.c_str());358 reportStackTrace(KTI.LaunchTrace);359 ++I;360 }361 362 if (NumKTIs != 1) {363 print(Yellow,364 "Use '%s=<num>' to adjust the number of shown stack traces (%u "365 "now, up to %zu)\n",366 Device.OMPX_TrackNumKernelLaunches.getName().data(),367 Device.OMPX_TrackNumKernelLaunches.get(), KTIR.size());368 }369 // TODO: Let users know how to serialize kernels370 }371};372 373} // namespace plugin374} // namespace target375} // namespace omp376} // namespace llvm377 378#endif // OFFLOAD_PLUGINS_NEXTGEN_COMMON_ERROR_REPORTING_H379