110 changes: 84 additions & 26 deletions llvm/lib/ProfileData/Coverage/CoverageMapping.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include "llvm/ADT/SmallBitVector.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Object/BuildID.h"
#include "llvm/ProfileData/Coverage/CoverageMappingReader.h"
#include "llvm/ProfileData/InstrProfReader.h"
#include "llvm/Support/Debug.h"
Expand Down Expand Up @@ -342,46 +343,103 @@ static Error handleMaybeNoDataFoundError(Error E) {
});
}

Error CoverageMapping::loadFromFile(
StringRef Filename, StringRef Arch, StringRef CompilationDir,
IndexedInstrProfReader &ProfileReader, CoverageMapping &Coverage,
bool &DataFound, SmallVectorImpl<object::BuildID> *FoundBinaryIDs) {
auto CovMappingBufOrErr = MemoryBuffer::getFileOrSTDIN(
Filename, /*IsText=*/false, /*RequiresNullTerminator=*/false);
if (std::error_code EC = CovMappingBufOrErr.getError())
return createFileError(Filename, errorCodeToError(EC));
MemoryBufferRef CovMappingBufRef =
CovMappingBufOrErr.get()->getMemBufferRef();
SmallVector<std::unique_ptr<MemoryBuffer>, 4> Buffers;

SmallVector<object::BuildIDRef> BinaryIDs;
auto CoverageReadersOrErr = BinaryCoverageReader::create(
CovMappingBufRef, Arch, Buffers, CompilationDir,
FoundBinaryIDs ? &BinaryIDs : nullptr);
if (Error E = CoverageReadersOrErr.takeError()) {
E = handleMaybeNoDataFoundError(std::move(E));
if (E)
return createFileError(Filename, std::move(E));
return E;
}

SmallVector<std::unique_ptr<CoverageMappingReader>, 4> Readers;
for (auto &Reader : CoverageReadersOrErr.get())
Readers.push_back(std::move(Reader));
if (FoundBinaryIDs && !Readers.empty()) {
llvm::append_range(*FoundBinaryIDs,
llvm::map_range(BinaryIDs, [](object::BuildIDRef BID) {
return object::BuildID(BID);
}));
}
DataFound |= !Readers.empty();
if (Error E = loadFromReaders(Readers, ProfileReader, Coverage))
return createFileError(Filename, std::move(E));
return Error::success();
}

Expected<std::unique_ptr<CoverageMapping>>
CoverageMapping::load(ArrayRef<StringRef> ObjectFilenames,
StringRef ProfileFilename, ArrayRef<StringRef> Arches,
StringRef CompilationDir) {
StringRef CompilationDir,
const object::BuildIDFetcher *BIDFetcher) {
auto ProfileReaderOrErr = IndexedInstrProfReader::create(ProfileFilename);
if (Error E = ProfileReaderOrErr.takeError())
return createFileError(ProfileFilename, std::move(E));
auto ProfileReader = std::move(ProfileReaderOrErr.get());
auto Coverage = std::unique_ptr<CoverageMapping>(new CoverageMapping());
bool DataFound = false;

auto GetArch = [&](size_t Idx) {
if (Arches.empty())
return StringRef();
if (Arches.size() == 1)
return Arches.front();
return Arches[Idx];
};

SmallVector<object::BuildID> FoundBinaryIDs;
for (const auto &File : llvm::enumerate(ObjectFilenames)) {
auto CovMappingBufOrErr = MemoryBuffer::getFileOrSTDIN(
File.value(), /*IsText=*/false, /*RequiresNullTerminator=*/false);
if (std::error_code EC = CovMappingBufOrErr.getError())
return createFileError(File.value(), errorCodeToError(EC));
StringRef Arch = Arches.empty() ? StringRef() : Arches[File.index()];
MemoryBufferRef CovMappingBufRef =
CovMappingBufOrErr.get()->getMemBufferRef();
SmallVector<std::unique_ptr<MemoryBuffer>, 4> Buffers;
auto CoverageReadersOrErr = BinaryCoverageReader::create(
CovMappingBufRef, Arch, Buffers, CompilationDir);
if (Error E = CoverageReadersOrErr.takeError()) {
E = handleMaybeNoDataFoundError(std::move(E));
if (E)
return createFileError(File.value(), std::move(E));
// E == success (originally a no_data_found error).
continue;
if (Error E =
loadFromFile(File.value(), GetArch(File.index()), CompilationDir,
*ProfileReader, *Coverage, DataFound, &FoundBinaryIDs))
return std::move(E);
}

if (BIDFetcher) {
std::vector<object::BuildID> ProfileBinaryIDs;
if (Error E = ProfileReader->readBinaryIds(ProfileBinaryIDs))
return createFileError(ProfileFilename, std::move(E));

SmallVector<object::BuildIDRef> BinaryIDsToFetch;
if (!ProfileBinaryIDs.empty()) {
const auto &Compare = [](object::BuildIDRef A, object::BuildIDRef B) {
return std::lexicographical_compare(A.begin(), A.end(), B.begin(),
B.end());
};
llvm::sort(FoundBinaryIDs, Compare);
std::set_difference(
ProfileBinaryIDs.begin(), ProfileBinaryIDs.end(),
FoundBinaryIDs.begin(), FoundBinaryIDs.end(),
std::inserter(BinaryIDsToFetch, BinaryIDsToFetch.end()), Compare);
}

SmallVector<std::unique_ptr<CoverageMappingReader>, 4> Readers;
for (auto &Reader : CoverageReadersOrErr.get())
Readers.push_back(std::move(Reader));
DataFound |= !Readers.empty();
if (Error E = loadFromReaders(Readers, *ProfileReader, *Coverage))
return createFileError(File.value(), std::move(E));
for (object::BuildIDRef BinaryID : BinaryIDsToFetch) {
std::optional<std::string> PathOpt = BIDFetcher->fetch(BinaryID);
if (!PathOpt)
continue;
std::string Path = std::move(*PathOpt);
StringRef Arch = Arches.size() == 1 ? Arches.front() : StringRef();
if (Error E = loadFromFile(Path, Arch, CompilationDir, *ProfileReader,
*Coverage, DataFound))
return std::move(E);
}
}
// If no readers were created, either no objects were provided or none of them
// had coverage data. Return an error in the latter case.
if (!DataFound && !ObjectFilenames.empty())

if (!DataFound)
return createFileError(
join(ObjectFilenames.begin(), ObjectFilenames.end(), ", "),
make_error<CoverageMapError>(coveragemap_error::no_data_found));
Expand Down
19 changes: 14 additions & 5 deletions llvm/lib/ProfileData/Coverage/CoverageMappingReader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -954,7 +954,8 @@ static Expected<std::vector<SectionRef>> lookupSections(ObjectFile &OF,

static Expected<std::unique_ptr<BinaryCoverageReader>>
loadBinaryFormat(std::unique_ptr<Binary> Bin, StringRef Arch,
StringRef CompilationDir = "") {
StringRef CompilationDir = "",
std::optional<object::BuildIDRef> *BinaryID = nullptr) {
std::unique_ptr<ObjectFile> OF;
if (auto *Universal = dyn_cast<MachOUniversalBinary>(Bin.get())) {
// If we have a universal binary, try to look up the object for the
Expand Down Expand Up @@ -1052,6 +1053,9 @@ loadBinaryFormat(std::unique_ptr<Binary> Bin, StringRef Arch,
FuncRecords = std::move(WritableBuffer);
}

if (BinaryID)
*BinaryID = getBuildID(OF.get());

return BinaryCoverageReader::createCoverageReaderFromBuffer(
CoverageMapping, std::move(FuncRecords), std::move(ProfileNames),
BytesInAddress, Endian, CompilationDir);
Expand All @@ -1074,7 +1078,7 @@ Expected<std::vector<std::unique_ptr<BinaryCoverageReader>>>
BinaryCoverageReader::create(
MemoryBufferRef ObjectBuffer, StringRef Arch,
SmallVectorImpl<std::unique_ptr<MemoryBuffer>> &ObjectFileBuffers,
StringRef CompilationDir) {
StringRef CompilationDir, SmallVectorImpl<object::BuildIDRef> *BinaryIDs) {
std::vector<std::unique_ptr<BinaryCoverageReader>> Readers;

if (ObjectBuffer.getBuffer().startswith(TestingFormatMagic)) {
Expand Down Expand Up @@ -1114,7 +1118,7 @@ BinaryCoverageReader::create(

return BinaryCoverageReader::create(
ArchiveOrErr.get()->getMemoryBufferRef(), Arch, ObjectFileBuffers,
CompilationDir);
CompilationDir, BinaryIDs);
}
}

Expand All @@ -1127,7 +1131,8 @@ BinaryCoverageReader::create(
return ChildBufOrErr.takeError();

auto ChildReadersOrErr = BinaryCoverageReader::create(
ChildBufOrErr.get(), Arch, ObjectFileBuffers, CompilationDir);
ChildBufOrErr.get(), Arch, ObjectFileBuffers, CompilationDir,
BinaryIDs);
if (!ChildReadersOrErr)
return ChildReadersOrErr.takeError();
for (auto &Reader : ChildReadersOrErr.get())
Expand All @@ -1146,10 +1151,14 @@ BinaryCoverageReader::create(
return std::move(Readers);
}

auto ReaderOrErr = loadBinaryFormat(std::move(Bin), Arch, CompilationDir);
std::optional<object::BuildIDRef> BinaryID;
auto ReaderOrErr = loadBinaryFormat(std::move(Bin), Arch, CompilationDir,
BinaryIDs ? &BinaryID : nullptr);
if (!ReaderOrErr)
return ReaderOrErr.takeError();
Readers.push_back(std::move(ReaderOrErr.get()));
if (BinaryID)
BinaryIDs->push_back(*BinaryID);
return std::move(Readers);
}

Expand Down
2 changes: 2 additions & 0 deletions llvm/tools/llvm-cov/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@ add_llvm_tool(llvm-cov
SourceCoverageViewText.cpp
TestingSupport.cpp
)

target_link_libraries(llvm-cov PRIVATE LLVMDebuginfod)
30 changes: 24 additions & 6 deletions llvm/tools/llvm-cov/CodeCoverage.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/Triple.h"
#include "llvm/Debuginfod/BuildIDFetcher.h"
#include "llvm/Debuginfod/Debuginfod.h"
#include "llvm/Debuginfod/HTTPClient.h"
#include "llvm/Object/BuildID.h"
#include "llvm/ProfileData/Coverage/CoverageMapping.h"
#include "llvm/ProfileData/InstrProfReader.h"
#include "llvm/Support/CommandLine.h"
Expand Down Expand Up @@ -179,6 +183,8 @@ class CodeCoverageTool {

/// Allowlist from -name-allowlist to be used for filtering.
std::unique_ptr<SpecialCaseList> NameAllowlist;

std::unique_ptr<object::BuildIDFetcher> BIDFetcher;
};
}

Expand Down Expand Up @@ -435,7 +441,7 @@ std::unique_ptr<CoverageMapping> CodeCoverageTool::load() {
ObjectFilename);
auto CoverageOrErr =
CoverageMapping::load(ObjectFilenames, PGOFilename, CoverageArches,
ViewOpts.CompilationDirectory);
ViewOpts.CompilationDirectory, BIDFetcher.get());
if (Error E = CoverageOrErr.takeError()) {
error("Failed to load coverage: " + toString(std::move(E)));
return nullptr;
Expand Down Expand Up @@ -647,6 +653,14 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
cl::opt<bool> DebugDump("dump", cl::Optional,
cl::desc("Show internal debug dump"));

cl::list<std::string> DebugFileDirectory(
"debug-file-directory",
cl::desc("Directories to search for object files by build ID"));
cl::opt<bool> Debuginfod(
"debuginfod", cl::ZeroOrMore,
cl::desc("Use debuginfod to look up object files from profile"),
cl::init(canUseDebuginfod()));

cl::opt<CoverageViewOptions::OutputFormat> Format(
"format", cl::desc("Output format for line-based coverage reports"),
cl::values(clEnumValN(CoverageViewOptions::OutputFormat::Text, "text",
Expand Down Expand Up @@ -749,12 +763,18 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
auto commandLineParser = [&, this](int argc, const char **argv) -> int {
cl::ParseCommandLineOptions(argc, argv, "LLVM code coverage tool\n");
ViewOpts.Debug = DebugDump;
if (Debuginfod) {
HTTPClient::initialize();
BIDFetcher = std::make_unique<DebuginfodFetcher>(DebugFileDirectory);
} else {
BIDFetcher = std::make_unique<object::BuildIDFetcher>(DebugFileDirectory);
}

if (!CovFilename.empty())
ObjectFilenames.emplace_back(CovFilename);
for (const std::string &Filename : CovFilenames)
ObjectFilenames.emplace_back(Filename);
if (ObjectFilenames.empty()) {
if (ObjectFilenames.empty() && !Debuginfod && DebugFileDirectory.empty()) {
errs() << "No filenames specified!\n";
::exit(1);
}
Expand Down Expand Up @@ -867,10 +887,8 @@ int CodeCoverageTool::run(Command Cmd, int argc, const char **argv) {
}
CoverageArches.emplace_back(Arch);
}
if (CoverageArches.size() == 1)
CoverageArches.insert(CoverageArches.end(), ObjectFilenames.size() - 1,
CoverageArches[0]);
if (CoverageArches.size() != ObjectFilenames.size()) {
if (CoverageArches.size() != 1 &&
CoverageArches.size() != ObjectFilenames.size()) {
error("Number of architectures doesn't match the number of objects");
return 1;
}
Expand Down
4 changes: 1 addition & 3 deletions llvm/tools/llvm-objdump/llvm-objdump.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3198,9 +3198,7 @@ int main(int argc, char **argv) {

// Initialize debuginfod.
const bool ShouldUseDebuginfodByDefault =
InputArgs.hasArg(OBJDUMP_build_id) ||
(HTTPClient::isAvailable() &&
!ExitOnErr(getDefaultDebuginfodUrls()).empty());
InputArgs.hasArg(OBJDUMP_build_id) || canUseDebuginfod();
std::vector<std::string> DebugFileDirectories =
InputArgs.getAllArgValues(OBJDUMP_debug_file_directory);
if (InputArgs.hasFlag(OBJDUMP_debuginfod, OBJDUMP_no_debuginfod,
Expand Down
8 changes: 1 addition & 7 deletions llvm/tools/llvm-symbolizer/llvm-symbolizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -443,13 +443,7 @@ int main(int argc, char **argv) {

LLVMSymbolizer Symbolizer(Opts);

// A debuginfod lookup could succeed if a HTTP client is available and at
// least one backing URL is configured.
bool ShouldUseDebuginfodByDefault =
HTTPClient::isAvailable() &&
!ExitOnErr(getDefaultDebuginfodUrls()).empty();
if (Args.hasFlag(OPT_debuginfod, OPT_no_debuginfod,
ShouldUseDebuginfodByDefault))
if (Args.hasFlag(OPT_debuginfod, OPT_no_debuginfod, canUseDebuginfod()))
enableDebuginfod(Symbolizer, Args);

if (Args.hasArg(OPT_filter_markup)) {
Expand Down