[llvm] 806dbe9 - [Profile] Add a more descriptive message to the bad_header error (#211281)

via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 13 13:46:31 PDT 2026


Author: Wael Yehia
Date: 2026-08-13T20:46:25Z
New Revision: 806dbe95e7f05b12afd1c7cc579c42e101921199

URL: https://github.com/llvm/llvm-project/commit/806dbe95e7f05b12afd1c7cc579c42e101921199
DIFF: https://github.com/llvm/llvm-project/commit/806dbe95e7f05b12afd1c7cc579c42e101921199.diff

LOG: [Profile] Add a more descriptive message to the bad_header error (#211281)

At the moment, the raw profile reader diagnoses different problem with
the same error message "invalid instrumentation profile data (file
header is corrupt)". On AIX, we had users report such problems thinking
it's a compiler bug, while the real problem was truncated profiles.
The particular case they hit was the condition
[here](https://github.com/llvm/llvm-project/blob/668803fa73bdf2f8d095162a1f81c000d18458c9/llvm/lib/ProfileData/InstrProfReader.cpp#L652):
```
Error RawInstrProfReader<IntPtrT>::readHeader(...) {
  ...
  if (Start + ValueDataOffset > DataBuffer->getBufferEnd())
    return error(instrprof_error::bad_header);
```
This could indicate a corrupted header (large garbage values for some
sections) or a truncated file.
I figured if we print the values and sizes we read in the header, it
will give the user more info.

This PR 
1. adds an extra description for a couple of `bad_header` error points,
and
2. updates `tools/llvm-profdata/insufficient-binary-ids-size.test`: this
test was originally meant to test a truncated BinaryId section, but the
test hasn't been updated to reflect new profile header changes and now
is failing due to a truncated header. But both errors are reported as
`bad_header`, which is probably why it was uncaught by authors that
updated the profile header.

---------

Co-authored-by: Wael Yehia <wyehia at ca.ibm.com>

Added: 
    llvm/test/tools/llvm-profdata/truncated-profile.test

Modified: 
    llvm/include/llvm/ProfileData/InstrProf.h
    llvm/lib/ProfileData/InstrProf.cpp
    llvm/lib/ProfileData/InstrProfReader.cpp
    llvm/test/tools/llvm-profdata/insufficient-binary-ids-size.test
    llvm/test/tools/llvm-profdata/misaligned-binary-ids-size.test
    llvm/test/tools/llvm-profdata/raw-magic-but-no-header.test

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/ProfileData/InstrProf.h b/llvm/include/llvm/ProfileData/InstrProf.h
index cd07a012297ef..bff17fcf64781 100644
--- a/llvm/include/llvm/ProfileData/InstrProf.h
+++ b/llvm/include/llvm/ProfileData/InstrProf.h
@@ -413,6 +413,7 @@ enum class instrprof_error {
   unrecognized_format,
   bad_magic,
   bad_header,
+  header_size_mismatch,
   unsupported_version,
   unsupported_hash_type,
   too_large,

diff  --git a/llvm/lib/ProfileData/InstrProf.cpp b/llvm/lib/ProfileData/InstrProf.cpp
index 6703d04c9493f..b446b70464f11 100644
--- a/llvm/lib/ProfileData/InstrProf.cpp
+++ b/llvm/lib/ProfileData/InstrProf.cpp
@@ -101,6 +101,10 @@ static std::string getInstrProfErrString(instrprof_error Err,
   case instrprof_error::bad_header:
     OS << "invalid instrumentation profile data (file header is corrupt)";
     break;
+  case instrprof_error::header_size_mismatch:
+    OS << "invalid instrumentation profile data (file is incomplete or header "
+          "is corrupt)";
+    break;
   case instrprof_error::unsupported_version:
     OS << "unsupported instrumentation profile format version";
     break;

diff  --git a/llvm/lib/ProfileData/InstrProfReader.cpp b/llvm/lib/ProfileData/InstrProfReader.cpp
index 05770f83ed160..8cb107296ba6a 100644
--- a/llvm/lib/ProfileData/InstrProfReader.cpp
+++ b/llvm/lib/ProfileData/InstrProfReader.cpp
@@ -513,7 +513,8 @@ Error RawInstrProfReader<IntPtrT>::readHeader() {
   if (!hasFormat(*DataBuffer))
     return error(instrprof_error::bad_magic);
   if (DataBuffer->getBufferSize() < sizeof(RawInstrProf::Header))
-    return error(instrprof_error::bad_header);
+    return error(instrprof_error::bad_header,
+                 std::string("profile file header is truncated"));
   auto *Header = reinterpret_cast<const RawInstrProf::Header *>(
       DataBuffer->getBufferStart());
   ShouldSwapBytes = Header->Magic != RawInstrProf::getMagic<IntPtrT>();
@@ -589,15 +590,23 @@ Error RawInstrProfReader<IntPtrT>::readHeader(
                   "\nPLEASE update this tool to version in the raw profile, or "
                   "regenerate raw profile with expected version.")
                      .str());
-
   uint64_t BinaryIdSize = swap(Header.BinaryIdsSize);
   // Binary id start just after the header if exists.
   const uint8_t *BinaryIdStart =
       reinterpret_cast<const uint8_t *>(&Header) + sizeof(RawInstrProf::Header);
   const uint8_t *BinaryIdEnd = BinaryIdStart + BinaryIdSize;
   const uint8_t *BufferEnd = (const uint8_t *)DataBuffer->getBufferEnd();
-  if (BinaryIdSize % sizeof(uint64_t) || BinaryIdEnd > BufferEnd)
-    return error(instrprof_error::bad_header);
+  if (BinaryIdSize % sizeof(uint64_t))
+    return error(
+        instrprof_error::bad_header,
+        ("BinaryIdSize (" + Twine(BinaryIdSize) + ") is not a multiple of 8")
+            .str());
+  if (BinaryIdEnd > BufferEnd)
+    return error(instrprof_error::header_size_mismatch,
+                 ("Header.BinaryIdSize = " + Twine(BinaryIdSize) + " bytes; " +
+                  Twine(BufferEnd - BinaryIdStart) + " bytes available")
+                     .str());
+
   ArrayRef<uint8_t> BinaryIdsBuffer(BinaryIdStart, BinaryIdSize);
   if (!BinaryIdsBuffer.empty()) {
     if (Error Err = readBinaryIdsInternal(*DataBuffer, BinaryIdsBuffer,
@@ -650,7 +659,28 @@ Error RawInstrProfReader<IntPtrT>::readHeader(
 
   auto *Start = reinterpret_cast<const char *>(&Header);
   if (Start + ValueDataOffset > DataBuffer->getBufferEnd())
-    return error(instrprof_error::bad_header);
+    // clang-format off
+    return error(
+        instrprof_error::header_size_mismatch,
+        ("profile file size (" + Twine(DataBuffer->getBufferSize()) +
+         " bytes) smaller than expected (at least " + Twine(ValueDataOffset) +
+         " bytes: " +
+         Twine(sizeof(RawInstrProf::Header)) + "(Header) + " +
+         Twine(BinaryIdSize) + "(BinaryIdSize) + " +
+         Twine(DataSize) + "(DataSize) + " +
+         Twine(CountersSize) + "(CountersSize) + " +
+         Twine(NumBitmapBytes) + "(NumBitmapBytes) + " +
+         Twine(UniformCountersSectionSize) + "(UniformCountersSectionSize) + " +
+         Twine(NamesSize) + "(NamesSize) + " +
+         Twine(VTableSectionSize) + "(VTableSectionSize) + " +
+         Twine(VTableNameSize) + "(VTableNameSize) + " +
+         Twine(PaddingBytesBeforeCounters + PaddingBytesAfterCounters +
+               PaddingBytesAfterBitmapBytes + PaddingBytesAfterUniformCounters +
+               PaddingBytesAfterNames + PaddingBytesAfterVTableProfData +
+               PaddingBytesAfterVTableNames) +
+         "(Padding))")
+            .str());
+  // clang-format on
 
   if (BIDFetcher) {
     std::vector<object::BuildID> BinaryIDs;
@@ -1373,7 +1403,10 @@ Error IndexedInstrProfReader::readHeader() {
     uint64_t BinaryIdsSize =
         support::endian::readNext<uint64_t, llvm::endianness::little>(Ptr);
     if (BinaryIdsSize % sizeof(uint64_t))
-      return error(instrprof_error::bad_header);
+      return error(
+          instrprof_error::bad_header,
+          ("BinaryIdSize (" + Twine(BinaryIdsSize) + ") is not a multiple of 8")
+              .str());
     // Set the binary ids start.
     BinaryIdsBuffer = ArrayRef<uint8_t>(Ptr, BinaryIdsSize);
     if (Ptr > (const unsigned char *)DataBuffer->getBufferEnd())

diff  --git a/llvm/test/tools/llvm-profdata/insufficient-binary-ids-size.test b/llvm/test/tools/llvm-profdata/insufficient-binary-ids-size.test
index 66fba4ba495b7..ecfc4551d0b1a 100644
--- a/llvm/test/tools/llvm-profdata/insufficient-binary-ids-size.test
+++ b/llvm/test/tools/llvm-profdata/insufficient-binary-ids-size.test
@@ -3,12 +3,13 @@
 // TODO: use a builtin version of printf
 UNSUPPORTED: system-zos
 RUN: printf '\201rforpl\377' > %t.profraw
-RUN: printf '\10\0\0\0\0\0\0\0' >> %t.profraw
+RUN: printf '\x0B\0\0\0\0\0\0\0' >> %t.profraw
 // We should fail on this because the data buffer (profraw file) is not long
 // enough to hold this binary IDs size. NOTE that this (combined with the 8-byte
 // alignment requirement for binary IDs size) will ensure we can at least read one
 // 8-byte size if the binary IDs are provided.
-RUN: printf '\8\0\0\0\0\0\0\0' >> %t.profraw
+// BinaryIdSize - set to 8 bytes
+RUN: printf '\x08\0\0\0\0\0\0\0' >> %t.profraw
 RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw
 RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw
 RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw
@@ -17,8 +18,17 @@ RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw
 RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw
 RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw
 RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw
-
+RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw
+RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw
+RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw
+RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw
+RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw
+RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw
+RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw
+RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw
+// ^^ end of Profile Header ^^
+// Incomplete binary IDs data - only 7 bytes instead of 8
 RUN: printf '\0\0\0\0\0\0\0' >> %t.profraw
 
-// RUN: not llvm-profdata show --binary-ids  %t.profraw 2>&1 | FileCheck %s
-// CHECK: invalid instrumentation profile data (file header is corrupt)
+RUN: not llvm-profdata show --binary-ids  %t.profraw 2>&1 | FileCheck %s
+CHECK: invalid instrumentation profile data (file is incomplete or header is corrupt): Header.BinaryIdSize = 8 bytes; 7 bytes available

diff  --git a/llvm/test/tools/llvm-profdata/misaligned-binary-ids-size.test b/llvm/test/tools/llvm-profdata/misaligned-binary-ids-size.test
index 46be16d8b728b..fb864f9810d48 100644
--- a/llvm/test/tools/llvm-profdata/misaligned-binary-ids-size.test
+++ b/llvm/test/tools/llvm-profdata/misaligned-binary-ids-size.test
@@ -29,5 +29,5 @@ RUN: printf '\1\1\1\1\1\1\1\1' >> %t.profraw
 RUN: printf '\2\2\2\2\2\2\2\2' >> %t.profraw
 RUN: printf '\3\3\3\3\0\0\0\0' >> %t.profraw
 
-// RUN: not llvm-profdata show --binary-ids  %t.profraw 2>&1 | FileCheck %s
-// CHECK: invalid instrumentation profile data (file header is corrupt)
+RUN: not llvm-profdata show --binary-ids  %t.profraw 2>&1 | FileCheck %s
+CHECK: invalid instrumentation profile data (file header is corrupt): BinaryIdSize (63) is not a multiple of 8

diff  --git a/llvm/test/tools/llvm-profdata/raw-magic-but-no-header.test b/llvm/test/tools/llvm-profdata/raw-magic-but-no-header.test
index caec1663a04fd..e8c3b4582f63c 100644
--- a/llvm/test/tools/llvm-profdata/raw-magic-but-no-header.test
+++ b/llvm/test/tools/llvm-profdata/raw-magic-but-no-header.test
@@ -7,4 +7,4 @@ RUN: not llvm-profdata show %t 2>&1 | FileCheck %s
 RUN: printf '\377lprofr\201' > %t
 RUN: not llvm-profdata show %t 2>&1 | FileCheck %s
 
-CHECK: error: {{.+}}: invalid instrumentation profile data (file header is corrupt)
+CHECK: error: {{.+}}: invalid instrumentation profile data (file header is corrupt): profile file header is truncated

diff  --git a/llvm/test/tools/llvm-profdata/truncated-profile.test b/llvm/test/tools/llvm-profdata/truncated-profile.test
new file mode 100644
index 0000000000000..adc1ebfdff148
--- /dev/null
+++ b/llvm/test/tools/llvm-profdata/truncated-profile.test
@@ -0,0 +1,44 @@
+// Magic
+RUN: printf '\x81rforpl\xff' > %t.profraw
+// Version (11 = 0x0B)
+RUN: printf '\13\0\0\0\0\0\0\0' >> %t.profraw
+// BinaryIdsSize
+RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw
+// NumData
+RUN: printf '\1\0\0\0\0\0\0\0' >> %t.profraw
+// PaddingBytesBeforeCounters
+RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw
+// NumCounters
+RUN: printf '\1\0\0\0\0\0\0\0' >> %t.profraw
+// PaddingBytesAfterCounters
+RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw
+// NumBitmapBytes
+RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw
+// PaddingBytesAfterBitmapBytes
+RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw
+// NumUniformCounters
+RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw
+// PaddingBytesAfterUniformCounters
+RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw
+// UniformCountersDelta
+RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw
+// NamesSize
+RUN: printf '\x10\0\0\0\0\0\0\0' >> %t.profraw
+// CountersDelta
+RUN: printf '\xF8\xFF\xFF\xFF\0\0\0\0' >> %t.profraw
+// BitmapDelta
+RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw
+// NamesDelta
+RUN: printf '\x58\x87\x00\x10\0\0\0\0' >> %t.profraw
+// NumVTables
+RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw
+// VNamesSize
+RUN: printf '\0\0\0\0\0\0\0\0' >> %t.profraw
+// ValueKindLast
+RUN: printf '\3\0\0\0\0\0\0\0' >> %t.profraw
+// No Binary Ids
+// Array of __llvm_prof_data goes next but we'll truncate it to 4 bytes 
+RUN: printf '\0\0\0\0' >> %t.profraw
+
+RUN: not llvm-profdata show  %t.profraw 2>&1 | FileCheck %s
+CHECK: invalid instrumentation profile data (file is incomplete or header is corrupt): profile file size (156 bytes) smaller than expected (at least 248 bytes: 152(Header) + 0(BinaryIdSize) + 72(DataSize) + 8(CountersSize) + 0(NumBitmapBytes) + 0(UniformCountersSectionSize) + 16(NamesSize) + 0(VTableSectionSize) + 0(VTableNameSize) + 0(Padding))


        


More information about the llvm-commits mailing list