diff options
| author | vitalyisaev <[email protected]> | 2023-06-29 10:00:50 +0300 |
|---|---|---|
| committer | vitalyisaev <[email protected]> | 2023-06-29 10:00:50 +0300 |
| commit | 6ffe9e53658409f212834330e13564e4952558f6 (patch) | |
| tree | 85b1e00183517648b228aafa7c8fb07f5276f419 /contrib/libs/llvm16/lib/DebugInfo/Symbolize/Symbolize.cpp | |
| parent | 726057070f9c5a91fc10fde0d5024913d10f1ab9 (diff) | |
YQ Connector: support managed ClickHouse
Со стороны dqrun можно обратиться к инстансу коннектора, который работает на streaming стенде, и извлечь данные из облачного CH.
Diffstat (limited to 'contrib/libs/llvm16/lib/DebugInfo/Symbolize/Symbolize.cpp')
| -rw-r--r-- | contrib/libs/llvm16/lib/DebugInfo/Symbolize/Symbolize.cpp | 744 |
1 files changed, 744 insertions, 0 deletions
diff --git a/contrib/libs/llvm16/lib/DebugInfo/Symbolize/Symbolize.cpp b/contrib/libs/llvm16/lib/DebugInfo/Symbolize/Symbolize.cpp new file mode 100644 index 00000000000..a92dfbcc5c6 --- /dev/null +++ b/contrib/libs/llvm16/lib/DebugInfo/Symbolize/Symbolize.cpp @@ -0,0 +1,744 @@ +//===-- LLVMSymbolize.cpp -------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// +// +// Implementation for LLVM symbolization library. +// +//===----------------------------------------------------------------------===// + +#include "llvm/DebugInfo/Symbolize/Symbolize.h" + +#include "llvm/ADT/STLExtras.h" +#include "llvm/DebugInfo/DWARF/DWARFContext.h" +#include "llvm/DebugInfo/PDB/PDB.h" +#include "llvm/DebugInfo/PDB/PDBContext.h" +#include "llvm/DebugInfo/Symbolize/SymbolizableObjectFile.h" +#include "llvm/Demangle/Demangle.h" +#include "llvm/Object/BuildID.h" +#include "llvm/Object/COFF.h" +#include "llvm/Object/ELFObjectFile.h" +#include "llvm/Object/MachO.h" +#include "llvm/Object/MachOUniversal.h" +#include "llvm/Support/CRC.h" +#include "llvm/Support/Casting.h" +#include "llvm/Support/DataExtractor.h" +#include "llvm/Support/Errc.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/Support/MemoryBuffer.h" +#include "llvm/Support/Path.h" +#include <algorithm> +#include <cassert> +#include <cstring> + +namespace llvm { +namespace codeview { +union DebugInfo; +} +namespace symbolize { + +LLVMSymbolizer::LLVMSymbolizer() = default; + +LLVMSymbolizer::LLVMSymbolizer(const Options &Opts) + : Opts(Opts), + BIDFetcher(std::make_unique<BuildIDFetcher>(Opts.DebugFileDirectory)) {} + +LLVMSymbolizer::~LLVMSymbolizer() = default; + +template <typename T> +Expected<DILineInfo> +LLVMSymbolizer::symbolizeCodeCommon(const T &ModuleSpecifier, + object::SectionedAddress ModuleOffset) { + + auto InfoOrErr = getOrCreateModuleInfo(ModuleSpecifier); + if (!InfoOrErr) + return InfoOrErr.takeError(); + + SymbolizableModule *Info = *InfoOrErr; + + // A null module means an error has already been reported. Return an empty + // result. + if (!Info) + return DILineInfo(); + + // If the user is giving us relative addresses, add the preferred base of the + // object to the offset before we do the query. It's what DIContext expects. + if (Opts.RelativeAddresses) + ModuleOffset.Address += Info->getModulePreferredBase(); + + DILineInfo LineInfo = Info->symbolizeCode( + ModuleOffset, DILineInfoSpecifier(Opts.PathStyle, Opts.PrintFunctions), + Opts.UseSymbolTable); + if (Opts.Demangle) + LineInfo.FunctionName = DemangleName(LineInfo.FunctionName, Info); + return LineInfo; +} + +Expected<DILineInfo> +LLVMSymbolizer::symbolizeCode(const ObjectFile &Obj, + object::SectionedAddress ModuleOffset) { + return symbolizeCodeCommon(Obj, ModuleOffset); +} + +Expected<DILineInfo> +LLVMSymbolizer::symbolizeCode(const std::string &ModuleName, + object::SectionedAddress ModuleOffset) { + return symbolizeCodeCommon(ModuleName, ModuleOffset); +} + +Expected<DILineInfo> +LLVMSymbolizer::symbolizeCode(ArrayRef<uint8_t> BuildID, + object::SectionedAddress ModuleOffset) { + return symbolizeCodeCommon(BuildID, ModuleOffset); +} + +template <typename T> +Expected<DIInliningInfo> LLVMSymbolizer::symbolizeInlinedCodeCommon( + const T &ModuleSpecifier, object::SectionedAddress ModuleOffset) { + auto InfoOrErr = getOrCreateModuleInfo(ModuleSpecifier); + if (!InfoOrErr) + return InfoOrErr.takeError(); + + SymbolizableModule *Info = *InfoOrErr; + + // A null module means an error has already been reported. Return an empty + // result. + if (!Info) + return DIInliningInfo(); + + // If the user is giving us relative addresses, add the preferred base of the + // object to the offset before we do the query. It's what DIContext expects. + if (Opts.RelativeAddresses) + ModuleOffset.Address += Info->getModulePreferredBase(); + + DIInliningInfo InlinedContext = Info->symbolizeInlinedCode( + ModuleOffset, DILineInfoSpecifier(Opts.PathStyle, Opts.PrintFunctions), + Opts.UseSymbolTable); + if (Opts.Demangle) { + for (int i = 0, n = InlinedContext.getNumberOfFrames(); i < n; i++) { + auto *Frame = InlinedContext.getMutableFrame(i); + Frame->FunctionName = DemangleName(Frame->FunctionName, Info); + } + } + return InlinedContext; +} + +Expected<DIInliningInfo> +LLVMSymbolizer::symbolizeInlinedCode(const ObjectFile &Obj, + object::SectionedAddress ModuleOffset) { + return symbolizeInlinedCodeCommon(Obj, ModuleOffset); +} + +Expected<DIInliningInfo> +LLVMSymbolizer::symbolizeInlinedCode(const std::string &ModuleName, + object::SectionedAddress ModuleOffset) { + return symbolizeInlinedCodeCommon(ModuleName, ModuleOffset); +} + +Expected<DIInliningInfo> +LLVMSymbolizer::symbolizeInlinedCode(ArrayRef<uint8_t> BuildID, + object::SectionedAddress ModuleOffset) { + return symbolizeInlinedCodeCommon(BuildID, ModuleOffset); +} + +template <typename T> +Expected<DIGlobal> +LLVMSymbolizer::symbolizeDataCommon(const T &ModuleSpecifier, + object::SectionedAddress ModuleOffset) { + + auto InfoOrErr = getOrCreateModuleInfo(ModuleSpecifier); + if (!InfoOrErr) + return InfoOrErr.takeError(); + + SymbolizableModule *Info = *InfoOrErr; + // A null module means an error has already been reported. Return an empty + // result. + if (!Info) + return DIGlobal(); + + // If the user is giving us relative addresses, add the preferred base of + // the object to the offset before we do the query. It's what DIContext + // expects. + if (Opts.RelativeAddresses) + ModuleOffset.Address += Info->getModulePreferredBase(); + + DIGlobal Global = Info->symbolizeData(ModuleOffset); + if (Opts.Demangle) + Global.Name = DemangleName(Global.Name, Info); + return Global; +} + +Expected<DIGlobal> +LLVMSymbolizer::symbolizeData(const ObjectFile &Obj, + object::SectionedAddress ModuleOffset) { + return symbolizeDataCommon(Obj, ModuleOffset); +} + +Expected<DIGlobal> +LLVMSymbolizer::symbolizeData(const std::string &ModuleName, + object::SectionedAddress ModuleOffset) { + return symbolizeDataCommon(ModuleName, ModuleOffset); +} + +Expected<DIGlobal> +LLVMSymbolizer::symbolizeData(ArrayRef<uint8_t> BuildID, + object::SectionedAddress ModuleOffset) { + return symbolizeDataCommon(BuildID, ModuleOffset); +} + +template <typename T> +Expected<std::vector<DILocal>> +LLVMSymbolizer::symbolizeFrameCommon(const T &ModuleSpecifier, + object::SectionedAddress ModuleOffset) { + auto InfoOrErr = getOrCreateModuleInfo(ModuleSpecifier); + if (!InfoOrErr) + return InfoOrErr.takeError(); + + SymbolizableModule *Info = *InfoOrErr; + // A null module means an error has already been reported. Return an empty + // result. + if (!Info) + return std::vector<DILocal>(); + + // If the user is giving us relative addresses, add the preferred base of + // the object to the offset before we do the query. It's what DIContext + // expects. + if (Opts.RelativeAddresses) + ModuleOffset.Address += Info->getModulePreferredBase(); + + return Info->symbolizeFrame(ModuleOffset); +} + +Expected<std::vector<DILocal>> +LLVMSymbolizer::symbolizeFrame(const ObjectFile &Obj, + object::SectionedAddress ModuleOffset) { + return symbolizeFrameCommon(Obj, ModuleOffset); +} + +Expected<std::vector<DILocal>> +LLVMSymbolizer::symbolizeFrame(const std::string &ModuleName, + object::SectionedAddress ModuleOffset) { + return symbolizeFrameCommon(ModuleName, ModuleOffset); +} + +Expected<std::vector<DILocal>> +LLVMSymbolizer::symbolizeFrame(ArrayRef<uint8_t> BuildID, + object::SectionedAddress ModuleOffset) { + return symbolizeFrameCommon(BuildID, ModuleOffset); +} + +void LLVMSymbolizer::flush() { + ObjectForUBPathAndArch.clear(); + LRUBinaries.clear(); + CacheSize = 0; + BinaryForPath.clear(); + ObjectPairForPathArch.clear(); + Modules.clear(); + BuildIDPaths.clear(); +} + +namespace { + +// For Path="/path/to/foo" and Basename="foo" assume that debug info is in +// /path/to/foo.dSYM/Contents/Resources/DWARF/foo. +// For Path="/path/to/bar.dSYM" and Basename="foo" assume that debug info is in +// /path/to/bar.dSYM/Contents/Resources/DWARF/foo. +std::string getDarwinDWARFResourceForPath(const std::string &Path, + const std::string &Basename) { + SmallString<16> ResourceName = StringRef(Path); + if (sys::path::extension(Path) != ".dSYM") { + ResourceName += ".dSYM"; + } + sys::path::append(ResourceName, "Contents", "Resources", "DWARF"); + sys::path::append(ResourceName, Basename); + return std::string(ResourceName.str()); +} + +bool checkFileCRC(StringRef Path, uint32_t CRCHash) { + ErrorOr<std::unique_ptr<MemoryBuffer>> MB = + MemoryBuffer::getFileOrSTDIN(Path); + if (!MB) + return false; + return CRCHash == llvm::crc32(arrayRefFromStringRef(MB.get()->getBuffer())); +} + +bool getGNUDebuglinkContents(const ObjectFile *Obj, std::string &DebugName, + uint32_t &CRCHash) { + if (!Obj) + return false; + for (const SectionRef &Section : Obj->sections()) { + StringRef Name; + consumeError(Section.getName().moveInto(Name)); + + Name = Name.substr(Name.find_first_not_of("._")); + if (Name == "gnu_debuglink") { + Expected<StringRef> ContentsOrErr = Section.getContents(); + if (!ContentsOrErr) { + consumeError(ContentsOrErr.takeError()); + return false; + } + DataExtractor DE(*ContentsOrErr, Obj->isLittleEndian(), 0); + uint64_t Offset = 0; + if (const char *DebugNameStr = DE.getCStr(&Offset)) { + // 4-byte align the offset. + Offset = (Offset + 3) & ~0x3; + if (DE.isValidOffsetForDataOfSize(Offset, 4)) { + DebugName = DebugNameStr; + CRCHash = DE.getU32(&Offset); + return true; + } + } + break; + } + } + return false; +} + +bool darwinDsymMatchesBinary(const MachOObjectFile *DbgObj, + const MachOObjectFile *Obj) { + ArrayRef<uint8_t> dbg_uuid = DbgObj->getUuid(); + ArrayRef<uint8_t> bin_uuid = Obj->getUuid(); + if (dbg_uuid.empty() || bin_uuid.empty()) + return false; + return !memcmp(dbg_uuid.data(), bin_uuid.data(), dbg_uuid.size()); +} + +} // end anonymous namespace + +ObjectFile *LLVMSymbolizer::lookUpDsymFile(const std::string &ExePath, + const MachOObjectFile *MachExeObj, + const std::string &ArchName) { + // On Darwin we may find DWARF in separate object file in + // resource directory. + std::vector<std::string> DsymPaths; + StringRef Filename = sys::path::filename(ExePath); + DsymPaths.push_back( + getDarwinDWARFResourceForPath(ExePath, std::string(Filename))); + for (const auto &Path : Opts.DsymHints) { + DsymPaths.push_back( + getDarwinDWARFResourceForPath(Path, std::string(Filename))); + } + for (const auto &Path : DsymPaths) { + auto DbgObjOrErr = getOrCreateObject(Path, ArchName); + if (!DbgObjOrErr) { + // Ignore errors, the file might not exist. + consumeError(DbgObjOrErr.takeError()); + continue; + } + ObjectFile *DbgObj = DbgObjOrErr.get(); + if (!DbgObj) + continue; + const MachOObjectFile *MachDbgObj = dyn_cast<const MachOObjectFile>(DbgObj); + if (!MachDbgObj) + continue; + if (darwinDsymMatchesBinary(MachDbgObj, MachExeObj)) + return DbgObj; + } + return nullptr; +} + +ObjectFile *LLVMSymbolizer::lookUpDebuglinkObject(const std::string &Path, + const ObjectFile *Obj, + const std::string &ArchName) { + std::string DebuglinkName; + uint32_t CRCHash; + std::string DebugBinaryPath; + if (!getGNUDebuglinkContents(Obj, DebuglinkName, CRCHash)) + return nullptr; + if (!findDebugBinary(Path, DebuglinkName, CRCHash, DebugBinaryPath)) + return nullptr; + auto DbgObjOrErr = getOrCreateObject(DebugBinaryPath, ArchName); + if (!DbgObjOrErr) { + // Ignore errors, the file might not exist. + consumeError(DbgObjOrErr.takeError()); + return nullptr; + } + return DbgObjOrErr.get(); +} + +ObjectFile *LLVMSymbolizer::lookUpBuildIDObject(const std::string &Path, + const ELFObjectFileBase *Obj, + const std::string &ArchName) { + auto BuildID = getBuildID(Obj); + if (!BuildID) + return nullptr; + if (BuildID->size() < 2) + return nullptr; + std::string DebugBinaryPath; + if (!getOrFindDebugBinary(*BuildID, DebugBinaryPath)) + return nullptr; + auto DbgObjOrErr = getOrCreateObject(DebugBinaryPath, ArchName); + if (!DbgObjOrErr) { + consumeError(DbgObjOrErr.takeError()); + return nullptr; + } + return DbgObjOrErr.get(); +} + +bool LLVMSymbolizer::findDebugBinary(const std::string &OrigPath, + const std::string &DebuglinkName, + uint32_t CRCHash, std::string &Result) { + SmallString<16> OrigDir(OrigPath); + llvm::sys::path::remove_filename(OrigDir); + SmallString<16> DebugPath = OrigDir; + // Try relative/path/to/original_binary/debuglink_name + llvm::sys::path::append(DebugPath, DebuglinkName); + if (checkFileCRC(DebugPath, CRCHash)) { + Result = std::string(DebugPath.str()); + return true; + } + // Try relative/path/to/original_binary/.debug/debuglink_name + DebugPath = OrigDir; + llvm::sys::path::append(DebugPath, ".debug", DebuglinkName); + if (checkFileCRC(DebugPath, CRCHash)) { + Result = std::string(DebugPath.str()); + return true; + } + // Make the path absolute so that lookups will go to + // "/usr/lib/debug/full/path/to/debug", not + // "/usr/lib/debug/to/debug" + llvm::sys::fs::make_absolute(OrigDir); + if (!Opts.FallbackDebugPath.empty()) { + // Try <FallbackDebugPath>/absolute/path/to/original_binary/debuglink_name + DebugPath = Opts.FallbackDebugPath; + } else { +#if defined(__NetBSD__) + // Try /usr/libdata/debug/absolute/path/to/original_binary/debuglink_name + DebugPath = "/usr/libdata/debug"; +#else + // Try /usr/lib/debug/absolute/path/to/original_binary/debuglink_name + DebugPath = "/usr/lib/debug"; +#endif + } + llvm::sys::path::append(DebugPath, llvm::sys::path::relative_path(OrigDir), + DebuglinkName); + if (checkFileCRC(DebugPath, CRCHash)) { + Result = std::string(DebugPath.str()); + return true; + } + return false; +} + +static StringRef getBuildIDStr(ArrayRef<uint8_t> BuildID) { + return StringRef(reinterpret_cast<const char *>(BuildID.data()), + BuildID.size()); +} + +bool LLVMSymbolizer::getOrFindDebugBinary(const ArrayRef<uint8_t> BuildID, + std::string &Result) { + StringRef BuildIDStr = getBuildIDStr(BuildID); + auto I = BuildIDPaths.find(BuildIDStr); + if (I != BuildIDPaths.end()) { + Result = I->second; + return true; + } + if (!BIDFetcher) + return false; + if (std::optional<std::string> Path = BIDFetcher->fetch(BuildID)) { + Result = *Path; + auto InsertResult = BuildIDPaths.insert({BuildIDStr, Result}); + assert(InsertResult.second); + (void)InsertResult; + return true; + } + + return false; +} + +Expected<LLVMSymbolizer::ObjectPair> +LLVMSymbolizer::getOrCreateObjectPair(const std::string &Path, + const std::string &ArchName) { + auto I = ObjectPairForPathArch.find(std::make_pair(Path, ArchName)); + if (I != ObjectPairForPathArch.end()) { + recordAccess(BinaryForPath.find(Path)->second); + return I->second; + } + + auto ObjOrErr = getOrCreateObject(Path, ArchName); + if (!ObjOrErr) { + ObjectPairForPathArch.emplace(std::make_pair(Path, ArchName), + ObjectPair(nullptr, nullptr)); + return ObjOrErr.takeError(); + } + + ObjectFile *Obj = ObjOrErr.get(); + assert(Obj != nullptr); + ObjectFile *DbgObj = nullptr; + + if (auto MachObj = dyn_cast<const MachOObjectFile>(Obj)) + DbgObj = lookUpDsymFile(Path, MachObj, ArchName); + else if (auto ELFObj = dyn_cast<const ELFObjectFileBase>(Obj)) + DbgObj = lookUpBuildIDObject(Path, ELFObj, ArchName); + if (!DbgObj) + DbgObj = lookUpDebuglinkObject(Path, Obj, ArchName); + if (!DbgObj) + DbgObj = Obj; + ObjectPair Res = std::make_pair(Obj, DbgObj); + std::string DbgObjPath = DbgObj->getFileName().str(); + auto Pair = + ObjectPairForPathArch.emplace(std::make_pair(Path, ArchName), Res); + BinaryForPath.find(DbgObjPath)->second.pushEvictor([this, I = Pair.first]() { + ObjectPairForPathArch.erase(I); + }); + return Res; +} + +Expected<ObjectFile *> +LLVMSymbolizer::getOrCreateObject(const std::string &Path, + const std::string &ArchName) { + Binary *Bin; + auto Pair = BinaryForPath.emplace(Path, OwningBinary<Binary>()); + if (!Pair.second) { + Bin = Pair.first->second->getBinary(); + recordAccess(Pair.first->second); + } else { + Expected<OwningBinary<Binary>> BinOrErr = createBinary(Path); + if (!BinOrErr) + return BinOrErr.takeError(); + + CachedBinary &CachedBin = Pair.first->second; + CachedBin = std::move(BinOrErr.get()); + CachedBin.pushEvictor([this, I = Pair.first]() { BinaryForPath.erase(I); }); + LRUBinaries.push_back(CachedBin); + CacheSize += CachedBin.size(); + Bin = CachedBin->getBinary(); + } + + if (!Bin) + return static_cast<ObjectFile *>(nullptr); + + if (MachOUniversalBinary *UB = dyn_cast_or_null<MachOUniversalBinary>(Bin)) { + auto I = ObjectForUBPathAndArch.find(std::make_pair(Path, ArchName)); + if (I != ObjectForUBPathAndArch.end()) + return I->second.get(); + + Expected<std::unique_ptr<ObjectFile>> ObjOrErr = + UB->getMachOObjectForArch(ArchName); + if (!ObjOrErr) { + ObjectForUBPathAndArch.emplace(std::make_pair(Path, ArchName), + std::unique_ptr<ObjectFile>()); + return ObjOrErr.takeError(); + } + ObjectFile *Res = ObjOrErr->get(); + auto Pair = ObjectForUBPathAndArch.emplace(std::make_pair(Path, ArchName), + std::move(ObjOrErr.get())); + BinaryForPath.find(Path)->second.pushEvictor( + [this, Iter = Pair.first]() { ObjectForUBPathAndArch.erase(Iter); }); + return Res; + } + if (Bin->isObject()) { + return cast<ObjectFile>(Bin); + } + return errorCodeToError(object_error::arch_not_found); +} + +Expected<SymbolizableModule *> +LLVMSymbolizer::createModuleInfo(const ObjectFile *Obj, + std::unique_ptr<DIContext> Context, + StringRef ModuleName) { + auto InfoOrErr = SymbolizableObjectFile::create(Obj, std::move(Context), + Opts.UntagAddresses); + std::unique_ptr<SymbolizableModule> SymMod; + if (InfoOrErr) + SymMod = std::move(*InfoOrErr); + auto InsertResult = Modules.insert( + std::make_pair(std::string(ModuleName), std::move(SymMod))); + assert(InsertResult.second); + if (!InfoOrErr) + return InfoOrErr.takeError(); + return InsertResult.first->second.get(); +} + +Expected<SymbolizableModule *> +LLVMSymbolizer::getOrCreateModuleInfo(const std::string &ModuleName) { + std::string BinaryName = ModuleName; + std::string ArchName = Opts.DefaultArch; + size_t ColonPos = ModuleName.find_last_of(':'); + // Verify that substring after colon form a valid arch name. + if (ColonPos != std::string::npos) { + std::string ArchStr = ModuleName.substr(ColonPos + 1); + if (Triple(ArchStr).getArch() != Triple::UnknownArch) { + BinaryName = ModuleName.substr(0, ColonPos); + ArchName = ArchStr; + } + } + + auto I = Modules.find(ModuleName); + if (I != Modules.end()) { + recordAccess(BinaryForPath.find(BinaryName)->second); + return I->second.get(); + } + + auto ObjectsOrErr = getOrCreateObjectPair(BinaryName, ArchName); + if (!ObjectsOrErr) { + // Failed to find valid object file. + Modules.emplace(ModuleName, std::unique_ptr<SymbolizableModule>()); + return ObjectsOrErr.takeError(); + } + ObjectPair Objects = ObjectsOrErr.get(); + + std::unique_ptr<DIContext> Context; + // If this is a COFF object containing PDB info, use a PDBContext to + // symbolize. Otherwise, use DWARF. + if (auto CoffObject = dyn_cast<COFFObjectFile>(Objects.first)) { + const codeview::DebugInfo *DebugInfo; + StringRef PDBFileName; + auto EC = CoffObject->getDebugPDBInfo(DebugInfo, PDBFileName); + if (!EC && DebugInfo != nullptr && !PDBFileName.empty()) { + using namespace pdb; + std::unique_ptr<IPDBSession> Session; + + PDB_ReaderType ReaderType = + Opts.UseDIA ? PDB_ReaderType::DIA : PDB_ReaderType::Native; + if (auto Err = loadDataForEXE(ReaderType, Objects.first->getFileName(), + Session)) { + Modules.emplace(ModuleName, std::unique_ptr<SymbolizableModule>()); + // Return along the PDB filename to provide more context + return createFileError(PDBFileName, std::move(Err)); + } + Context.reset(new PDBContext(*CoffObject, std::move(Session))); + } + } + if (!Context) + Context = DWARFContext::create( + *Objects.second, DWARFContext::ProcessDebugRelocations::Process, + nullptr, Opts.DWPName); + auto ModuleOrErr = + createModuleInfo(Objects.first, std::move(Context), ModuleName); + if (ModuleOrErr) { + auto I = Modules.find(ModuleName); + BinaryForPath.find(BinaryName)->second.pushEvictor([this, I]() { + Modules.erase(I); + }); + } + return ModuleOrErr; +} + +Expected<SymbolizableModule *> +LLVMSymbolizer::getOrCreateModuleInfo(const ObjectFile &Obj) { + StringRef ObjName = Obj.getFileName(); + auto I = Modules.find(ObjName); + if (I != Modules.end()) + return I->second.get(); + + std::unique_ptr<DIContext> Context = DWARFContext::create(Obj); + // FIXME: handle COFF object with PDB info to use PDBContext + return createModuleInfo(&Obj, std::move(Context), ObjName); +} + +Expected<SymbolizableModule *> +LLVMSymbolizer::getOrCreateModuleInfo(ArrayRef<uint8_t> BuildID) { + std::string Path; + if (!getOrFindDebugBinary(BuildID, Path)) { + return createStringError(errc::no_such_file_or_directory, + Twine("could not find build ID '") + + toHex(BuildID) + "'"); + } + return getOrCreateModuleInfo(Path); +} + +namespace { + +// Undo these various manglings for Win32 extern "C" functions: +// cdecl - _foo +// stdcall - _foo@12 +// fastcall - @foo@12 +// vectorcall - foo@@12 +// These are all different linkage names for 'foo'. +StringRef demanglePE32ExternCFunc(StringRef SymbolName) { + char Front = SymbolName.empty() ? '\0' : SymbolName[0]; + + // Remove any '@[0-9]+' suffix. + bool HasAtNumSuffix = false; + if (Front != '?') { + size_t AtPos = SymbolName.rfind('@'); + if (AtPos != StringRef::npos && + all_of(drop_begin(SymbolName, AtPos + 1), isDigit)) { + SymbolName = SymbolName.substr(0, AtPos); + HasAtNumSuffix = true; + } + } + + // Remove any ending '@' for vectorcall. + bool IsVectorCall = false; + if (HasAtNumSuffix && SymbolName.endswith("@")) { + SymbolName = SymbolName.drop_back(); + IsVectorCall = true; + } + + // If not vectorcall, remove any '_' or '@' prefix. + if (!IsVectorCall && (Front == '_' || Front == '@')) + SymbolName = SymbolName.drop_front(); + + return SymbolName; +} + +} // end anonymous namespace + +std::string +LLVMSymbolizer::DemangleName(const std::string &Name, + const SymbolizableModule *DbiModuleDescriptor) { + std::string Result; + if (nonMicrosoftDemangle(Name.c_str(), Result)) + return Result; + + if (!Name.empty() && Name.front() == '?') { + // Only do MSVC C++ demangling on symbols starting with '?'. + int status = 0; + char *DemangledName = microsoftDemangle( + Name.c_str(), nullptr, nullptr, nullptr, &status, + MSDemangleFlags(MSDF_NoAccessSpecifier | MSDF_NoCallingConvention | + MSDF_NoMemberType | MSDF_NoReturnType)); + if (status != 0) + return Name; + Result = DemangledName; + free(DemangledName); + return Result; + } + + if (DbiModuleDescriptor && DbiModuleDescriptor->isWin32Module()) { + std::string DemangledCName(demanglePE32ExternCFunc(Name)); + // On i386 Windows, the C name mangling for different calling conventions + // may also be applied on top of the Itanium or Rust name mangling. + if (nonMicrosoftDemangle(DemangledCName.c_str(), Result)) + return Result; + return DemangledCName; + } + return Name; +} + +void LLVMSymbolizer::recordAccess(CachedBinary &Bin) { + if (Bin->getBinary()) + LRUBinaries.splice(LRUBinaries.end(), LRUBinaries, Bin.getIterator()); +} + +void LLVMSymbolizer::pruneCache() { + // Evict the LRU binary until the max cache size is reached or there's <= 1 + // item in the cache. The MRU binary is always kept to avoid thrashing if it's + // larger than the cache size. + while (CacheSize > Opts.MaxCacheSize && !LRUBinaries.empty() && + std::next(LRUBinaries.begin()) != LRUBinaries.end()) { + CachedBinary &Bin = LRUBinaries.front(); + CacheSize -= Bin.size(); + LRUBinaries.pop_front(); + Bin.evict(); + } +} + +void CachedBinary::pushEvictor(std::function<void()> NewEvictor) { + if (Evictor) { + this->Evictor = [OldEvictor = std::move(this->Evictor), + NewEvictor = std::move(NewEvictor)]() { + NewEvictor(); + OldEvictor(); + }; + } else { + this->Evictor = std::move(NewEvictor); + } +} + +} // namespace symbolize +} // namespace llvm |
