[llvm] [OFFLOAD] Add support to dump device images (PR #180545)

via llvm-commits llvm-commits at lists.llvm.org
Mon Feb 9 07:46:02 PST 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-offload

Author: Alex Duran (adurang)

<details>
<summary>Changes</summary>

This enables to get the device images to be dumped to a file at different stages to be used for debugging.

LIBOMPTARGET_DEBUG=BinaryDump will dump the image after it has been loaded on the device.
LIBOMPTARGET_DEBUG=BinaryDump:3 will also dump the image that was passed to the RTL, and the image after JIT was invoked (if JIT was used).

Uses support from #<!-- -->180538.

---
Full diff: https://github.com/llvm/llvm-project/pull/180545.diff


3 Files Affected:

- (modified) offload/include/Shared/Debug.h (+55-25) 
- (added) offload/include/Utils/OsUtils.h (+51) 
- (modified) offload/plugins-nextgen/common/src/PluginInterface.cpp (+28) 


``````````diff
diff --git a/offload/include/Shared/Debug.h b/offload/include/Shared/Debug.h
index 0f98a445c73ea..1517574ade92e 100644
--- a/offload/include/Shared/Debug.h
+++ b/offload/include/Shared/Debug.h
@@ -159,6 +159,28 @@ inline uint32_t getInfoLevel() { return getInfoLevelInternal().load(); }
 
 namespace llvm::offload::debug {
 
+enum OffloadDebugLevel : uint32_t {
+  OLDL_Default = 1,
+  OLDL_Error = OLDL_Default,
+  OLDL_Detailed = 2,
+  OLDL_Verbose = 3,
+  OLDL_VeryVerbose = 4,
+};
+
+// 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";
+constexpr const char *OLDT_BinaryDump = "BinaryDump";
+
 /// 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 {
@@ -271,7 +293,12 @@ struct DebugFilter {
 struct DebugSettings {
   bool Enabled = false;
   uint32_t DefaultLevel = 1;
-  llvm::SmallVector<DebugFilter> Filters;
+  // 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) {
@@ -309,13 +336,13 @@ struct DebugSettings {
 
     Settings.Enabled = true;
 
-    if (EnvRef.starts_with_insensitive("all")) {
-      auto Spec = parseDebugFilter(EnvRef);
-      if (Spec.Type.equals_insensitive("all")) {
-        Settings.DefaultLevel = Spec.Level;
-        return;
-      }
-    }
+    // 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.
+    // Eventuall this should be configured from the upper layers but
+    // for now we can hardcode some excluded types here like:
+    // Settings.ExcludeFilters.push_back(Type);
+    Settings.ExcludeFilters.push_back(OLDT_BinaryDump);
 
     if (!EnvRef.getAsInteger(10, Settings.DefaultLevel))
       return;
@@ -325,7 +352,18 @@ struct DebugSettings {
     for (auto &FilterSpec : llvm::split(EnvRef, ',')) {
       if (FilterSpec.empty())
         continue;
-      Settings.Filters.push_back(parseDebugFilter(FilterSpec));
+      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);
     }
   });
 
@@ -340,7 +378,12 @@ shouldPrintDebug(const char *Component, const char *Type, uint32_t &Level) {
   if (!Settings.Enabled)
     return false;
 
-  if (Settings.Filters.empty()) {
+  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;
@@ -348,10 +391,10 @@ shouldPrintDebug(const char *Component, const char *Type, uint32_t &Level) {
     return false;
   }
 
-  for (const auto &DT : Settings.Filters) {
+  for (const auto &DT : Settings.IncludeFilters) {
     if (DT.Level < Level)
       continue;
-    if (DT.Type.equals_insensitive(Type) ||
+    if (DT.Type.equals_insensitive("all") || DT.Type.equals_insensitive(Type) ||
         DT.Type.equals_insensitive(Component)) {
       Level = DT.Level;
       return true;
@@ -543,19 +586,6 @@ inline bool isDebugEnabled() { return false; }
 
 #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 {
diff --git a/offload/include/Utils/OsUtils.h b/offload/include/Utils/OsUtils.h
new file mode 100644
index 0000000000000..9da9ce69faa4d
--- /dev/null
+++ b/offload/include/Utils/OsUtils.h
@@ -0,0 +1,51 @@
+//===-- Utils/OsUtils.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
+//
+//===----------------------------------------------------------------------===//
+//
+// Useful utilites to interact with the OS environment in a platform independent
+// way.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef OMPTARGET_UTILS_OSUTILS_H
+#define OMPTARGET_UTILS_OSUTILS_H
+
+#ifdef _WIN32
+#include <windows.h>
+#else
+#include <limits.h>
+#include <unistd.h>
+#endif
+
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/Path.h"
+
+namespace utils {
+
+static inline std::string getExecName() {
+#if defined(_WIN32)
+  char Buffer[MAX_PATH];
+  GetModuleFileNameA(nullptr, Buffer, MAX_PATH);
+#else
+  char Buffer[PATH_MAX];
+  ssize_t Len = readlink("/proc/self/exe", Buffer, sizeof(Buffer) - 1);
+  if (Len == -1)
+    return "unknown";
+  Buffer[Len] = '\0';
+#endif
+  llvm::StringRef Path(Buffer);
+
+  if (!Path.empty())
+    return llvm::sys::path::filename(Path).str();
+
+  return "unknown";
+}
+
+} // namespace utils
+
+#endif // OMPTARGET_UTILS_OSUTILS_H
\ No newline at end of file
diff --git a/offload/plugins-nextgen/common/src/PluginInterface.cpp b/offload/plugins-nextgen/common/src/PluginInterface.cpp
index 807df0ffd7874..1ed6aa49490f8 100644
--- a/offload/plugins-nextgen/common/src/PluginInterface.cpp
+++ b/offload/plugins-nextgen/common/src/PluginInterface.cpp
@@ -19,6 +19,7 @@
 #include "JIT.h"
 #include "Shared/Utils.h"
 #include "Utils/ELF.h"
+#include "Utils/OsUtils.h"
 #include "omptarget.h"
 
 #ifdef OMPT_SUPPORT
@@ -851,6 +852,24 @@ Expected<DeviceImageTy *> GenericDeviceTy::loadBinary(GenericPluginTy &Plugin,
                                                       StringRef InputTgtImage) {
   ODBG(OLDT_Init) << "Load data from image "
                   << static_cast<const void *>(InputTgtImage.bytes_begin());
+  auto DumpImage = [](StringRef Label, StringRef Image, llvm::raw_ostream &Os,
+                      int ImageId) {
+    std::string Filename = llvm::formatv(
+        "{0}_{1}_image{2}.bin", utils::getExecName(), Label.str(), ImageId);
+    std::error_code EC;
+    raw_fd_ostream FS(Filename, EC, llvm::sys::fs::OF_None);
+    if (EC) {
+      Os << "Error saving " << Label << " image : " << StringRef(EC.message());
+      return;
+    }
+    FS << Image;
+    FS.close();
+    Os << "Saved " << Label << " image to " << Filename;
+  };
+
+  ODBG_OS(OLDT_BinaryDump, OLDL_Verbose, [&](llvm::raw_ostream &Os) {
+    DumpImage("input", InputTgtImage, Os, LoadedImages.size());
+  });
 
   std::unique_ptr<MemoryBuffer> Buffer;
   if (identify_magic(InputTgtImage) == file_magic::bitcode) {
@@ -861,6 +880,9 @@ Expected<DeviceImageTy *> GenericDeviceTy::loadBinary(GenericPluginTy &Plugin,
                            "failure to jit IR image");
     }
     Buffer = std::move(*CompiledImageOrErr);
+    ODBG_OS(OLDT_BinaryDump, OLDL_Verbose, [&](llvm::raw_ostream &Os) {
+      DumpImage("jitted", Buffer->getBuffer(), Os, LoadedImages.size());
+    });
   } else {
     Buffer = MemoryBuffer::getMemBufferCopy(InputTgtImage);
   }
@@ -871,6 +893,12 @@ Expected<DeviceImageTy *> GenericDeviceTy::loadBinary(GenericPluginTy &Plugin,
   if (!ImageOrErr)
     return ImageOrErr.takeError();
   DeviceImageTy *Image = *ImageOrErr;
+  ODBG_OS(OLDT_BinaryDump, [&](llvm::raw_ostream &Os) {
+    DumpImage("loaded",
+              StringRef(static_cast<const char *>(Image->getStart()),
+                        Image->getSize()),
+              Os, LoadedImages.size());
+  });
 
   // Add the image to list.
   LoadedImages.push_back(Image);

``````````

</details>


https://github.com/llvm/llvm-project/pull/180545


More information about the llvm-commits mailing list