[llvm] [gsymutil] Add parameter into `GsymReader` to disable readahead (PR #199230)

Roy Shi via llvm-commits llvm-commits at lists.llvm.org
Fri May 22 09:29:54 PDT 2026


https://github.com/royitaqi updated https://github.com/llvm/llvm-project/pull/199230

>From 61d8c091dd33e15b00b9be9ca93e1f340bffb601 Mon Sep 17 00:00:00 2001
From: Roy Shi <royshi at meta.com>
Date: Thu, 21 May 2026 11:16:32 -0700
Subject: [PATCH 1/6] Disable readahead

---
 llvm/lib/DebugInfo/GSYM/GsymReader.cpp | 8 +++++++-
 1 file changed, 7 insertions(+), 1 deletion(-)

diff --git a/llvm/lib/DebugInfo/GSYM/GsymReader.cpp b/llvm/lib/DebugInfo/GSYM/GsymReader.cpp
index b68588fb24f75..06252b86623b2 100644
--- a/llvm/lib/DebugInfo/GSYM/GsymReader.cpp
+++ b/llvm/lib/DebugInfo/GSYM/GsymReader.cpp
@@ -12,6 +12,7 @@
 #include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <sys/mman.h>
 
 #include "llvm/DebugInfo/GSYM/GsymReaderV1.h"
 #include "llvm/DebugInfo/GSYM/GsymReaderV2.h"
@@ -71,7 +72,12 @@ GsymReader::openFile(StringRef Filename) {
   auto Err = BuffOrErr.getError();
   if (Err)
     return llvm::errorCodeToError(Err);
-  return create(BuffOrErr.get());
+  // Disable readahead for benchmarking random access patterns.
+  auto &Buf = BuffOrErr.get();
+  madvise(const_cast<void *>(
+              static_cast<const void *>(Buf->getBufferStart())),
+          Buf->getBufferSize(), MADV_RANDOM);
+  return create(Buf);
 }
 
 llvm::Expected<std::unique_ptr<GsymReader>>

>From f926f77b87b7f9573bbda5abed364c5017af1818 Mon Sep 17 00:00:00 2001
From: Roy Shi <royshi at meta.com>
Date: Thu, 21 May 2026 12:58:12 -0700
Subject: [PATCH 2/6] Add --benchmark-reader=start,stride parameters

---
 llvm/tools/llvm-gsymutil/Opts.td           |  8 +++-
 llvm/tools/llvm-gsymutil/llvm-gsymutil.cpp | 50 +++++++++++++++++++---
 2 files changed, 49 insertions(+), 9 deletions(-)

diff --git a/llvm/tools/llvm-gsymutil/Opts.td b/llvm/tools/llvm-gsymutil/Opts.td
index f2bf5e0783671..2b211eec9a96d 100644
--- a/llvm/tools/llvm-gsymutil/Opts.td
+++ b/llvm/tools/llvm-gsymutil/Opts.td
@@ -60,6 +60,10 @@ defm output_version :
   Eq<"output-version",
      "Set the GSYM output version (1 or 2). Default: 1.">;
 def benchmark_reader :
-  FF<"benchmark-reader",
-     "Open a GSYM file and perform a lookup for every function start address to benchmark the reader performance">,
+  Joined<["--"], "benchmark-reader=">,
+  HelpText<"Benchmark reader with start,stride (default: 0,1 = every address)">,
+  Flags<[HelpHidden]>;
+def benchmark_reader_all :
+  Flag<["--"], "benchmark-reader">,
+  HelpText<"Benchmark reader by looking up every address">,
   Flags<[HelpHidden]>;
diff --git a/llvm/tools/llvm-gsymutil/llvm-gsymutil.cpp b/llvm/tools/llvm-gsymutil/llvm-gsymutil.cpp
index 33a5a2224e3f9..6dd5ea8569e72 100644
--- a/llvm/tools/llvm-gsymutil/llvm-gsymutil.cpp
+++ b/llvm/tools/llvm-gsymutil/llvm-gsymutil.cpp
@@ -98,6 +98,8 @@ static std::string OutputFilename;
 static std::string JsonSummaryFile;
 static bool Verify;
 static bool BenchmarkReader;
+static uint32_t BenchmarkStart;
+static uint32_t BenchmarkStride;
 static unsigned NumThreads;
 static uint64_t SegmentSize;
 static bool Quiet;
@@ -161,7 +163,37 @@ static void parseArgs(int argc, char **argv) {
     JsonSummaryFile = A->getValue();
 
   Verify = Args.hasArg(OPT_verify);
-  BenchmarkReader = Args.hasArg(OPT_benchmark_reader);
+  BenchmarkStart = 0;
+  BenchmarkStride = 1;
+  if (Args.hasArg(OPT_benchmark_reader_all)) {
+    BenchmarkReader = true;
+  } else if (const llvm::opt::Arg *A =
+                 Args.getLastArg(OPT_benchmark_reader)) {
+    BenchmarkReader = true;
+    StringRef S{A->getValue()};
+    if (!S.empty()) {
+      auto [StartStr, StrideStr] = S.split(',');
+      if (!llvm::to_integer(StartStr, BenchmarkStart, 0)) {
+        llvm::errs() << ToolName
+                     << ": for the --benchmark-reader option: invalid start '"
+                     << StartStr << "'\n";
+        std::exit(1);
+      }
+      if (!StrideStr.empty() &&
+          !llvm::to_integer(StrideStr, BenchmarkStride, 0)) {
+        llvm::errs() << ToolName
+                     << ": for the --benchmark-reader option: invalid stride '"
+                     << StrideStr << "'\n";
+        std::exit(1);
+      }
+      if (BenchmarkStride == 0) {
+        llvm::errs() << ToolName
+                     << ": for the --benchmark-reader option: stride must be "
+                        "positive\n";
+        std::exit(1);
+      }
+    }
+  }
 
   if (const llvm::opt::Arg *A = Args.getLastArg(OPT_num_threads_EQ)) {
     StringRef S{A->getValue()};
@@ -731,12 +763,14 @@ static void doLookup(GsymReader &Gsym, uint64_t Addr, raw_ostream &OS) {
   }
 }
 
-static llvm::Error benchmarkReader(StringRef GSYMPath) {
+static llvm::Error benchmarkReader(StringRef GSYMPath, uint32_t Start,
+                                   uint32_t Stride) {
   auto Gsym = GsymReader::openFile(GSYMPath);
   if (!Gsym)
     return Gsym.takeError();
-  auto NumAddrs = (*Gsym)->getNumAddresses();
-  for (uint32_t I = 0; I < NumAddrs; ++I) {
+  uint32_t N = (*Gsym)->getNumAddresses();
+  uint32_t NumLookups = 0;
+  for (uint32_t I = Start; I < N; I += Stride) {
     auto Addr = (*Gsym)->getAddress(I);
     if (!Addr)
       return createStringError(std::errc::invalid_argument,
@@ -744,9 +778,10 @@ static llvm::Error benchmarkReader(StringRef GSYMPath) {
     auto LR = (*Gsym)->lookup(*Addr);
     if (!LR)
       return LR.takeError();
+    ++NumLookups;
   }
-  outs() << "Benchmarked " << NumAddrs << " lookups in \"" << GSYMPath
-         << "\"\n";
+  outs() << "Benchmarked " << NumLookups << " lookups (out of " << N
+         << " addresses) in \"" << GSYMPath << "\"\n";
   return Error::success();
 }
 
@@ -764,7 +799,8 @@ int llvm_gsymutil_main(int argc, char **argv, const llvm::ToolContext &) {
 
   if (BenchmarkReader) {
     for (const auto &GSYMPath : InputFilenames)
-      if (auto Err = benchmarkReader(GSYMPath))
+      if (auto Err =
+              benchmarkReader(GSYMPath, BenchmarkStart, BenchmarkStride))
         error("Benchmark failed: ", std::move(Err));
     return EXIT_SUCCESS;
   }

>From ff56ad776fcff540799dd19aef652ba44054b79b Mon Sep 17 00:00:00 2001
From: Roy Shi <royshi at meta.com>
Date: Fri, 22 May 2026 08:27:21 -0700
Subject: [PATCH 3/6] Move MADV_RANDOM into LLVM's mapped_file_region
 abstraction

Replace the direct madvise() call in GsymReader with a proper
OS-independent API through mapped_file_region and MemoryBuffer,
following the existing dontNeed()/willNeed() pattern.
---
 llvm/include/llvm/Support/FileSystem.h   |  2 ++
 llvm/include/llvm/Support/MemoryBuffer.h |  4 ++++
 llvm/lib/DebugInfo/GSYM/GsymReader.cpp   |  6 +-----
 llvm/lib/Support/MemoryBuffer.cpp        |  1 +
 llvm/lib/Support/Unix/Path.inc           | 13 +++++++++++++
 llvm/lib/Support/Windows/Path.inc        |  2 ++
 6 files changed, 23 insertions(+), 5 deletions(-)

diff --git a/llvm/include/llvm/Support/FileSystem.h b/llvm/include/llvm/Support/FileSystem.h
index 8dd5bb86949fa..6fe795042c6a4 100644
--- a/llvm/include/llvm/Support/FileSystem.h
+++ b/llvm/include/llvm/Support/FileSystem.h
@@ -1333,6 +1333,7 @@ class mapped_file_region {
   LLVM_ABI void unmapImpl();
   LLVM_ABI void dontNeedImpl();
   LLVM_ABI void willNeedImpl();
+  LLVM_ABI void randomAccessImpl();
 
   LLVM_ABI std::error_code init(sys::fs::file_t FD, uint64_t Offset,
                                 mapmode Mode, const char *Name);
@@ -1366,6 +1367,7 @@ class mapped_file_region {
   }
   void dontNeed() { dontNeedImpl(); }
   void willNeed() { willNeedImpl(); }
+  void randomAccess() { randomAccessImpl(); }
 
   LLVM_ABI size_t size() const;
   LLVM_ABI char *data() const;
diff --git a/llvm/include/llvm/Support/MemoryBuffer.h b/llvm/include/llvm/Support/MemoryBuffer.h
index 9b0f1f0f7d34e..77efb49725631 100644
--- a/llvm/include/llvm/Support/MemoryBuffer.h
+++ b/llvm/include/llvm/Support/MemoryBuffer.h
@@ -88,6 +88,10 @@ class LLVM_ABI MemoryBuffer {
   /// This should be use purely as an read optimization.
   virtual void willNeedIfMmap() {}
 
+  /// For read-only MemoryBuffer_MMap, advise the kernel that accesses will be
+  /// random, disabling readahead. This calls madvise(MADV_RANDOM) on *NIX.
+  virtual void randomAccessIfMmap() {}
+
   /// Open the specified file as a MemoryBuffer, returning a new MemoryBuffer
   /// if successful, otherwise returning null.
   ///
diff --git a/llvm/lib/DebugInfo/GSYM/GsymReader.cpp b/llvm/lib/DebugInfo/GSYM/GsymReader.cpp
index 06252b86623b2..92efd66566445 100644
--- a/llvm/lib/DebugInfo/GSYM/GsymReader.cpp
+++ b/llvm/lib/DebugInfo/GSYM/GsymReader.cpp
@@ -12,7 +12,6 @@
 #include <inttypes.h>
 #include <stdio.h>
 #include <stdlib.h>
-#include <sys/mman.h>
 
 #include "llvm/DebugInfo/GSYM/GsymReaderV1.h"
 #include "llvm/DebugInfo/GSYM/GsymReaderV2.h"
@@ -72,11 +71,8 @@ GsymReader::openFile(StringRef Filename) {
   auto Err = BuffOrErr.getError();
   if (Err)
     return llvm::errorCodeToError(Err);
-  // Disable readahead for benchmarking random access patterns.
   auto &Buf = BuffOrErr.get();
-  madvise(const_cast<void *>(
-              static_cast<const void *>(Buf->getBufferStart())),
-          Buf->getBufferSize(), MADV_RANDOM);
+  Buf->randomAccessIfMmap();
   return create(Buf);
 }
 
diff --git a/llvm/lib/Support/MemoryBuffer.cpp b/llvm/lib/Support/MemoryBuffer.cpp
index 8491421762d1d..9c49c143cf01c 100644
--- a/llvm/lib/Support/MemoryBuffer.cpp
+++ b/llvm/lib/Support/MemoryBuffer.cpp
@@ -249,6 +249,7 @@ class MemoryBufferMMapFile : public MB {
 
   void dontNeedIfMmap() override { MFR.dontNeed(); }
   void willNeedIfMmap() override { MFR.willNeed(); }
+  void randomAccessIfMmap() override { MFR.randomAccess(); }
 };
 } // namespace
 
diff --git a/llvm/lib/Support/Unix/Path.inc b/llvm/lib/Support/Unix/Path.inc
index bde8cf1255025..98c210a5d31f7 100644
--- a/llvm/lib/Support/Unix/Path.inc
+++ b/llvm/lib/Support/Unix/Path.inc
@@ -944,6 +944,19 @@ void mapped_file_region::willNeedImpl() {
 #endif
 }
 
+void mapped_file_region::randomAccessImpl() {
+  assert(Mode == mapped_file_region::readonly);
+  if (!Mapping)
+    return;
+#if defined(__MVS__) || defined(_AIX)
+  // If we don't have madvise, or it isn't beneficial, treat this as a no-op.
+#elif defined(POSIX_MADV_RANDOM)
+  ::posix_madvise(Mapping, Size, POSIX_MADV_RANDOM);
+#else
+  ::madvise(Mapping, Size, MADV_RANDOM);
+#endif
+}
+
 int mapped_file_region::alignment() { return Process::getPageSizeEstimate(); }
 
 std::error_code detail::directory_iterator_construct(detail::DirIterState &it,
diff --git a/llvm/lib/Support/Windows/Path.inc b/llvm/lib/Support/Windows/Path.inc
index 7161af15046ab..19897242a9638 100644
--- a/llvm/lib/Support/Windows/Path.inc
+++ b/llvm/lib/Support/Windows/Path.inc
@@ -1183,6 +1183,8 @@ void mapped_file_region::unmapImpl() {
 
 void mapped_file_region::dontNeedImpl() {}
 
+void mapped_file_region::randomAccessImpl() {}
+
 void mapped_file_region::willNeedImpl() {
   struct MEMORY_RANGE_ENTRY {
     PVOID VirtualAddress;

>From def54918cb146b6d8016111e651d7aa23587365e Mon Sep 17 00:00:00 2001
From: Roy Shi <royshi at meta.com>
Date: Fri, 22 May 2026 08:40:28 -0700
Subject: [PATCH 4/6] Add RandomAccess parameter to GsymReader::openFile

Allow callers to opt in to MADV_RANDOM on the memory-mapped file
by passing RandomAccess=true. Default is false (no behavior change).
---
 llvm/include/llvm/DebugInfo/GSYM/GsymReader.h | 4 +++-
 llvm/lib/DebugInfo/GSYM/GsymReader.cpp        | 5 +++--
 2 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/llvm/include/llvm/DebugInfo/GSYM/GsymReader.h b/llvm/include/llvm/DebugInfo/GSYM/GsymReader.h
index b44f895ed9634..3bf2d8a08b3d5 100644
--- a/llvm/include/llvm/DebugInfo/GSYM/GsymReader.h
+++ b/llvm/include/llvm/DebugInfo/GSYM/GsymReader.h
@@ -87,10 +87,12 @@ class GsymReader {
   /// Construct a GsymReader from a file on disk.
   ///
   /// \param Path The file path the GSYM file to read.
+  /// \param RandomAccess If true, advise the kernel that accesses will be
+  /// random, disabling readahead on the memory-mapped file.
   /// \returns An expected GsymReader that contains the object or an error
   /// object that indicates reason for failing to read the GSYM.
   LLVM_ABI static llvm::Expected<std::unique_ptr<GsymReader>>
-  openFile(StringRef Path);
+  openFile(StringRef Path, bool RandomAccess = false);
 
   /// Construct a GsymReader from a buffer.
   ///
diff --git a/llvm/lib/DebugInfo/GSYM/GsymReader.cpp b/llvm/lib/DebugInfo/GSYM/GsymReader.cpp
index 92efd66566445..835b275e28a7e 100644
--- a/llvm/lib/DebugInfo/GSYM/GsymReader.cpp
+++ b/llvm/lib/DebugInfo/GSYM/GsymReader.cpp
@@ -64,7 +64,7 @@ checkMagicAndDetectVersionEndian(StringRef Bytes) {
 }
 
 llvm::Expected<std::unique_ptr<GsymReader>>
-GsymReader::openFile(StringRef Filename) {
+GsymReader::openFile(StringRef Filename, bool RandomAccess) {
   // Open the input file and return an appropriate error if needed.
   ErrorOr<std::unique_ptr<MemoryBuffer>> BuffOrErr =
       MemoryBuffer::getFileOrSTDIN(Filename);
@@ -72,7 +72,8 @@ GsymReader::openFile(StringRef Filename) {
   if (Err)
     return llvm::errorCodeToError(Err);
   auto &Buf = BuffOrErr.get();
-  Buf->randomAccessIfMmap();
+  if (RandomAccess)
+    Buf->randomAccessIfMmap();
   return create(Buf);
 }
 

>From 94a24da9db4e73176606066eeb3bcc558e0e831d Mon Sep 17 00:00:00 2001
From: Roy Shi <royshi at meta.com>
Date: Fri, 22 May 2026 09:13:50 -0700
Subject: [PATCH 5/6] Fix format

---
 llvm/lib/Support/Unix/Path.inc             | 2 +-
 llvm/tools/llvm-gsymutil/llvm-gsymutil.cpp | 6 ++----
 2 files changed, 3 insertions(+), 5 deletions(-)

diff --git a/llvm/lib/Support/Unix/Path.inc b/llvm/lib/Support/Unix/Path.inc
index 98c210a5d31f7..3c8d4c7c28013 100644
--- a/llvm/lib/Support/Unix/Path.inc
+++ b/llvm/lib/Support/Unix/Path.inc
@@ -949,7 +949,7 @@ void mapped_file_region::randomAccessImpl() {
   if (!Mapping)
     return;
 #if defined(__MVS__) || defined(_AIX)
-  // If we don't have madvise, or it isn't beneficial, treat this as a no-op.
+    // If we don't have madvise, or it isn't beneficial, treat this as a no-op.
 #elif defined(POSIX_MADV_RANDOM)
   ::posix_madvise(Mapping, Size, POSIX_MADV_RANDOM);
 #else
diff --git a/llvm/tools/llvm-gsymutil/llvm-gsymutil.cpp b/llvm/tools/llvm-gsymutil/llvm-gsymutil.cpp
index 6dd5ea8569e72..7bbb8cf198f75 100644
--- a/llvm/tools/llvm-gsymutil/llvm-gsymutil.cpp
+++ b/llvm/tools/llvm-gsymutil/llvm-gsymutil.cpp
@@ -167,8 +167,7 @@ static void parseArgs(int argc, char **argv) {
   BenchmarkStride = 1;
   if (Args.hasArg(OPT_benchmark_reader_all)) {
     BenchmarkReader = true;
-  } else if (const llvm::opt::Arg *A =
-                 Args.getLastArg(OPT_benchmark_reader)) {
+  } else if (const llvm::opt::Arg *A = Args.getLastArg(OPT_benchmark_reader)) {
     BenchmarkReader = true;
     StringRef S{A->getValue()};
     if (!S.empty()) {
@@ -799,8 +798,7 @@ int llvm_gsymutil_main(int argc, char **argv, const llvm::ToolContext &) {
 
   if (BenchmarkReader) {
     for (const auto &GSYMPath : InputFilenames)
-      if (auto Err =
-              benchmarkReader(GSYMPath, BenchmarkStart, BenchmarkStride))
+      if (auto Err = benchmarkReader(GSYMPath, BenchmarkStart, BenchmarkStride))
         error("Benchmark failed: ", std::move(Err));
     return EXIT_SUCCESS;
   }

>From 22bce1a011fb3dcaf45c3c60ccd7eb5ed27c8fc3 Mon Sep 17 00:00:00 2001
From: Roy Shi <royshi at meta.com>
Date: Fri, 22 May 2026 09:29:36 -0700
Subject: [PATCH 6/6] Manually fix format

---
 llvm/lib/Support/Unix/Path.inc | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Support/Unix/Path.inc b/llvm/lib/Support/Unix/Path.inc
index 3c8d4c7c28013..98c210a5d31f7 100644
--- a/llvm/lib/Support/Unix/Path.inc
+++ b/llvm/lib/Support/Unix/Path.inc
@@ -949,7 +949,7 @@ void mapped_file_region::randomAccessImpl() {
   if (!Mapping)
     return;
 #if defined(__MVS__) || defined(_AIX)
-    // If we don't have madvise, or it isn't beneficial, treat this as a no-op.
+  // If we don't have madvise, or it isn't beneficial, treat this as a no-op.
 #elif defined(POSIX_MADV_RANDOM)
   ::posix_madvise(Mapping, Size, POSIX_MADV_RANDOM);
 #else



More information about the llvm-commits mailing list