Skip to content
This repository was archived by the owner on Sep 15, 2025. It is now read-only.

Commit 558de0e

Browse files
authored
[llvm-gsymutil] Add option to load callsites from DWARF (llvm#119913)
This change adds support for loading gSYM callsite information from DWARF. Previously the only support was for loading callsites info from YAML. For testing, we add a pass where `macho-gsym-merged-callsites-dsym` loads callsite info from DWARF rather than YAML.
1 parent 8bb1bdf commit 558de0e

File tree

5 files changed

+82
-3
lines changed

5 files changed

+82
-3
lines changed

llvm/include/llvm/DebugInfo/GSYM/DwarfTransformer.h

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,14 +33,17 @@ class OutputAggregator;
3333
/// allows this class to be unit tested.
3434
class DwarfTransformer {
3535
public:
36-
3736
/// Create a DWARF transformer.
3837
///
3938
/// \param D The DWARF to use when converting to GSYM.
4039
///
4140
/// \param G The GSYM creator to populate with the function information
4241
/// from the debug info.
43-
DwarfTransformer(DWARFContext &D, GsymCreator &G) : DICtx(D), Gsym(G) {}
42+
///
43+
/// \param LDCS Flag to indicate whether we should load the call site
44+
/// information from DWARF `DW_TAG_call_site` entries
45+
DwarfTransformer(DWARFContext &D, GsymCreator &G, bool LDCS = false)
46+
: DICtx(D), Gsym(G), LoadDwarfCallSites(LDCS) {}
4447

4548
/// Extract the DWARF from the supplied object file and convert it into the
4649
/// Gsym format in the GsymCreator object that is passed in. Returns an
@@ -83,8 +86,16 @@ class DwarfTransformer {
8386
/// \param Die The DWARF debug info entry to parse.
8487
void handleDie(OutputAggregator &Strm, CUInfo &CUI, DWARFDie Die);
8588

89+
/// Parse call site information from DWARF
90+
///
91+
/// \param CUI The compile unit info for the current CU.
92+
/// \param Die The DWARFDie for the function.
93+
/// \param FI The FunctionInfo for the function being populated.
94+
void parseCallSiteInfoFromDwarf(CUInfo &CUI, DWARFDie Die, FunctionInfo &FI);
95+
8696
DWARFContext &DICtx;
8797
GsymCreator &Gsym;
98+
bool LoadDwarfCallSites;
8899

89100
friend class DwarfTransformerTest;
90101
};

llvm/lib/DebugInfo/GSYM/DwarfTransformer.cpp

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -543,6 +543,11 @@ void DwarfTransformer::handleDie(OutputAggregator &Out, CUInfo &CUI,
543543
FI.Inline = std::nullopt;
544544
}
545545
}
546+
547+
// If dwarf-callsites flag is set, parse DW_TAG_call_site DIEs.
548+
if (LoadDwarfCallSites)
549+
parseCallSiteInfoFromDwarf(CUI, Die, FI);
550+
546551
Gsym.addFunctionInfo(std::move(FI));
547552
}
548553
} break;
@@ -553,6 +558,63 @@ void DwarfTransformer::handleDie(OutputAggregator &Out, CUInfo &CUI,
553558
handleDie(Out, CUI, ChildDie);
554559
}
555560

561+
void DwarfTransformer::parseCallSiteInfoFromDwarf(CUInfo &CUI, DWARFDie Die,
562+
FunctionInfo &FI) {
563+
// Parse all DW_TAG_call_site DIEs that are children of this subprogram DIE.
564+
// DWARF specification:
565+
// - DW_TAG_call_site can have DW_AT_call_return_pc for return address offset.
566+
// - DW_AT_call_origin might point to a DIE of the function being called.
567+
// For simplicity, we will just extract return_offset and possibly target name
568+
// if available.
569+
570+
CallSiteInfoCollection CSIC;
571+
572+
for (DWARFDie Child : Die.children()) {
573+
if (Child.getTag() != dwarf::DW_TAG_call_site)
574+
continue;
575+
576+
CallSiteInfo CSI;
577+
// DW_AT_call_return_pc: the return PC (address). We'll convert it to
578+
// offset relative to FI's start.
579+
auto ReturnPC =
580+
dwarf::toAddress(Child.findRecursively(dwarf::DW_AT_call_return_pc));
581+
if (!ReturnPC || !FI.Range.contains(*ReturnPC))
582+
continue;
583+
584+
CSI.ReturnOffset = *ReturnPC - FI.startAddress();
585+
586+
// Attempt to get function name from DW_AT_call_origin. If present, we can
587+
// insert it as a match regex.
588+
if (DWARFDie OriginDie =
589+
Child.getAttributeValueAsReferencedDie(dwarf::DW_AT_call_origin)) {
590+
591+
// Include the full unmangled name if available, otherwise the short name.
592+
if (const char *LinkName = OriginDie.getLinkageName()) {
593+
uint32_t LinkNameOff = Gsym.insertString(LinkName, /*Copy=*/false);
594+
CSI.MatchRegex.push_back(LinkNameOff);
595+
} else if (const char *ShortName = OriginDie.getShortName()) {
596+
uint32_t ShortNameOff = Gsym.insertString(ShortName, /*Copy=*/false);
597+
CSI.MatchRegex.push_back(ShortNameOff);
598+
}
599+
}
600+
601+
// For now, we won't attempt to deduce InternalCall/ExternalCall flags
602+
// from DWARF.
603+
CSI.Flags = CallSiteInfo::Flags::None;
604+
605+
CSIC.CallSites.push_back(CSI);
606+
}
607+
608+
if (!CSIC.CallSites.empty()) {
609+
if (!FI.CallSites)
610+
FI.CallSites = CallSiteInfoCollection();
611+
// Append parsed DWARF callsites:
612+
FI.CallSites->CallSites.insert(FI.CallSites->CallSites.end(),
613+
CSIC.CallSites.begin(),
614+
CSIC.CallSites.end());
615+
}
616+
}
617+
556618
Error DwarfTransformer::convert(uint32_t NumThreads, OutputAggregator &Out) {
557619
size_t NumBefore = Gsym.getNumFunctionInfos();
558620
auto getDie = [&](DWARFUnit &DwarfUnit) -> DWARFDie {

llvm/test/tools/llvm-gsymutil/ARM_AArch64/macho-gsym-merged-callsites-dsym.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,11 @@
88
# RUN: yaml2obj %t/merged_callsites.dSYM.yaml -o %t/merged_callsites.dSYM
99

1010
# RUN: llvm-gsymutil --convert=%t/merged_callsites.dSYM --merged-functions --callsites-yaml-file=%t/callsites.yaml -o %t/call_sites_dSYM.gsym
11+
# RUN: llvm-gsymutil --convert=%t/merged_callsites.dSYM --merged-functions --dwarf-callsites -o %t/dwarf_call_sites_dSYM.gsym
1112

1213
# Dump the GSYM file and check the output for callsite information
1314
# RUN: llvm-gsymutil %t/call_sites_dSYM.gsym | FileCheck --check-prefix=CHECK-MERGED-CALLSITES %s
15+
# RUN: llvm-gsymutil %t/dwarf_call_sites_dSYM.gsym | FileCheck --check-prefix=CHECK-MERGED-CALLSITES %s
1416

1517
# CHECK-MERGED-CALLSITES: FunctionInfo @ 0x[[#%x,FUNC4_1:]]: [0x[[#%x,FUNC4_1_START:]] - 0x[[#%x,FUNC4_1_END:]]) "function4_copy1"
1618
# CHECK-MERGED-CALLSITES: ++ Merged FunctionInfos[0]:

llvm/tools/llvm-gsymutil/Opts.td

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ defm convert :
1818
"Convert the specified file to the GSYM format.\nSupported files include ELF and mach-o files that will have their debug info (DWARF) and symbol table converted">;
1919
def merged_functions :
2020
FF<"merged-functions", "Encode merged function information for functions in debug info that have matching address ranges.\nWithout this option one function per unique address range will be emitted.">;
21+
def dwarf_callsites : FF<"dwarf-callsites", "Load call site info from DWARF, if available">;
2122
defm callsites_yaml_file :
2223
Eq<"callsites-yaml-file", "Load call site info from YAML file. Useful for testing.">, Flags<[HelpHidden]>;
2324
defm arch :

llvm/tools/llvm-gsymutil/llvm-gsymutil.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@ static bool Quiet;
9999
static std::vector<uint64_t> LookupAddresses;
100100
static bool LookupAddressesFromStdin;
101101
static bool StoreMergedFunctionInfo = false;
102+
static bool LoadDwarfCallSites = false;
102103
static std::string CallSiteYamlPath;
103104

104105
static void parseArgs(int argc, char **argv) {
@@ -191,6 +192,8 @@ static void parseArgs(int argc, char **argv) {
191192
std::exit(1);
192193
}
193194
}
195+
196+
LoadDwarfCallSites = Args.hasArg(OPT_dwarf_callsites);
194197
}
195198

196199
/// @}
@@ -365,7 +368,7 @@ static llvm::Error handleObjectFile(ObjectFile &Obj, const std::string &OutFile,
365368

366369
// Make a DWARF transformer object and populate the ranges of the code
367370
// so we don't end up adding invalid functions to GSYM data.
368-
DwarfTransformer DT(*DICtx, Gsym);
371+
DwarfTransformer DT(*DICtx, Gsym, LoadDwarfCallSites);
369372
if (!TextRanges.empty())
370373
Gsym.SetValidTextRanges(TextRanges);
371374

0 commit comments

Comments
 (0)