|  | //===- tools/dsymutil/DebugMap.cpp - Generic debug map representation -----===// | 
|  | // | 
|  | //                     The LLVM Compiler Infrastructure | 
|  | // | 
|  | // This file is distributed under the University of Illinois Open Source | 
|  | // License. See LICENSE.TXT for details. | 
|  | // | 
|  | //===----------------------------------------------------------------------===// | 
|  |  | 
|  | #include "DebugMap.h" | 
|  | #include "BinaryHolder.h" | 
|  | #include "llvm/ADT/Optional.h" | 
|  | #include "llvm/ADT/SmallString.h" | 
|  | #include "llvm/ADT/StringMap.h" | 
|  | #include "llvm/ADT/StringRef.h" | 
|  | #include "llvm/ADT/Triple.h" | 
|  | #include "llvm/ADT/iterator_range.h" | 
|  | #include "llvm/BinaryFormat/MachO.h" | 
|  | #include "llvm/Object/ObjectFile.h" | 
|  | #include "llvm/Support/Chrono.h" | 
|  | #include "llvm/Support/Error.h" | 
|  | #include "llvm/Support/Format.h" | 
|  | #include "llvm/Support/MemoryBuffer.h" | 
|  | #include "llvm/Support/Path.h" | 
|  | #include "llvm/Support/WithColor.h" | 
|  | #include "llvm/Support/YAMLTraits.h" | 
|  | #include "llvm/Support/raw_ostream.h" | 
|  | #include <algorithm> | 
|  | #include <cinttypes> | 
|  | #include <cstdint> | 
|  | #include <memory> | 
|  | #include <string> | 
|  | #include <utility> | 
|  | #include <vector> | 
|  |  | 
|  | namespace llvm { | 
|  |  | 
|  | namespace dsymutil { | 
|  |  | 
|  | using namespace llvm::object; | 
|  |  | 
|  | DebugMapObject::DebugMapObject(StringRef ObjectFilename, | 
|  | sys::TimePoint<std::chrono::seconds> Timestamp, | 
|  | uint8_t Type) | 
|  | : Filename(ObjectFilename), Timestamp(Timestamp), Type(Type) {} | 
|  |  | 
|  | bool DebugMapObject::addSymbol(StringRef Name, Optional<uint64_t> ObjectAddress, | 
|  | uint64_t LinkedAddress, uint32_t Size) { | 
|  | auto InsertResult = Symbols.insert( | 
|  | std::make_pair(Name, SymbolMapping(ObjectAddress, LinkedAddress, Size))); | 
|  |  | 
|  | if (ObjectAddress && InsertResult.second) | 
|  | AddressToMapping[*ObjectAddress] = &*InsertResult.first; | 
|  | return InsertResult.second; | 
|  | } | 
|  |  | 
|  | void DebugMapObject::print(raw_ostream &OS) const { | 
|  | OS << getObjectFilename() << ":\n"; | 
|  | // Sort the symbols in alphabetical order, like llvm-nm (and to get | 
|  | // deterministic output for testing). | 
|  | using Entry = std::pair<StringRef, SymbolMapping>; | 
|  | std::vector<Entry> Entries; | 
|  | Entries.reserve(Symbols.getNumItems()); | 
|  | for (const auto &Sym : make_range(Symbols.begin(), Symbols.end())) | 
|  | Entries.push_back(std::make_pair(Sym.getKey(), Sym.getValue())); | 
|  | llvm::sort(Entries, [](const Entry &LHS, const Entry &RHS) { | 
|  | return LHS.first < RHS.first; | 
|  | }); | 
|  | for (const auto &Sym : Entries) { | 
|  | if (Sym.second.ObjectAddress) | 
|  | OS << format("\t%016" PRIx64, uint64_t(*Sym.second.ObjectAddress)); | 
|  | else | 
|  | OS << "\t????????????????"; | 
|  | OS << format(" => %016" PRIx64 "+0x%x\t%s\n", | 
|  | uint64_t(Sym.second.BinaryAddress), uint32_t(Sym.second.Size), | 
|  | Sym.first.data()); | 
|  | } | 
|  | OS << '\n'; | 
|  | } | 
|  |  | 
|  | #ifndef NDEBUG | 
|  | void DebugMapObject::dump() const { print(errs()); } | 
|  | #endif | 
|  |  | 
|  | DebugMapObject & | 
|  | DebugMap::addDebugMapObject(StringRef ObjectFilePath, | 
|  | sys::TimePoint<std::chrono::seconds> Timestamp, | 
|  | uint8_t Type) { | 
|  | Objects.emplace_back(new DebugMapObject(ObjectFilePath, Timestamp, Type)); | 
|  | return *Objects.back(); | 
|  | } | 
|  |  | 
|  | const DebugMapObject::DebugMapEntry * | 
|  | DebugMapObject::lookupSymbol(StringRef SymbolName) const { | 
|  | StringMap<SymbolMapping>::const_iterator Sym = Symbols.find(SymbolName); | 
|  | if (Sym == Symbols.end()) | 
|  | return nullptr; | 
|  | return &*Sym; | 
|  | } | 
|  |  | 
|  | const DebugMapObject::DebugMapEntry * | 
|  | DebugMapObject::lookupObjectAddress(uint64_t Address) const { | 
|  | auto Mapping = AddressToMapping.find(Address); | 
|  | if (Mapping == AddressToMapping.end()) | 
|  | return nullptr; | 
|  | return Mapping->getSecond(); | 
|  | } | 
|  |  | 
|  | void DebugMap::print(raw_ostream &OS) const { | 
|  | yaml::Output yout(OS, /* Ctxt = */ nullptr, /* WrapColumn = */ 0); | 
|  | yout << const_cast<DebugMap &>(*this); | 
|  | } | 
|  |  | 
|  | #ifndef NDEBUG | 
|  | void DebugMap::dump() const { print(errs()); } | 
|  | #endif | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | struct YAMLContext { | 
|  | StringRef PrependPath; | 
|  | Triple BinaryTriple; | 
|  | }; | 
|  |  | 
|  | } // end anonymous namespace | 
|  |  | 
|  | ErrorOr<std::vector<std::unique_ptr<DebugMap>>> | 
|  | DebugMap::parseYAMLDebugMap(StringRef InputFile, StringRef PrependPath, | 
|  | bool Verbose) { | 
|  | auto ErrOrFile = MemoryBuffer::getFileOrSTDIN(InputFile); | 
|  | if (auto Err = ErrOrFile.getError()) | 
|  | return Err; | 
|  |  | 
|  | YAMLContext Ctxt; | 
|  |  | 
|  | Ctxt.PrependPath = PrependPath; | 
|  |  | 
|  | std::unique_ptr<DebugMap> Res; | 
|  | yaml::Input yin((*ErrOrFile)->getBuffer(), &Ctxt); | 
|  | yin >> Res; | 
|  |  | 
|  | if (auto EC = yin.error()) | 
|  | return EC; | 
|  | std::vector<std::unique_ptr<DebugMap>> Result; | 
|  | Result.push_back(std::move(Res)); | 
|  | return std::move(Result); | 
|  | } | 
|  |  | 
|  | } // end namespace dsymutil | 
|  |  | 
|  | namespace yaml { | 
|  |  | 
|  | // Normalize/Denormalize between YAML and a DebugMapObject. | 
|  | struct MappingTraits<dsymutil::DebugMapObject>::YamlDMO { | 
|  | YamlDMO(IO &io) { Timestamp = 0; } | 
|  | YamlDMO(IO &io, dsymutil::DebugMapObject &Obj); | 
|  | dsymutil::DebugMapObject denormalize(IO &IO); | 
|  |  | 
|  | std::string Filename; | 
|  | int64_t Timestamp; | 
|  | std::vector<dsymutil::DebugMapObject::YAMLSymbolMapping> Entries; | 
|  | }; | 
|  |  | 
|  | void MappingTraits<std::pair<std::string, DebugMapObject::SymbolMapping>>:: | 
|  | mapping(IO &io, std::pair<std::string, DebugMapObject::SymbolMapping> &s) { | 
|  | io.mapRequired("sym", s.first); | 
|  | io.mapOptional("objAddr", s.second.ObjectAddress); | 
|  | io.mapRequired("binAddr", s.second.BinaryAddress); | 
|  | io.mapOptional("size", s.second.Size); | 
|  | } | 
|  |  | 
|  | void MappingTraits<dsymutil::DebugMapObject>::mapping( | 
|  | IO &io, dsymutil::DebugMapObject &DMO) { | 
|  | MappingNormalization<YamlDMO, dsymutil::DebugMapObject> Norm(io, DMO); | 
|  | io.mapRequired("filename", Norm->Filename); | 
|  | io.mapOptional("timestamp", Norm->Timestamp); | 
|  | io.mapRequired("symbols", Norm->Entries); | 
|  | } | 
|  |  | 
|  | void ScalarTraits<Triple>::output(const Triple &val, void *, raw_ostream &out) { | 
|  | out << val.str(); | 
|  | } | 
|  |  | 
|  | StringRef ScalarTraits<Triple>::input(StringRef scalar, void *, Triple &value) { | 
|  | value = Triple(scalar); | 
|  | return StringRef(); | 
|  | } | 
|  |  | 
|  | size_t | 
|  | SequenceTraits<std::vector<std::unique_ptr<dsymutil::DebugMapObject>>>::size( | 
|  | IO &io, std::vector<std::unique_ptr<dsymutil::DebugMapObject>> &seq) { | 
|  | return seq.size(); | 
|  | } | 
|  |  | 
|  | dsymutil::DebugMapObject & | 
|  | SequenceTraits<std::vector<std::unique_ptr<dsymutil::DebugMapObject>>>::element( | 
|  | IO &, std::vector<std::unique_ptr<dsymutil::DebugMapObject>> &seq, | 
|  | size_t index) { | 
|  | if (index >= seq.size()) { | 
|  | seq.resize(index + 1); | 
|  | seq[index].reset(new dsymutil::DebugMapObject); | 
|  | } | 
|  | return *seq[index]; | 
|  | } | 
|  |  | 
|  | void MappingTraits<dsymutil::DebugMap>::mapping(IO &io, | 
|  | dsymutil::DebugMap &DM) { | 
|  | io.mapRequired("triple", DM.BinaryTriple); | 
|  | io.mapOptional("binary-path", DM.BinaryPath); | 
|  | if (void *Ctxt = io.getContext()) | 
|  | reinterpret_cast<YAMLContext *>(Ctxt)->BinaryTriple = DM.BinaryTriple; | 
|  | io.mapOptional("objects", DM.Objects); | 
|  | } | 
|  |  | 
|  | void MappingTraits<std::unique_ptr<dsymutil::DebugMap>>::mapping( | 
|  | IO &io, std::unique_ptr<dsymutil::DebugMap> &DM) { | 
|  | if (!DM) | 
|  | DM.reset(new DebugMap()); | 
|  | io.mapRequired("triple", DM->BinaryTriple); | 
|  | io.mapOptional("binary-path", DM->BinaryPath); | 
|  | if (void *Ctxt = io.getContext()) | 
|  | reinterpret_cast<YAMLContext *>(Ctxt)->BinaryTriple = DM->BinaryTriple; | 
|  | io.mapOptional("objects", DM->Objects); | 
|  | } | 
|  |  | 
|  | MappingTraits<dsymutil::DebugMapObject>::YamlDMO::YamlDMO( | 
|  | IO &io, dsymutil::DebugMapObject &Obj) { | 
|  | Filename = Obj.Filename; | 
|  | Timestamp = sys::toTimeT(Obj.getTimestamp()); | 
|  | Entries.reserve(Obj.Symbols.size()); | 
|  | for (auto &Entry : Obj.Symbols) | 
|  | Entries.push_back(std::make_pair(Entry.getKey(), Entry.getValue())); | 
|  | } | 
|  |  | 
|  | dsymutil::DebugMapObject | 
|  | MappingTraits<dsymutil::DebugMapObject>::YamlDMO::denormalize(IO &IO) { | 
|  | BinaryHolder BinHolder(/* Verbose =*/false); | 
|  | const auto &Ctxt = *reinterpret_cast<YAMLContext *>(IO.getContext()); | 
|  | SmallString<80> Path(Ctxt.PrependPath); | 
|  | StringMap<uint64_t> SymbolAddresses; | 
|  |  | 
|  | sys::path::append(Path, Filename); | 
|  |  | 
|  | auto ObjectEntry = BinHolder.getObjectEntry(Path); | 
|  | if (!ObjectEntry) { | 
|  | auto Err = ObjectEntry.takeError(); | 
|  | WithColor::warning() << "Unable to open " << Path << " " | 
|  | << toString(std::move(Err)) << '\n'; | 
|  | } else { | 
|  | auto Object = ObjectEntry->getObject(Ctxt.BinaryTriple); | 
|  | if (!Object) { | 
|  | auto Err = Object.takeError(); | 
|  | WithColor::warning() << "Unable to open " << Path << " " | 
|  | << toString(std::move(Err)) << '\n'; | 
|  | } else { | 
|  | for (const auto &Sym : Object->symbols()) { | 
|  | uint64_t Address = Sym.getValue(); | 
|  | Expected<StringRef> Name = Sym.getName(); | 
|  | if (!Name || (Sym.getFlags() & | 
|  | (SymbolRef::SF_Absolute | SymbolRef::SF_Common))) { | 
|  | // TODO: Actually report errors helpfully. | 
|  | if (!Name) | 
|  | consumeError(Name.takeError()); | 
|  | continue; | 
|  | } | 
|  | SymbolAddresses[*Name] = Address; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | dsymutil::DebugMapObject Res(Path, sys::toTimePoint(Timestamp), MachO::N_OSO); | 
|  | for (auto &Entry : Entries) { | 
|  | auto &Mapping = Entry.second; | 
|  | Optional<uint64_t> ObjAddress; | 
|  | if (Mapping.ObjectAddress) | 
|  | ObjAddress = *Mapping.ObjectAddress; | 
|  | auto AddressIt = SymbolAddresses.find(Entry.first); | 
|  | if (AddressIt != SymbolAddresses.end()) | 
|  | ObjAddress = AddressIt->getValue(); | 
|  | Res.addSymbol(Entry.first, ObjAddress, Mapping.BinaryAddress, Mapping.Size); | 
|  | } | 
|  | return Res; | 
|  | } | 
|  |  | 
|  | } // end namespace yaml | 
|  | } // end namespace llvm |