[llvm] [dsymutil] Add support for pseudo probes (PR #186877)

Henry Jiang via llvm-commits llvm-commits at lists.llvm.org
Fri Apr 3 16:39:59 PDT 2026


https://github.com/mustartt updated https://github.com/llvm/llvm-project/pull/186877

>From 4004dac22d97919bb31d05de8f6e7b5018928b6d Mon Sep 17 00:00:00 2001
From: Henry Jiang <henry_jiang2 at apple.com>
Date: Thu, 12 Mar 2026 11:28:28 -0700
Subject: [PATCH 1/2] dsymutil support for pseudo probes

---
 .../tools/dsymutil/AArch64/pseudo-probe.test  | 315 ++++++++++++++++++
 llvm/tools/dsymutil/MachOUtils.cpp            |  71 +++-
 2 files changed, 379 insertions(+), 7 deletions(-)
 create mode 100644 llvm/test/tools/dsymutil/AArch64/pseudo-probe.test

diff --git a/llvm/test/tools/dsymutil/AArch64/pseudo-probe.test b/llvm/test/tools/dsymutil/AArch64/pseudo-probe.test
new file mode 100644
index 0000000000000..3d8afbfe601c7
--- /dev/null
+++ b/llvm/test/tools/dsymutil/AArch64/pseudo-probe.test
@@ -0,0 +1,315 @@
+## Test that dsymutil correctly copies pseudoprobe sections and segments
+
+# Example program
+
+# RUN: rm -rf %t && mkdir -p %t
+# RUN: yaml2obj %s -o %t/main.o
+# RUN: dsymutil %t/main.o -o %t/main.dSYM
+# RUN: obj2yaml %t/main.dSYM/Contents/Resources/DWARF/main.o -o %t/out.yaml
+# RUN: FileCheck %s < %t/out.yaml
+
+# CHECK:        segname:         __PSEUDO_PROBE
+# CHECK:        Sections:
+# CHECK-NEXT:     - sectname:        __probe_descs
+# CHECK-NEXT:       segname:         __PSEUDO_PROBE
+# CHECK:            content:         ACBD18DB4CC2F85CFFFFFFFF0000000003666F6F37B51D194A7513E4FFFFFFFF000000000362617273FEFFA4B7F6BB68FFFFFFFF000002000362617AFAD58DE7366495DBFFFFFFFF00000100046D61696E
+# CHECK-NEXT:     - sectname:        __probes
+# CHECK-NEXT:       segname:         __PSEUDO_PROBE
+# CHECK:            content:         37B51D194A7513E402000020A9A7418A5E3E02B5018000FAD58DE7366495DB0200002001411FAE90F5722601800073FEFFA4B7F6BB6802000020DEDF76EC3AE9F50501800037B51D194A7513E402000020DEDF76EC3AE9F505018000ACBD18DB4CC2F85C020000208774C18D504B6E28018000
+
+--- !mach-o
+FileHeader:
+  magic:           0xFEEDFACF
+  cputype:         0x100000C
+  cpusubtype:      0x0
+  filetype:        0x2
+  ncmds:           17
+  sizeofcmds:      976
+  flags:           0x200085
+  reserved:        0x0
+LoadCommands:
+  - cmd:             LC_SEGMENT_64
+    cmdsize:         72
+    segname:         __PAGEZERO
+    vmaddr:          0
+    vmsize:          4294967296
+    fileoff:         0
+    filesize:        0
+    maxprot:         0
+    initprot:        0
+    nsects:          0
+    flags:           0
+  - cmd:             LC_SEGMENT_64
+    cmdsize:         232
+    segname:         __TEXT
+    vmaddr:          4294967296
+    vmsize:          16384
+    fileoff:         0
+    filesize:        16384
+    maxprot:         5
+    initprot:        5
+    nsects:          2
+    flags:           0
+    Sections:
+      - sectname:        __text
+        segname:         __TEXT
+        addr:            0x100000410
+        size:            20
+        offset:          0x410
+        align:           2
+        reloff:          0x0
+        nreloc:          0
+        flags:           0x80000400
+        reserved1:       0x0
+        reserved2:       0x0
+        reserved3:       0x0
+        content:         C0035FD6C0035FD6C0035FD600008052C0035FD6
+      - sectname:        __unwind_info
+        segname:         __TEXT
+        addr:            0x100000424
+        size:            88
+        offset:          0x424
+        align:           2
+        reloff:          0x0
+        nreloc:          0
+        flags:           0x0
+        reserved1:       0x0
+        reserved2:       0x0
+        reserved3:       0x0
+        content:         010000001C000000000000001C000000000000001C00000002000000100400004000000040000000250400000000000040000000000000000000000000000000030000000C00010010000100000000000000000200000000
+  - cmd:             LC_SEGMENT_64
+    cmdsize:         232
+    segname:         __PSEUDO_PROBE
+    vmaddr:          4294983680
+    vmsize:          16384
+    fileoff:         16384
+    filesize:        16384
+    maxprot:         3
+    initprot:        3
+    nsects:          2
+    flags:           0
+    Sections:
+      - sectname:        __probe_descs
+        segname:         __PSEUDO_PROBE
+        addr:            0x100004000
+        size:            81
+        offset:          0x4000
+        align:           0
+        reloff:          0x0
+        nreloc:          0
+        flags:           0x0
+        reserved1:       0x0
+        reserved2:       0x0
+        reserved3:       0x0
+        content:         ACBD18DB4CC2F85CFFFFFFFF0000000003666F6F37B51D194A7513E4FFFFFFFF000000000362617273FEFFA4B7F6BB68FFFFFFFF000002000362617AFAD58DE7366495DBFFFFFFFF00000100046D61696E
+      - sectname:        __probes
+        segname:         __PSEUDO_PROBE
+        addr:            0x100004051
+        size:            115
+        offset:          0x4051
+        align:           0
+        reloff:          0x0
+        nreloc:          0
+        flags:           0x0
+        reserved1:       0x0
+        reserved2:       0x0
+        reserved3:       0x0
+        content:         37B51D194A7513E402000020A9A7418A5E3E02B5018000FAD58DE7366495DB0200002001411FAE90F5722601800073FEFFA4B7F6BB6802000020DEDF76EC3AE9F50501800037B51D194A7513E402000020DEDF76EC3AE9F505018000ACBD18DB4CC2F85C020000208774C18D504B6E28018000
+  - cmd:             LC_SEGMENT_64
+    cmdsize:         72
+    segname:         __LINKEDIT
+    vmaddr:          4295000064
+    vmsize:          16384
+    fileoff:         32768
+    filesize:        680
+    maxprot:         1
+    initprot:        1
+    nsects:          0
+    flags:           0
+  - cmd:             LC_DYLD_CHAINED_FIXUPS
+    cmdsize:         16
+    dataoff:         32768
+    datasize:        56
+  - cmd:             LC_DYLD_EXPORTS_TRIE
+    cmdsize:         16
+    dataoff:         32824
+    datasize:        80
+  - cmd:             LC_SYMTAB
+    cmdsize:         24
+    symoff:          32912
+    nsyms:           5
+    stroff:          32992
+    strsize:         48
+  - cmd:             LC_DYSYMTAB
+    cmdsize:         80
+    ilocalsym:       0
+    nlocalsym:       0
+    iextdefsym:      0
+    nextdefsym:      5
+    iundefsym:       5
+    nundefsym:       0
+    tocoff:          0
+    ntoc:            0
+    modtaboff:       0
+    nmodtab:         0
+    extrefsymoff:    0
+    nextrefsyms:     0
+    indirectsymoff:  0
+    nindirectsyms:   0
+    extreloff:       0
+    nextrel:         0
+    locreloff:       0
+    nlocrel:         0
+  - cmd:             LC_LOAD_DYLINKER
+    cmdsize:         32
+    name:            12
+    Content:         '/usr/lib/dyld'
+    ZeroPadBytes:    7
+  - cmd:             LC_UUID
+    cmdsize:         24
+    uuid:            B95C17E1-7B8C-362B-B402-C636C09BB979
+  - cmd:             LC_BUILD_VERSION
+    cmdsize:         32
+    platform:        1
+    minos:           1703936
+    sdk:             1704448
+    ntools:          1
+    Tools:
+      - tool:            3
+        version:         86573312
+  - cmd:             LC_SOURCE_VERSION
+    cmdsize:         16
+    version:         0
+  - cmd:             LC_MAIN
+    cmdsize:         24
+    entryoff:        1052
+    stacksize:       0
+  - cmd:             LC_LOAD_DYLIB
+    cmdsize:         56
+    dylib:
+      name:            24
+      timestamp:       2
+      current_version: 88866816
+      compatibility_version: 65536
+    Content:         '/usr/lib/libSystem.B.dylib'
+    ZeroPadBytes:    6
+  - cmd:             LC_FUNCTION_STARTS
+    cmdsize:         16
+    dataoff:         32904
+    datasize:        8
+  - cmd:             LC_DATA_IN_CODE
+    cmdsize:         16
+    dataoff:         32912
+    datasize:        0
+  - cmd:             LC_CODE_SIGNATURE
+    cmdsize:         16
+    dataoff:         33040
+    datasize:        408
+LinkEditData:
+  ExportTrie:
+    TerminalSize:    0
+    NodeOffset:      0
+    Name:            ''
+    Flags:           0x0
+    Address:         0x0
+    Other:           0x0
+    ImportName:      ''
+    Children:
+      - TerminalSize:    0
+        NodeOffset:      41
+        Name:            _
+        Flags:           0x0
+        Address:         0x0
+        Other:           0x0
+        ImportName:      ''
+        Children:
+          - TerminalSize:    2
+            NodeOffset:      9
+            Name:            _mh_execute_header
+            Flags:           0x0
+            Address:         0x0
+            Other:           0x0
+            ImportName:      ''
+          - TerminalSize:    0
+            NodeOffset:      23
+            Name:            ba
+            Flags:           0x0
+            Address:         0x0
+            Other:           0x0
+            ImportName:      ''
+            Children:
+              - TerminalSize:    3
+                NodeOffset:      13
+                Name:            r
+                Flags:           0x0
+                Address:         0x414
+                Other:           0x0
+                ImportName:      ''
+              - TerminalSize:    3
+                NodeOffset:      18
+                Name:            z
+                Flags:           0x0
+                Address:         0x418
+                Other:           0x0
+                ImportName:      ''
+          - TerminalSize:    3
+            NodeOffset:      31
+            Name:            foo
+            Flags:           0x0
+            Address:         0x410
+            Other:           0x0
+            ImportName:      ''
+          - TerminalSize:    3
+            NodeOffset:      36
+            Name:            main
+            Flags:           0x0
+            Address:         0x41C
+            Other:           0x0
+            ImportName:      ''
+  NameList:
+    - n_strx:          2
+      n_type:          0xF
+      n_sect:          1
+      n_desc:          16
+      n_value:         4294967296
+    - n_strx:          22
+      n_type:          0xF
+      n_sect:          1
+      n_desc:          0
+      n_value:         4294968340
+    - n_strx:          27
+      n_type:          0xF
+      n_sect:          1
+      n_desc:          0
+      n_value:         4294968344
+    - n_strx:          32
+      n_type:          0xF
+      n_sect:          1
+      n_desc:          0
+      n_value:         4294968336
+    - n_strx:          37
+      n_type:          0xF
+      n_sect:          1
+      n_desc:          0
+      n_value:         4294968348
+  StringTable:
+    - ' '
+    - __mh_execute_header
+    - _bar
+    - _baz
+    - _foo
+    - _main
+    - ''
+    - ''
+    - ''
+    - ''
+    - ''
+  FunctionStarts:  [ 0x410, 0x414, 0x418, 0x41C ]
+  ChainedFixups:   [ 0x0, 0x0, 0x0, 0x0, 0x20, 0x0, 0x0, 0x0, 0x34, 0x0, 
+                     0x0, 0x0, 0x34, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 
+                     0x1, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 
+                     0x0, 0x0, 0x4, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 
+                     0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 
+                     0x0, 0x0, 0x0, 0x0, 0x0, 0x0 ]
+...
+
diff --git a/llvm/tools/dsymutil/MachOUtils.cpp b/llvm/tools/dsymutil/MachOUtils.cpp
index 9b143157ac773..d8b745cf73dbe 100644
--- a/llvm/tools/dsymutil/MachOUtils.cpp
+++ b/llvm/tools/dsymutil/MachOUtils.cpp
@@ -269,13 +269,18 @@ getSection(const object::MachOObjectFile &Obj,
 //
 // When the eh_frame section is transferred, its offset and size are set resp.
 // to \a EHFrameOffset and \a EHFrameSize.
+//
+// When the __PSEUDO_PROBE segment is transferred, its sections' offsets are
+// updated using \a PseudoProbeOffset and \a PseudoProbeSize for __probes, and
+// \a PseudoProbeDescsOffset and \a PseudoProbeDescsSize for __probe_descs
 template <typename SegmentTy>
 static void transferSegmentAndSections(
     const object::MachOObjectFile::LoadCommandInfo &LCI, SegmentTy Segment,
     const object::MachOObjectFile &Obj, MachObjectWriter &Writer,
     uint64_t LinkeditOffset, uint64_t LinkeditSize, uint64_t EHFrameOffset,
-    uint64_t EHFrameSize, uint64_t DwarfSegmentSize, uint64_t &GapForDwarf,
-    uint64_t &EndAddress) {
+    uint64_t EHFrameSize, uint64_t PseudoProbeOffset, uint64_t PseudoProbeSize,
+    uint64_t PseudoProbeProbesOffset, uint64_t PseudoProbeDescsOffset,
+    uint64_t DwarfSegmentSize, uint64_t &GapForDwarf, uint64_t &EndAddress) {
   if (StringRef("__DWARF") == Segment.segname)
     return;
 
@@ -287,6 +292,10 @@ static void transferSegmentAndSections(
     Segment.filesize = LinkeditSize;
     // Resize vmsize by rounding to the page size.
     Segment.vmsize = alignTo(LinkeditSize, 0x1000);
+  } else if (StringRef("__PSEUDO_PROBE") == Segment.segname &&
+             PseudoProbeSize > 0) {
+    Segment.fileoff = PseudoProbeOffset;
+    Segment.filesize = PseudoProbeSize;
   } else {
     Segment.fileoff = Segment.filesize = 0;
   }
@@ -312,6 +321,14 @@ static void transferSegmentAndSections(
     if (StringRef("__eh_frame") == Sect.sectname) {
       Sect.offset = EHFrameOffset;
       Sect.reloff = Sect.nreloc = 0;
+    } else if (StringRef("__probes") == Sect.sectname &&
+               PseudoProbeProbesOffset > 0) {
+      Sect.offset = PseudoProbeProbesOffset;
+      Sect.reloff = Sect.nreloc = 0;
+    } else if (StringRef("__probe_descs") == Sect.sectname &&
+               PseudoProbeDescsOffset > 0) {
+      Sect.offset = PseudoProbeDescsOffset;
+      Sect.reloff = Sect.nreloc = 0;
     } else {
       Sect.offset = Sect.reloff = Sect.nreloc = 0;
     }
@@ -463,6 +480,10 @@ bool generateDsymCompanion(
   // If we have a valid eh_frame to copy, do it.
   uint64_t EHFrameSize = 0;
   StringRef EHFrameData;
+  StringRef PseudoProbeProbesData;
+  uint64_t PseudoProbeProbesSize = 0;
+  StringRef PseudoProbeDescsData;
+  uint64_t PseudoProbeDescsSize = 0;
   for (const object::SectionRef &Section : InputBinary.sections()) {
     Expected<StringRef> NameOrErr = Section.getName();
     if (!NameOrErr) {
@@ -478,8 +499,23 @@ bool generateDsymCompanion(
       } else {
         consumeError(ContentsOrErr.takeError());
       }
+    } else if (SectionName == "probes") {
+      if (Expected<StringRef> ContentsOrErr = Section.getContents()) {
+        PseudoProbeProbesData = *ContentsOrErr;
+        PseudoProbeProbesSize = Section.getSize();
+      } else {
+        consumeError(ContentsOrErr.takeError());
+      }
+    } else if (SectionName == "probe_descs") {
+      if (Expected<StringRef> ContentsOrErr = Section.getContents()) {
+        PseudoProbeDescsData = *ContentsOrErr;
+        PseudoProbeDescsSize = Section.getSize();
+      } else {
+        consumeError(ContentsOrErr.takeError());
+      }
     }
   }
+  uint64_t PseudoProbeSize = PseudoProbeProbesSize + PseudoProbeDescsSize;
 
   unsigned HeaderSize =
       Is64Bit ? sizeof(MachO::mach_header_64) : sizeof(MachO::mach_header);
@@ -561,8 +597,16 @@ bool generateDsymCompanion(
   uint64_t EHFrameStart = StringStart + NewStringsSize;
   EHFrameStart = alignTo(EHFrameStart, 0x1000);
 
-  uint64_t DwarfSegmentStart = EHFrameStart + EHFrameSize;
-  DwarfSegmentStart = alignTo(DwarfSegmentStart, 0x1000);
+  // Place pseudo probe data after the EH frame
+  uint64_t PseudoProbeStart = PseudoProbeSize > 0
+                                  ? alignTo(EHFrameStart + EHFrameSize, 0x1000)
+                                  : EHFrameStart + EHFrameSize;
+
+  uint64_t PseudoProbeProbesStart = PseudoProbeStart;
+  uint64_t PseudoProbeDescsStart = PseudoProbeStart + PseudoProbeProbesSize;
+
+  uint64_t DwarfSegmentStart =
+      alignTo(PseudoProbeStart + PseudoProbeSize, 0x1000);
 
   // Write the load commands for the segments and sections we 'import' from
   // the original binary.
@@ -573,12 +617,16 @@ bool generateDsymCompanion(
       transferSegmentAndSections(
           LCI, InputBinary.getSegmentLoadCommand(LCI), InputBinary, Writer,
           SymtabStart, StringStart + NewStringsSize - SymtabStart, EHFrameStart,
-          EHFrameSize, DwarfSegmentSize, GapForDwarf, EndAddress);
+          EHFrameSize, PseudoProbeStart, PseudoProbeSize,
+          PseudoProbeProbesStart, PseudoProbeDescsStart, DwarfSegmentSize,
+          GapForDwarf, EndAddress);
     else if (LCI.C.cmd == MachO::LC_SEGMENT_64)
       transferSegmentAndSections(
           LCI, InputBinary.getSegment64LoadCommand(LCI), InputBinary, Writer,
           SymtabStart, StringStart + NewStringsSize - SymtabStart, EHFrameStart,
-          EHFrameSize, DwarfSegmentSize, GapForDwarf, EndAddress);
+          EHFrameSize, PseudoProbeStart, PseudoProbeSize,
+          PseudoProbeProbesStart, PseudoProbeDescsStart, DwarfSegmentSize,
+          GapForDwarf, EndAddress);
   }
 
   uint64_t DwarfVMAddr = alignTo(EndAddress, 0x1000);
@@ -632,8 +680,17 @@ bool generateDsymCompanion(
     OutFile << EHFrameData;
   assert(OutFile.tell() == EHFrameStart + EHFrameSize);
 
+  // Transfer pseudo probe.
+  if (PseudoProbeSize > 0) {
+    OutFile.write_zeros(PseudoProbeStart - (EHFrameStart + EHFrameSize));
+    assert(OutFile.tell() == PseudoProbeStart);
+    OutFile << PseudoProbeProbesData;
+    OutFile << PseudoProbeDescsData;
+    assert(OutFile.tell() == PseudoProbeStart + PseudoProbeSize);
+  }
+
   // Pad till the Dwarf segment start.
-  OutFile.write_zeros(DwarfSegmentStart - (EHFrameStart + EHFrameSize));
+  OutFile.write_zeros(DwarfSegmentStart - OutFile.tell());
   assert(OutFile.tell() == DwarfSegmentStart);
 
   // Emit the Dwarf sections contents.

>From ad3df6d6c3668874d2c4691664038cffe9a766d5 Mon Sep 17 00:00:00 2001
From: Henry Jiang <henry_jiang2 at apple.com>
Date: Fri, 3 Apr 2026 16:39:40 -0700
Subject: [PATCH 2/2] updated comments

---
 llvm/tools/dsymutil/MachOUtils.cpp | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/llvm/tools/dsymutil/MachOUtils.cpp b/llvm/tools/dsymutil/MachOUtils.cpp
index d8b745cf73dbe..a6dd3afa223e7 100644
--- a/llvm/tools/dsymutil/MachOUtils.cpp
+++ b/llvm/tools/dsymutil/MachOUtils.cpp
@@ -270,9 +270,10 @@ getSection(const object::MachOObjectFile &Obj,
 // When the eh_frame section is transferred, its offset and size are set resp.
 // to \a EHFrameOffset and \a EHFrameSize.
 //
-// When the __PSEUDO_PROBE segment is transferred, its sections' offsets are
-// updated using \a PseudoProbeOffset and \a PseudoProbeSize for __probes, and
-// \a PseudoProbeDescsOffset and \a PseudoProbeDescsSize for __probe_descs
+// When the __PSEUDO_PROBE segment is transferred, its offset and size are set
+// resp. to \a PseudoProbeOffset and \a PseudoProbeSize, and its sections'
+// offsets are updated using \a PseudoProbeProbesOffset for __probes and
+// \a PseudoProbeDescsOffset for __probe_descs.
 template <typename SegmentTy>
 static void transferSegmentAndSections(
     const object::MachOObjectFile::LoadCommandInfo &LCI, SegmentTy Segment,
@@ -597,7 +598,7 @@ bool generateDsymCompanion(
   uint64_t EHFrameStart = StringStart + NewStringsSize;
   EHFrameStart = alignTo(EHFrameStart, 0x1000);
 
-  // Place pseudo probe data after the EH frame
+  // Place pseudo probe data after the EH frame.
   uint64_t PseudoProbeStart = PseudoProbeSize > 0
                                   ? alignTo(EHFrameStart + EHFrameSize, 0x1000)
                                   : EHFrameStart + EHFrameSize;



More information about the llvm-commits mailing list