blob: 88f66cf2093be2e379d889e05d7f2a69bb03abae [file] [log] [blame]
//===-- 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");
}