Skip to content

Commit d896e28

Browse files
LLVM symbolizer gsym support (#134847)
Add support for gsym files to llvm-symbolizer. co-author @sfc-gh-sgiesecke --------- Co-authored-by: David Blaikie <dblaikie@gmail.com>
1 parent 78f0af5 commit d896e28

File tree

13 files changed

+410
-24
lines changed

13 files changed

+410
-24
lines changed

llvm/include/llvm/DebugInfo/DIContext.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ struct DIDumpOptions {
238238

239239
class DIContext {
240240
public:
241-
enum DIContextKind { CK_DWARF, CK_PDB, CK_BTF };
241+
enum DIContextKind { CK_DWARF, CK_PDB, CK_BTF, CK_GSYM };
242242

243243
DIContext(DIContextKind K) : Kind(K) {}
244244
virtual ~DIContext() = default;
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
//===-- GsymDIContext.h --------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===/
8+
9+
#ifndef LLVM_DEBUGINFO_GSYM_GSYMDICONTEXT_H
10+
#define LLVM_DEBUGINFO_GSYM_GSYMDICONTEXT_H
11+
12+
#include "llvm/DebugInfo/DIContext.h"
13+
#include <cstdint>
14+
#include <memory>
15+
#include <string>
16+
17+
namespace llvm {
18+
19+
namespace gsym {
20+
21+
class GsymReader;
22+
23+
/// GSYM DI Context
24+
/// This data structure is the top level entity that deals with GSYM
25+
/// symbolication.
26+
/// This data structure exists only when there is a need for a transparent
27+
/// interface to different symbolication formats (e.g. GSYM, PDB and DWARF).
28+
/// More control and power over the debug information access can be had by using
29+
/// the GSYM interfaces directly.
30+
class GsymDIContext : public DIContext {
31+
public:
32+
GsymDIContext(std::unique_ptr<GsymReader> Reader);
33+
34+
GsymDIContext(GsymDIContext &) = delete;
35+
GsymDIContext &operator=(GsymDIContext &) = delete;
36+
37+
static bool classof(const DIContext *DICtx) {
38+
return DICtx->getKind() == CK_GSYM;
39+
}
40+
41+
void dump(raw_ostream &OS, DIDumpOptions DIDumpOpts) override;
42+
43+
std::optional<DILineInfo> getLineInfoForAddress(
44+
object::SectionedAddress Address,
45+
DILineInfoSpecifier Specifier = DILineInfoSpecifier()) override;
46+
std::optional<DILineInfo>
47+
getLineInfoForDataAddress(object::SectionedAddress Address) override;
48+
DILineInfoTable getLineInfoForAddressRange(
49+
object::SectionedAddress Address, uint64_t Size,
50+
DILineInfoSpecifier Specifier = DILineInfoSpecifier()) override;
51+
DIInliningInfo getInliningInfoForAddress(
52+
object::SectionedAddress Address,
53+
DILineInfoSpecifier Specifier = DILineInfoSpecifier()) override;
54+
55+
std::vector<DILocal>
56+
getLocalsForAddress(object::SectionedAddress Address) override;
57+
58+
private:
59+
const std::unique_ptr<GsymReader> Reader;
60+
};
61+
62+
} // end namespace gsym
63+
64+
} // end namespace llvm
65+
66+
#endif // LLVM_DEBUGINFO_PDB_PDBCONTEXT_H

llvm/include/llvm/DebugInfo/Symbolize/Symbolize.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,13 @@ class LLVMSymbolizer {
5858
bool RelativeAddresses = false;
5959
bool UntagAddresses = false;
6060
bool UseDIA = false;
61+
bool DisableGsym = false;
6162
std::string DefaultArch;
6263
std::vector<std::string> DsymHints;
6364
std::string FallbackDebugPath;
6465
std::string DWPName;
6566
std::vector<std::string> DebugFileDirectory;
67+
std::vector<std::string> GsymFileDirectory;
6668
size_t MaxCacheSize =
6769
sizeof(size_t) == 4
6870
? 512 * 1024 * 1024 /* 512 MiB */
@@ -177,6 +179,7 @@ class LLVMSymbolizer {
177179
ObjectFile *lookUpBuildIDObject(const std::string &Path,
178180
const ELFObjectFileBase *Obj,
179181
const std::string &ArchName);
182+
std::string lookUpGsymFile(const std::string &Path);
180183

181184
bool findDebugBinary(const std::string &OrigPath,
182185
const std::string &DebuglinkName, uint32_t CRCHash,

llvm/lib/DebugInfo/GSYM/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ add_llvm_component_library(LLVMDebugInfoGSYM
44
FileWriter.cpp
55
FunctionInfo.cpp
66
GsymCreator.cpp
7+
GsymDIContext.cpp
78
GsymReader.cpp
89
InlineInfo.cpp
910
LineTable.cpp
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
//===-- GsymDIContext.cpp ------------------------------------------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===/
8+
9+
#include "llvm/DebugInfo/GSYM/GsymDIContext.h"
10+
11+
#include "llvm/DebugInfo/GSYM/GsymReader.h"
12+
#include "llvm/Support/Path.h"
13+
14+
using namespace llvm;
15+
using namespace llvm::gsym;
16+
17+
GsymDIContext::GsymDIContext(std::unique_ptr<GsymReader> Reader)
18+
: DIContext(CK_GSYM), Reader(std::move(Reader)) {}
19+
20+
void GsymDIContext::dump(raw_ostream &OS, DIDumpOptions DumpOpts) {}
21+
22+
static bool fillLineInfoFromLocation(const SourceLocation &Location,
23+
DILineInfoSpecifier Specifier,
24+
DILineInfo &LineInfo) {
25+
// FIXME Demangle in case of DINameKind::ShortName
26+
if (Specifier.FNKind != DINameKind::None) {
27+
LineInfo.FunctionName = Location.Name.str();
28+
}
29+
30+
switch (Specifier.FLIKind) {
31+
case DILineInfoSpecifier::FileLineInfoKind::RelativeFilePath:
32+
// We have no information to determine the relative path, so we fall back to
33+
// returning the absolute path.
34+
case DILineInfoSpecifier::FileLineInfoKind::RawValue:
35+
case DILineInfoSpecifier::FileLineInfoKind::AbsoluteFilePath:
36+
if (Location.Dir.empty()) {
37+
if (Location.Base.empty())
38+
LineInfo.FileName = DILineInfo::BadString;
39+
else
40+
LineInfo.FileName = Location.Base.str();
41+
} else {
42+
SmallString<128> Path(Location.Dir);
43+
sys::path::append(Path, Location.Base);
44+
LineInfo.FileName = static_cast<std::string>(Path);
45+
}
46+
break;
47+
48+
case DILineInfoSpecifier::FileLineInfoKind::BaseNameOnly:
49+
LineInfo.FileName = Location.Base.str();
50+
break;
51+
52+
default:
53+
return false;
54+
}
55+
LineInfo.Line = Location.Line;
56+
57+
// We don't have information in GSYM to fill any of the Source, Column,
58+
// StartFileName or StartLine attributes.
59+
60+
return true;
61+
}
62+
63+
std::optional<DILineInfo>
64+
GsymDIContext::getLineInfoForAddress(object::SectionedAddress Address,
65+
DILineInfoSpecifier Specifier) {
66+
if (Address.SectionIndex != object::SectionedAddress::UndefSection)
67+
return {};
68+
69+
auto ResultOrErr = Reader->lookup(Address.Address);
70+
71+
if (!ResultOrErr) {
72+
consumeError(ResultOrErr.takeError());
73+
return {};
74+
}
75+
76+
const auto &Result = *ResultOrErr;
77+
78+
DILineInfo LineInfo;
79+
80+
if (Result.Locations.empty()) {
81+
// No debug info for this, we just had a symbol from the symbol table.
82+
83+
// FIXME Demangle in case of DINameKind::ShortName
84+
if (Specifier.FNKind != DINameKind::None)
85+
LineInfo.FunctionName = Result.FuncName.str();
86+
} else if (!fillLineInfoFromLocation(Result.Locations.front(), Specifier,
87+
LineInfo))
88+
return {};
89+
90+
LineInfo.StartAddress = Result.FuncRange.start();
91+
92+
return LineInfo;
93+
}
94+
95+
std::optional<DILineInfo>
96+
GsymDIContext::getLineInfoForDataAddress(object::SectionedAddress Address) {
97+
// We can't implement this, there's no such information in the GSYM file.
98+
99+
return {};
100+
}
101+
102+
DILineInfoTable
103+
GsymDIContext::getLineInfoForAddressRange(object::SectionedAddress Address,
104+
uint64_t Size,
105+
DILineInfoSpecifier Specifier) {
106+
if (Size == 0)
107+
return DILineInfoTable();
108+
109+
if (Address.SectionIndex != llvm::object::SectionedAddress::UndefSection)
110+
return DILineInfoTable();
111+
112+
if (auto FuncInfoOrErr = Reader->getFunctionInfo(Address.Address)) {
113+
DILineInfoTable Table;
114+
if (FuncInfoOrErr->OptLineTable) {
115+
const gsym::LineTable &LT = *FuncInfoOrErr->OptLineTable;
116+
const uint64_t StartAddr = Address.Address;
117+
const uint64_t EndAddr = Address.Address + Size;
118+
for (const auto &LineEntry : LT) {
119+
if (StartAddr <= LineEntry.Addr && LineEntry.Addr < EndAddr) {
120+
// Use LineEntry.Addr, LineEntry.File (which is a file index into the
121+
// files tables from the GsymReader), and LineEntry.Line (source line
122+
// number) to add stuff to the DILineInfoTable
123+
}
124+
}
125+
}
126+
return Table;
127+
} else {
128+
consumeError(FuncInfoOrErr.takeError());
129+
return DILineInfoTable();
130+
}
131+
}
132+
133+
DIInliningInfo
134+
GsymDIContext::getInliningInfoForAddress(object::SectionedAddress Address,
135+
DILineInfoSpecifier Specifier) {
136+
auto ResultOrErr = Reader->lookup(Address.Address);
137+
138+
if (!ResultOrErr)
139+
return {};
140+
141+
const auto &Result = *ResultOrErr;
142+
143+
DIInliningInfo InlineInfo;
144+
145+
for (const auto &Location : Result.Locations) {
146+
DILineInfo LineInfo;
147+
148+
if (!fillLineInfoFromLocation(Location, Specifier, LineInfo))
149+
return {};
150+
151+
// Hm, that's probably something that should only be filled in the first or
152+
// last frame?
153+
LineInfo.StartAddress = Result.FuncRange.start();
154+
155+
InlineInfo.addFrame(LineInfo);
156+
}
157+
158+
return InlineInfo;
159+
}
160+
161+
std::vector<DILocal>
162+
GsymDIContext::getLocalsForAddress(object::SectionedAddress Address) {
163+
// We can't implement this, there's no such information in the GSYM file.
164+
165+
return {};
166+
}

llvm/lib/DebugInfo/Symbolize/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ add_llvm_component_library(LLVMSymbolize
1010

1111
LINK_COMPONENTS
1212
DebugInfoDWARF
13+
DebugInfoGSYM
1314
DebugInfoPDB
1415
DebugInfoBTF
1516
Object

llvm/lib/DebugInfo/Symbolize/Symbolize.cpp

Lines changed: 71 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@
1515
#include "llvm/ADT/STLExtras.h"
1616
#include "llvm/DebugInfo/BTF/BTFContext.h"
1717
#include "llvm/DebugInfo/DWARF/DWARFContext.h"
18+
#include "llvm/DebugInfo/GSYM/GsymDIContext.h"
19+
#include "llvm/DebugInfo/GSYM/GsymReader.h"
1820
#include "llvm/DebugInfo/PDB/PDB.h"
1921
#include "llvm/DebugInfo/PDB/PDBContext.h"
2022
#include "llvm/DebugInfo/Symbolize/SymbolizableObjectFile.h"
@@ -498,6 +500,34 @@ bool LLVMSymbolizer::getOrFindDebugBinary(const ArrayRef<uint8_t> BuildID,
498500
return false;
499501
}
500502

503+
std::string LLVMSymbolizer::lookUpGsymFile(const std::string &Path) {
504+
if (Opts.DisableGsym)
505+
return {};
506+
507+
auto CheckGsymFile = [](const llvm::StringRef &GsymPath) {
508+
sys::fs::file_status Status;
509+
std::error_code EC = llvm::sys::fs::status(GsymPath, Status);
510+
return !EC && !llvm::sys::fs::is_directory(Status);
511+
};
512+
513+
// First, look beside the binary file
514+
if (const auto GsymPath = Path + ".gsym"; CheckGsymFile(GsymPath))
515+
return GsymPath;
516+
517+
// Then, look in the directories specified by GsymFileDirectory
518+
519+
for (const auto &Directory : Opts.GsymFileDirectory) {
520+
SmallString<16> GsymPath = llvm::StringRef{Directory};
521+
llvm::sys::path::append(GsymPath,
522+
llvm::sys::path::filename(Path) + ".gsym");
523+
524+
if (CheckGsymFile(GsymPath))
525+
return static_cast<std::string>(GsymPath);
526+
}
527+
528+
return {};
529+
}
530+
501531
Expected<LLVMSymbolizer::ObjectPair>
502532
LLVMSymbolizer::getOrCreateObjectPair(const std::string &Path,
503533
const std::string &ArchName) {
@@ -634,30 +664,48 @@ LLVMSymbolizer::getOrCreateModuleInfo(StringRef ModuleName) {
634664
std::unique_ptr<DIContext> Context;
635665
// If this is a COFF object containing PDB info and not containing DWARF
636666
// section, use a PDBContext to symbolize. Otherwise, use DWARF.
637-
if (auto CoffObject = dyn_cast<COFFObjectFile>(Objects.first)) {
638-
const codeview::DebugInfo *DebugInfo;
639-
StringRef PDBFileName;
640-
auto EC = CoffObject->getDebugPDBInfo(DebugInfo, PDBFileName);
641-
// Use DWARF if there're DWARF sections.
642-
bool HasDwarf =
643-
llvm::any_of(Objects.first->sections(), [](SectionRef Section) -> bool {
644-
if (Expected<StringRef> SectionName = Section.getName())
645-
return SectionName.get() == ".debug_info";
646-
return false;
647-
});
648-
if (!EC && !HasDwarf && DebugInfo != nullptr && !PDBFileName.empty()) {
649-
using namespace pdb;
650-
std::unique_ptr<IPDBSession> Session;
651-
652-
PDB_ReaderType ReaderType =
653-
Opts.UseDIA ? PDB_ReaderType::DIA : PDB_ReaderType::Native;
654-
if (auto Err = loadDataForEXE(ReaderType, Objects.first->getFileName(),
655-
Session)) {
656-
Modules.emplace(ModuleName, std::unique_ptr<SymbolizableModule>());
657-
// Return along the PDB filename to provide more context
658-
return createFileError(PDBFileName, std::move(Err));
667+
// Create a DIContext to symbolize as follows:
668+
// - If there is a GSYM file, create a GsymDIContext.
669+
// - Otherwise, if this is a COFF object containing PDB info, create a
670+
// PDBContext.
671+
// - Otherwise, create a DWARFContext.
672+
const auto GsymFile = lookUpGsymFile(BinaryName.str());
673+
if (!GsymFile.empty()) {
674+
auto ReaderOrErr = gsym::GsymReader::openFile(GsymFile);
675+
676+
if (ReaderOrErr) {
677+
std::unique_ptr<gsym::GsymReader> Reader =
678+
std::make_unique<gsym::GsymReader>(std::move(*ReaderOrErr));
679+
680+
Context = std::make_unique<gsym::GsymDIContext>(std::move(Reader));
681+
}
682+
}
683+
if (!Context) {
684+
if (auto CoffObject = dyn_cast<COFFObjectFile>(Objects.first)) {
685+
const codeview::DebugInfo *DebugInfo;
686+
StringRef PDBFileName;
687+
auto EC = CoffObject->getDebugPDBInfo(DebugInfo, PDBFileName);
688+
// Use DWARF if there're DWARF sections.
689+
bool HasDwarf = llvm::any_of(
690+
Objects.first->sections(), [](SectionRef Section) -> bool {
691+
if (Expected<StringRef> SectionName = Section.getName())
692+
return SectionName.get() == ".debug_info";
693+
return false;
694+
});
695+
if (!EC && !HasDwarf && DebugInfo != nullptr && !PDBFileName.empty()) {
696+
using namespace pdb;
697+
std::unique_ptr<IPDBSession> Session;
698+
699+
PDB_ReaderType ReaderType =
700+
Opts.UseDIA ? PDB_ReaderType::DIA : PDB_ReaderType::Native;
701+
if (auto Err = loadDataForEXE(ReaderType, Objects.first->getFileName(),
702+
Session)) {
703+
Modules.emplace(ModuleName, std::unique_ptr<SymbolizableModule>());
704+
// Return along the PDB filename to provide more context
705+
return createFileError(PDBFileName, std::move(Err));
706+
}
707+
Context.reset(new PDBContext(*CoffObject, std::move(Session)));
659708
}
660-
Context.reset(new PDBContext(*CoffObject, std::move(Session)));
661709
}
662710
}
663711
if (!Context)
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)