| //===-- LVReader.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 |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // This implements the LVReader class. |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "llvm/DebugInfo/LogicalView/Core/LVReader.h" |
| #include "llvm/DebugInfo/LogicalView/Core/LVLine.h" |
| #include "llvm/DebugInfo/LogicalView/Core/LVScope.h" |
| #include "llvm/Support/FileSystem.h" |
| #include "llvm/Support/FormatAdapters.h" |
| #include "llvm/Support/FormatVariadic.h" |
| #include <tuple> |
| |
| using namespace llvm; |
| using namespace llvm::logicalview; |
| |
| #define DEBUG_TYPE "Reader" |
| |
| // Detect elements that are inserted more than once at different scopes, |
| // causing a crash on the reader destruction, as the element is already |
| // deleted from other scope. Helper for CodeView reader. |
| bool checkIntegrityScopesTree(LVScope *Root) { |
| using LVDuplicateEntry = std::tuple<LVElement *, LVScope *, LVScope *>; |
| using LVDuplicate = std::vector<LVDuplicateEntry>; |
| LVDuplicate Duplicate; |
| |
| using LVIntegrity = std::map<LVElement *, LVScope *>; |
| LVIntegrity Integrity; |
| |
| // Add the given element to the integrity map. |
| auto AddElement = [&](LVElement *Element, LVScope *Scope) { |
| LVIntegrity::iterator Iter = Integrity.find(Element); |
| if (Iter == Integrity.end()) |
| Integrity.emplace(Element, Scope); |
| else |
| // We found a duplicate. |
| Duplicate.emplace_back(Element, Scope, Iter->second); |
| }; |
| |
| // Recursively add all the elements in the scope. |
| std::function<void(LVScope * Parent)> TraverseScope = [&](LVScope *Parent) { |
| auto Traverse = [&](const auto *Set) { |
| if (Set) |
| for (const auto &Entry : *Set) |
| AddElement(Entry, Parent); |
| }; |
| if (const LVScopes *Scopes = Parent->getScopes()) { |
| for (LVScope *Scope : *Scopes) { |
| AddElement(Scope, Parent); |
| TraverseScope(Scope); |
| } |
| } |
| Traverse(Parent->getSymbols()); |
| Traverse(Parent->getTypes()); |
| Traverse(Parent->getLines()); |
| }; |
| |
| // Start traversing the scopes root and print any duplicates. |
| TraverseScope(Root); |
| bool PassIntegrity = true; |
| if (Duplicate.size()) { |
| std::stable_sort(begin(Duplicate), end(Duplicate), |
| [](const auto &l, const auto &r) { |
| return std::get<0>(l)->getID() < std::get<0>(r)->getID(); |
| }); |
| |
| auto PrintIndex = [](unsigned Index) { |
| if (Index) |
| dbgs() << format("%8d: ", Index); |
| else |
| dbgs() << format("%8c: ", ' '); |
| }; |
| auto PrintElement = [&](LVElement *Element, unsigned Index = 0) { |
| PrintIndex(Index); |
| std::string ElementName(Element->getName()); |
| dbgs() << format("%15s ID=0x%08x '%s'\n", Element->kind(), |
| Element->getID(), ElementName.c_str()); |
| }; |
| |
| std::string RootName(Root->getName()); |
| dbgs() << formatv("{0}\n", fmt_repeat('=', 72)); |
| dbgs() << format("Root: '%s'\nDuplicated elements: %d\n", RootName.c_str(), |
| Duplicate.size()); |
| dbgs() << formatv("{0}\n", fmt_repeat('=', 72)); |
| |
| unsigned Index = 0; |
| for (const LVDuplicateEntry &Entry : Duplicate) { |
| LVElement *Element; |
| LVScope *First; |
| LVScope *Second; |
| std::tie(Element, First, Second) = Entry; |
| dbgs() << formatv("\n{0}\n", fmt_repeat('-', 72)); |
| PrintElement(Element, ++Index); |
| PrintElement(First); |
| PrintElement(Second); |
| dbgs() << formatv("{0}\n", fmt_repeat('-', 72)); |
| } |
| PassIntegrity = false; |
| } |
| return PassIntegrity; |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Class to represent a split context. |
| //===----------------------------------------------------------------------===// |
| Error LVSplitContext::createSplitFolder(StringRef Where) { |
| // The 'location' will represent the root directory for the output created |
| // by the context. It will contain the different CUs files, that will be |
| // extracted from a single ELF. |
| Location = std::string(Where); |
| |
| // Add a trailing slash, if there is none. |
| size_t Pos = Location.find_last_of('/'); |
| if (Location.length() != Pos + 1) |
| Location.append("/"); |
| |
| // Make sure the new directory exists, creating it if necessary. |
| if (std::error_code EC = llvm::sys::fs::create_directories(Location)) |
| return createStringError(EC, "Error: could not create directory %s", |
| Location.c_str()); |
| |
| return Error::success(); |
| } |
| |
| std::error_code LVSplitContext::open(std::string ContextName, |
| std::string Extension, raw_ostream &OS) { |
| assert(OutputFile == nullptr && "OutputFile already set."); |
| |
| // Transforms '/', '\', '.', ':' into '_'. |
| std::string Name(flattenedFilePath(ContextName)); |
| Name.append(Extension); |
| // Add the split context location folder name. |
| if (!Location.empty()) |
| Name.insert(0, Location); |
| |
| std::error_code EC; |
| OutputFile = std::make_unique<ToolOutputFile>(Name, EC, sys::fs::OF_None); |
| if (EC) |
| return EC; |
| |
| // Don't remove output file. |
| OutputFile->keep(); |
| return std::error_code(); |
| } |
| |
| LVReader *CurrentReader = nullptr; |
| LVReader &LVReader::getInstance() { |
| if (CurrentReader) |
| return *CurrentReader; |
| outs() << "Invalid instance reader.\n"; |
| llvm_unreachable("Invalid instance reader."); |
| } |
| void LVReader::setInstance(LVReader *Reader) { CurrentReader = Reader; } |
| |
| Error LVReader::createSplitFolder() { |
| if (OutputSplit) { |
| // If the '--output=split' was specified, but no '--split-folder' |
| // option, use the input file as base for the split location. |
| if (options().getOutputFolder().empty()) |
| options().setOutputFolder(getFilename().str() + "_cus"); |
| |
| SmallString<128> SplitFolder; |
| SplitFolder = options().getOutputFolder(); |
| sys::fs::make_absolute(SplitFolder); |
| |
| // Return error if unable to create a split context location. |
| if (Error Err = SplitContext.createSplitFolder(SplitFolder)) |
| return Err; |
| |
| OS << "\nSplit View Location: '" << SplitContext.getLocation() << "'\n"; |
| } |
| |
| return Error::success(); |
| } |
| |
| // Get the filename for given object. |
| StringRef LVReader::getFilename(LVObject *Object, size_t Index) const { |
| if (CompileUnits.size()) { |
| // Get Compile Unit for the given object. |
| LVCompileUnits::const_iterator Iter = |
| std::prev(CompileUnits.lower_bound(Object->getOffset())); |
| if (Iter != CompileUnits.end()) |
| return Iter->second->getFilename(Index); |
| } |
| |
| return CompileUnit ? CompileUnit->getFilename(Index) : StringRef(); |
| } |
| |
| // The Reader is the module that creates the logical view using the debug |
| // information contained in the binary file specified in the command line. |
| // This is the main entry point for the Reader and performs the following |
| // steps: |
| // - Process any patterns collected from the '--select' options. |
| // - For each compile unit in the debug information: |
| // * Create the logical elements (scopes, symbols, types, lines). |
| // * Collect debug ranges and debug locations. |
| // * Move the collected logical lines to their associated scopes. |
| // - Once all the compile units have been processed, traverse the scopes |
| // tree in order to: |
| // * Calculate symbol coverage. |
| // * Detect invalid ranges and locations. |
| // * "resolve" the logical elements. During this pass, the names and |
| // file information are updated, to reflect any dependency with other |
| // logical elements. |
| Error LVReader::doLoad() { |
| // Set current Reader instance. |
| setInstance(this); |
| |
| // Before any scopes creation, process any pattern specified by the |
| // --select and --select-offsets options. |
| patterns().addGenericPatterns(options().Select.Generic); |
| patterns().addOffsetPatterns(options().Select.Offsets); |
| |
| // Add any specific element printing requests based on the element kind. |
| patterns().addRequest(options().Select.Elements); |
| patterns().addRequest(options().Select.Lines); |
| patterns().addRequest(options().Select.Scopes); |
| patterns().addRequest(options().Select.Symbols); |
| patterns().addRequest(options().Select.Types); |
| |
| // Once we have processed the requests for any particular kind of elements, |
| // we need to update the report options, in order to have a default value. |
| patterns().updateReportOptions(); |
| |
| // Delegate the scope tree creation to the specific reader. |
| if (Error Err = createScopes()) |
| return Err; |
| |
| if (options().getInternalIntegrity() && !checkIntegrityScopesTree(Root)) |
| return llvm::make_error<StringError>("Duplicated elements in Scopes Tree", |
| inconvertibleErrorCode()); |
| |
| // Calculate symbol coverage and detect invalid debug locations and ranges. |
| Root->processRangeInformation(); |
| |
| // As the elements can depend on elements from a different compile unit, |
| // information such as name and file/line source information needs to be |
| // updated. |
| Root->resolveElements(); |
| |
| sortScopes(); |
| return Error::success(); |
| } |
| |
| // Default handler for a generic reader. |
| Error LVReader::doPrint() { |
| // Set current Reader instance. |
| setInstance(this); |
| |
| // Check for any '--report' request. |
| if (options().getReportExecute()) { |
| // Requested details. |
| if (options().getReportList()) |
| if (Error Err = printMatchedElements(/*UseMatchedElements=*/true)) |
| return Err; |
| // Requested only children. |
| if (options().getReportChildren() && !options().getReportParents()) |
| if (Error Err = printMatchedElements(/*UseMatchedElements=*/false)) |
| return Err; |
| // Requested (parents) or (parents and children). |
| if (options().getReportParents() || options().getReportView()) |
| if (Error Err = printScopes()) |
| return Err; |
| |
| return Error::success(); |
| } |
| |
| return printScopes(); |
| } |
| |
| Error LVReader::printScopes() { |
| if (bool DoPrint = |
| (options().getPrintExecute() || options().getComparePrint())) { |
| if (Error Err = createSplitFolder()) |
| return Err; |
| |
| // Start printing from the root. |
| bool DoMatch = options().getSelectGenericPattern() || |
| options().getSelectGenericKind() || |
| options().getSelectOffsetPattern(); |
| return Root->doPrint(OutputSplit, DoMatch, DoPrint, OS); |
| } |
| |
| return Error::success(); |
| } |
| |
| Error LVReader::printMatchedElements(bool UseMatchedElements) { |
| if (Error Err = createSplitFolder()) |
| return Err; |
| |
| return Root->doPrintMatches(OutputSplit, OS, UseMatchedElements); |
| } |
| |
| void LVReader::print(raw_ostream &OS) const { |
| OS << "LVReader\n"; |
| LLVM_DEBUG(dbgs() << "PrintReader\n"); |
| } |