summaryrefslogtreecommitdiff
path: root/bolt/lib/Core/BinaryContext.cpp
diff options
context:
space:
mode:
authorRafael Auler <rafaelauler@fb.com>2024-02-06 11:18:23 -0800
committerRafael Auler <rafaelauler@fb.com>2024-02-09 19:12:23 -0800
commitc1781b770d1400b62b3c324157a7895c1b9bdaaa (patch)
tree7ad6ba005f52a88f4670d30d28cfe2018a5f7593 /bolt/lib/Core/BinaryContext.cpp
parent2df42fe0efd92a89ae191a598e2727c7b63de80b (diff)
[BOLT][NFC] Refactor logging and error handlingusers/rafaelauler/bolt-logging-pr
Make core BOLT functionality more friendly to being used as a library instead of in our standalone driver llvm-bolt. To accomplish this, we augment BinaryContext with journaling streams that are to be used by most BOLT code whenever something needs to be logged to the screen. Users of the library can decide if logs should be printed to a file, no file or to the screen, as before. To illustrate this, this patch adds a new option --log-file that allows the user to redirect BOLT logging to a file on disk or completely hide it by using --log-file=/dev/null. Future BOLT code should now use BinaryContext::outs() for printing important messages instead of llvm::outs(). A new test log.test enforces this by verifying that no strings are print to screen once the --log-file option is used. We also refactor old error handling code that would directly call exit(1) inside BOLT libraries whenever a serious problem occurred. To overcome this, we add a new class BOLTError and auxiliary functions createFatalBOLTError() and createNonFatalBOLTError() that allows BOLT libs to bubble up the problem to the caller by using the Error class as a return type (or Expected). To easily handle problems as before (by quitting with exit(1)), callers can now use BinaryContext::logBOLTErrorsAndQuitOnFatal(Error) whenever code needs to deal with BOLT errors. To test this, we have fatal.s that checks we are correctly quitting and printing a fatal error to the screen. Because this is a significant change by itself, not all code was yet ported. Code from Profiler libs (DataAggregator and friends) still print errors directly to screen.
Diffstat (limited to 'bolt/lib/Core/BinaryContext.cpp')
-rw-r--r--bolt/lib/Core/BinaryContext.cpp220
1 files changed, 135 insertions, 85 deletions
diff --git a/bolt/lib/Core/BinaryContext.cpp b/bolt/lib/Core/BinaryContext.cpp
index df835d287680..d544ece13a83 100644
--- a/bolt/lib/Core/BinaryContext.cpp
+++ b/bolt/lib/Core/BinaryContext.cpp
@@ -83,6 +83,46 @@ cl::opt<std::string> CompDirOverride(
namespace llvm {
namespace bolt {
+char BOLTError::ID = 0;
+
+BOLTError::BOLTError(bool IsFatal, const Twine &S)
+ : IsFatal(IsFatal), Msg(S.str()) {}
+
+void BOLTError::log(raw_ostream &OS) const {
+ if (IsFatal)
+ OS << "FATAL ";
+ StringRef ErrMsg = StringRef(Msg);
+ // Prepend our error prefix if it is missing
+ if (ErrMsg.empty()) {
+ OS << "BOLT-ERROR\n";
+ } else {
+ if (!ErrMsg.starts_with("BOLT-ERROR"))
+ OS << "BOLT-ERROR: ";
+ OS << ErrMsg << "\n";
+ }
+}
+
+std::error_code BOLTError::convertToErrorCode() const {
+ return inconvertibleErrorCode();
+}
+
+Error createNonFatalBOLTError(const Twine &S) {
+ return make_error<BOLTError>(/*IsFatal*/ false, S);
+}
+
+Error createFatalBOLTError(const Twine &S) {
+ return make_error<BOLTError>(/*IsFatal*/ true, S);
+}
+
+void BinaryContext::logBOLTErrorsAndQuitOnFatal(Error E) {
+ handleAllErrors(Error(std::move(E)), [&](const BOLTError &E) {
+ if (!E.getMessage().empty())
+ E.log(this->errs());
+ if (E.isFatal())
+ exit(1);
+ });
+}
+
BinaryContext::BinaryContext(std::unique_ptr<MCContext> Ctx,
std::unique_ptr<DWARFContext> DwCtx,
std::unique_ptr<Triple> TheTriple,
@@ -96,13 +136,15 @@ BinaryContext::BinaryContext(std::unique_ptr<MCContext> Ctx,
std::unique_ptr<const MCInstrAnalysis> MIA,
std::unique_ptr<MCPlusBuilder> MIB,
std::unique_ptr<const MCRegisterInfo> MRI,
- std::unique_ptr<MCDisassembler> DisAsm)
+ std::unique_ptr<MCDisassembler> DisAsm,
+ JournalingStreams Logger)
: Ctx(std::move(Ctx)), DwCtx(std::move(DwCtx)),
TheTriple(std::move(TheTriple)), TheTarget(TheTarget),
TripleName(TripleName), MCE(std::move(MCE)), MOFI(std::move(MOFI)),
AsmInfo(std::move(AsmInfo)), MII(std::move(MII)), STI(std::move(STI)),
InstPrinter(std::move(InstPrinter)), MIA(std::move(MIA)),
- MIB(std::move(MIB)), MRI(std::move(MRI)), DisAsm(std::move(DisAsm)) {
+ MIB(std::move(MIB)), MRI(std::move(MRI)), DisAsm(std::move(DisAsm)),
+ Logger(Logger) {
Relocation::Arch = this->TheTriple->getArch();
RegularPageSize = isAArch64() ? RegularPageSizeAArch64 : RegularPageSizeX86;
PageAlign = opts::NoHugePages ? RegularPageSize : HugePageSize;
@@ -122,7 +164,8 @@ BinaryContext::~BinaryContext() {
/// triple \p TripleName.
Expected<std::unique_ptr<BinaryContext>>
BinaryContext::createBinaryContext(const ObjectFile *File, bool IsPIC,
- std::unique_ptr<DWARFContext> DwCtx) {
+ std::unique_ptr<DWARFContext> DwCtx,
+ JournalingStreams Logger) {
StringRef ArchName = "";
std::string FeaturesStr = "";
switch (File->getArch()) {
@@ -241,17 +284,12 @@ BinaryContext::createBinaryContext(const ObjectFile *File, bool IsPIC,
std::unique_ptr<MCCodeEmitter> MCE(
TheTarget->createMCCodeEmitter(*MII, *Ctx));
- // Make sure we don't miss any output on core dumps.
- outs().SetUnbuffered();
- errs().SetUnbuffered();
- dbgs().SetUnbuffered();
-
auto BC = std::make_unique<BinaryContext>(
std::move(Ctx), std::move(DwCtx), std::move(TheTriple), TheTarget,
std::string(TripleName), std::move(MCE), std::move(MOFI),
std::move(AsmInfo), std::move(MII), std::move(STI),
std::move(InstructionPrinter), std::move(MIA), nullptr, std::move(MRI),
- std::move(DisAsm));
+ std::move(DisAsm), Logger);
BC->LSDAEncoding = LSDAEncoding;
@@ -304,9 +342,9 @@ bool BinaryContext::validateObjectNesting() const {
Itr->second->containsRange(Next->second->getAddress(),
Next->second->getSize())) {
if (Next->second->Parent != Itr->second) {
- errs() << "BOLT-WARNING: object nesting incorrect for:\n"
- << "BOLT-WARNING: " << *Itr->second << "\n"
- << "BOLT-WARNING: " << *Next->second << "\n";
+ this->errs() << "BOLT-WARNING: object nesting incorrect for:\n"
+ << "BOLT-WARNING: " << *Itr->second << "\n"
+ << "BOLT-WARNING: " << *Next->second << "\n";
Valid = false;
}
++Next;
@@ -323,14 +361,16 @@ bool BinaryContext::validateHoles() const {
uint64_t RelAddr = Rel.Offset + Section.getAddress();
const BinaryData *BD = getBinaryDataContainingAddress(RelAddr);
if (!BD) {
- errs() << "BOLT-WARNING: no BinaryData found for relocation at address"
- << " 0x" << Twine::utohexstr(RelAddr) << " in "
- << Section.getName() << "\n";
+ this->errs()
+ << "BOLT-WARNING: no BinaryData found for relocation at address"
+ << " 0x" << Twine::utohexstr(RelAddr) << " in " << Section.getName()
+ << "\n";
Valid = false;
} else if (!BD->getAtomicRoot()) {
- errs() << "BOLT-WARNING: no atomic BinaryData found for relocation at "
- << "address 0x" << Twine::utohexstr(RelAddr) << " in "
- << Section.getName() << "\n";
+ this->errs()
+ << "BOLT-WARNING: no atomic BinaryData found for relocation at "
+ << "address 0x" << Twine::utohexstr(RelAddr) << " in "
+ << Section.getName() << "\n";
Valid = false;
}
}
@@ -438,8 +478,9 @@ BinaryContext::handleAddressRef(uint64_t Address, BinaryFunction &BF,
// The address could potentially escape. Mark it as another entry
// point into the function.
if (opts::Verbosity >= 1) {
- outs() << "BOLT-INFO: potentially escaped address 0x"
- << Twine::utohexstr(Address) << " in function " << BF << '\n';
+ this->outs() << "BOLT-INFO: potentially escaped address 0x"
+ << Twine::utohexstr(Address) << " in function " << BF
+ << '\n';
}
BF.HasInternalLabelReference = true;
return std::make_pair(
@@ -482,9 +523,9 @@ MemoryContentsType BinaryContext::analyzeMemoryAt(uint64_t Address,
// internal function addresses to escape the function scope - we
// consider it a tail call.
if (opts::Verbosity > 1) {
- errs() << "BOLT-WARNING: no section for address 0x"
- << Twine::utohexstr(Address) << " referenced from function " << BF
- << '\n';
+ this->errs() << "BOLT-WARNING: no section for address 0x"
+ << Twine::utohexstr(Address) << " referenced from function "
+ << BF << '\n';
}
return MemoryContentsType::UNKNOWN;
}
@@ -730,7 +771,7 @@ void BinaryContext::skipMarkedFragments() {
assert(FragmentsToSkip.count(BF) &&
"internal error in traversing function fragments");
if (opts::Verbosity >= 1)
- errs() << "BOLT-WARNING: Ignoring " << BF->getPrintName() << '\n';
+ this->errs() << "BOLT-WARNING: Ignoring " << BF->getPrintName() << '\n';
BF->setSimple(false);
BF->setHasIndirectTargetToSplitFragment(true);
@@ -738,9 +779,9 @@ void BinaryContext::skipMarkedFragments() {
llvm::for_each(BF->ParentFragments, addToWorklist);
}
if (!FragmentsToSkip.empty())
- errs() << "BOLT-WARNING: skipped " << FragmentsToSkip.size() << " function"
- << (FragmentsToSkip.size() == 1 ? "" : "s")
- << " due to cold fragments\n";
+ this->errs() << "BOLT-WARNING: skipped " << FragmentsToSkip.size()
+ << " function" << (FragmentsToSkip.size() == 1 ? "" : "s")
+ << " due to cold fragments\n";
}
MCSymbol *BinaryContext::getOrCreateGlobalSymbol(uint64_t Address, Twine Prefix,
@@ -791,10 +832,10 @@ BinaryContext::getOrCreateJumpTable(BinaryFunction &Function, uint64_t Address,
// Duplicate the entry for the parent function for easy access
JT->Parents.push_back(&Function);
if (opts::Verbosity > 2) {
- outs() << "BOLT-INFO: Multiple fragments access same jump table: "
- << JT->Parents[0]->getPrintName() << "; "
- << Function.getPrintName() << "\n";
- JT->print(outs());
+ this->outs() << "BOLT-INFO: Multiple fragments access same jump table: "
+ << JT->Parents[0]->getPrintName() << "; "
+ << Function.getPrintName() << "\n";
+ JT->print(this->outs());
}
Function.JumpTables.emplace(Address, JT);
JT->Parents[0]->setHasIndirectTargetToSplitFragment(true);
@@ -832,7 +873,7 @@ BinaryContext::getOrCreateJumpTable(BinaryFunction &Function, uint64_t Address,
*getSectionForAddress(Address));
JT->Parents.push_back(&Function);
if (opts::Verbosity > 2)
- JT->print(outs());
+ JT->print(this->outs());
JumpTables.emplace(Address, JT);
// Duplicate the entry for the parent function for easy access.
@@ -961,12 +1002,13 @@ bool BinaryContext::hasValidCodePadding(const BinaryFunction &BF) {
return true;
if (opts::Verbosity >= 1) {
- errs() << "BOLT-WARNING: bad padding at address 0x"
- << Twine::utohexstr(BF.getAddress() + BF.getSize())
- << " starting at offset " << (Offset - BF.getSize())
- << " in function " << BF << '\n'
- << FunctionData->slice(BF.getSize(), BF.getMaxSize() - BF.getSize())
- << '\n';
+ this->errs() << "BOLT-WARNING: bad padding at address 0x"
+ << Twine::utohexstr(BF.getAddress() + BF.getSize())
+ << " starting at offset " << (Offset - BF.getSize())
+ << " in function " << BF << '\n'
+ << FunctionData->slice(BF.getSize(),
+ BF.getMaxSize() - BF.getSize())
+ << '\n';
}
return false;
@@ -981,8 +1023,8 @@ void BinaryContext::adjustCodePadding() {
if (!hasValidCodePadding(BF)) {
if (HasRelocations) {
if (opts::Verbosity >= 1) {
- outs() << "BOLT-INFO: function " << BF
- << " has invalid padding. Ignoring the function.\n";
+ this->outs() << "BOLT-INFO: function " << BF
+ << " has invalid padding. Ignoring the function.\n";
}
BF.setIgnored();
} else {
@@ -1130,8 +1172,8 @@ void BinaryContext::generateSymbolHashes() {
// (i.e. all zeros or a "hole")
if (!isPadding(BD)) {
if (opts::Verbosity) {
- errs() << "BOLT-WARNING: collision detected when hashing " << BD
- << " with new name (" << NewName << "), skipping.\n";
+ this->errs() << "BOLT-WARNING: collision detected when hashing " << BD
+ << " with new name (" << NewName << "), skipping.\n";
}
++NumCollisions;
}
@@ -1141,11 +1183,11 @@ void BinaryContext::generateSymbolHashes() {
GlobalSymbols[NewName] = &BD;
}
if (NumCollisions) {
- errs() << "BOLT-WARNING: " << NumCollisions
- << " collisions detected while hashing binary objects";
+ this->errs() << "BOLT-WARNING: " << NumCollisions
+ << " collisions detected while hashing binary objects";
if (!opts::Verbosity)
- errs() << ". Use -v=1 to see the list.";
- errs() << '\n';
+ this->errs() << ". Use -v=1 to see the list.";
+ this->errs() << '\n';
}
}
@@ -1161,8 +1203,8 @@ bool BinaryContext::registerFragment(BinaryFunction &TargetFunction,
Function.setSimple(false);
}
if (opts::Verbosity >= 1) {
- outs() << "BOLT-INFO: marking " << TargetFunction << " as a fragment of "
- << Function << '\n';
+ this->outs() << "BOLT-INFO: marking " << TargetFunction
+ << " as a fragment of " << Function << '\n';
}
return true;
}
@@ -1276,10 +1318,11 @@ void BinaryContext::processInterproceduralReferences() {
if (TargetFunction) {
if (TargetFunction->isFragment() &&
!TargetFunction->isChildOf(Function)) {
- errs() << "BOLT-WARNING: interprocedural reference between unrelated "
- "fragments: "
- << Function.getPrintName() << " and "
- << TargetFunction->getPrintName() << '\n';
+ this->errs()
+ << "BOLT-WARNING: interprocedural reference between unrelated "
+ "fragments: "
+ << Function.getPrintName() << " and "
+ << TargetFunction->getPrintName() << '\n';
}
if (uint64_t Offset = Address - TargetFunction->getAddress())
TargetFunction->addEntryPointAtOffset(Offset);
@@ -1305,9 +1348,10 @@ void BinaryContext::processInterproceduralReferences() {
continue;
if (opts::processAllFunctions()) {
- errs() << "BOLT-ERROR: cannot process binaries with unmarked "
- << "object in code at address 0x" << Twine::utohexstr(Address)
- << " belonging to section " << SectionName << " in current mode\n";
+ this->errs() << "BOLT-ERROR: cannot process binaries with unmarked "
+ << "object in code at address 0x"
+ << Twine::utohexstr(Address) << " belonging to section "
+ << SectionName << " in current mode\n";
exit(1);
}
@@ -1317,9 +1361,10 @@ void BinaryContext::processInterproceduralReferences() {
// We are not going to overwrite non-simple functions, but for simple
// ones - adjust the padding size.
if (TargetFunction && TargetFunction->isSimple()) {
- errs() << "BOLT-WARNING: function " << *TargetFunction
- << " has an object detected in a padding region at address 0x"
- << Twine::utohexstr(Address) << '\n';
+ this->errs()
+ << "BOLT-WARNING: function " << *TargetFunction
+ << " has an object detected in a padding region at address 0x"
+ << Twine::utohexstr(Address) << '\n';
TargetFunction->setMaxSize(TargetFunction->getSize());
}
}
@@ -1336,7 +1381,8 @@ void BinaryContext::postProcessSymbolTable() {
BD->getName().starts_with("DATAat")) &&
!BD->getParent() && !BD->getSize() && !BD->isAbsolute() &&
BD->getSection()) {
- errs() << "BOLT-WARNING: zero-sized top level symbol: " << *BD << "\n";
+ this->errs() << "BOLT-WARNING: zero-sized top level symbol: " << *BD
+ << "\n";
Valid = false;
}
}
@@ -1592,17 +1638,18 @@ void BinaryContext::preprocessDWODebugInfo() {
DWARFUnit *DWOCU =
DwarfUnit->getNonSkeletonUnitDIE(false, AbsolutePath).getDwarfUnit();
if (!DWOCU->isDWOUnit()) {
- outs() << "BOLT-WARNING: Debug Fission: DWO debug information for "
- << DWOName
- << " was not retrieved and won't be updated. Please check "
- "relative path.\n";
+ this->outs()
+ << "BOLT-WARNING: Debug Fission: DWO debug information for "
+ << DWOName
+ << " was not retrieved and won't be updated. Please check "
+ "relative path.\n";
continue;
}
DWOCUs[*DWOId] = DWOCU;
}
}
if (!DWOCUs.empty())
- outs() << "BOLT-INFO: processing split DWARF\n";
+ this->outs() << "BOLT-INFO: processing split DWARF\n";
}
void BinaryContext::preprocessDebugInfo() {
@@ -1663,8 +1710,8 @@ void BinaryContext::preprocessDebugInfo() {
}
if (opts::Verbosity >= 1) {
- outs() << "BOLT-INFO: " << ProcessedCUs.size() << " out of "
- << DwCtx->getNumCompileUnits() << " CUs will be updated\n";
+ this->outs() << "BOLT-INFO: " << ProcessedCUs.size() << " out of "
+ << DwCtx->getNumCompileUnits() << " CUs will be updated\n";
}
preprocessDWODebugInfo();
@@ -2245,23 +2292,26 @@ BinaryFunction *BinaryContext::getFunctionForSymbol(const MCSymbol *Symbol,
return BF;
}
-void BinaryContext::exitWithBugReport(StringRef Message,
- const BinaryFunction &Function) const {
- errs() << "=======================================\n";
- errs() << "BOLT is unable to proceed because it couldn't properly understand "
- "this function.\n";
- errs() << "If you are running the most recent version of BOLT, you may "
- "want to "
- "report this and paste this dump.\nPlease check that there is no "
- "sensitive contents being shared in this dump.\n";
- errs() << "\nOffending function: " << Function.getPrintName() << "\n\n";
- ScopedPrinter SP(errs());
+std::string
+BinaryContext::generateBugReportMessage(StringRef Message,
+ const BinaryFunction &Function) const {
+ std::string Msg;
+ raw_string_ostream SS(Msg);
+ SS << "=======================================\n";
+ SS << "BOLT is unable to proceed because it couldn't properly understand "
+ "this function.\n";
+ SS << "If you are running the most recent version of BOLT, you may "
+ "want to "
+ "report this and paste this dump.\nPlease check that there is no "
+ "sensitive contents being shared in this dump.\n";
+ SS << "\nOffending function: " << Function.getPrintName() << "\n\n";
+ ScopedPrinter SP(SS);
SP.printBinaryBlock("Function contents", *Function.getData());
- errs() << "\n";
- Function.dump();
- errs() << "ERROR: " << Message;
- errs() << "\n=======================================\n";
- exit(1);
+ SS << "\n";
+ const_cast<BinaryFunction &>(Function).print(SS, "");
+ SS << "ERROR: " << Message;
+ SS << "\n=======================================\n";
+ return Msg;
}
BinaryFunction *
@@ -2399,9 +2449,9 @@ bool BinaryContext::validateInstructionEncoding(
auto OutputSequence = ArrayRef<uint8_t>((uint8_t *)Code.data(), Code.size());
if (InputSequence != OutputSequence) {
if (opts::Verbosity > 1) {
- errs() << "BOLT-WARNING: mismatched encoding detected\n"
- << " input: " << InputSequence << '\n'
- << " output: " << OutputSequence << '\n';
+ this->errs() << "BOLT-WARNING: mismatched encoding detected\n"
+ << " input: " << InputSequence << '\n'
+ << " output: " << OutputSequence << '\n';
}
return false;
}