Path: blob/main/contrib/llvm-project/lld/Common/ErrorHandler.cpp
34879 views
//===- ErrorHandler.cpp ---------------------------------------------------===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//6//===----------------------------------------------------------------------===//78#include "lld/Common/ErrorHandler.h"910#include "llvm/Support/Parallel.h"1112#include "lld/Common/CommonLinkerContext.h"13#include "llvm/ADT/Twine.h"14#include "llvm/IR/DiagnosticInfo.h"15#include "llvm/IR/DiagnosticPrinter.h"16#include "llvm/Support/CrashRecoveryContext.h"17#include "llvm/Support/ManagedStatic.h"18#include "llvm/Support/Process.h"19#include "llvm/Support/Program.h"20#include "llvm/Support/raw_ostream.h"21#include <regex>2223using namespace llvm;24using namespace lld;2526static StringRef getSeparator(const Twine &msg) {27if (StringRef(msg.str()).contains('\n'))28return "\n";29return "";30}3132ErrorHandler::~ErrorHandler() {33if (cleanupCallback)34cleanupCallback();35}3637void ErrorHandler::initialize(llvm::raw_ostream &stdoutOS,38llvm::raw_ostream &stderrOS, bool exitEarly,39bool disableOutput) {40this->stdoutOS = &stdoutOS;41this->stderrOS = &stderrOS;42stderrOS.enable_colors(stderrOS.has_colors());43this->exitEarly = exitEarly;44this->disableOutput = disableOutput;45}4647void ErrorHandler::flushStreams() {48std::lock_guard<std::mutex> lock(mu);49outs().flush();50errs().flush();51}5253ErrorHandler &lld::errorHandler() { return context().e; }5455void lld::error(const Twine &msg) { errorHandler().error(msg); }56void lld::error(const Twine &msg, ErrorTag tag, ArrayRef<StringRef> args) {57errorHandler().error(msg, tag, args);58}59void lld::fatal(const Twine &msg) { errorHandler().fatal(msg); }60void lld::log(const Twine &msg) { errorHandler().log(msg); }61void lld::message(const Twine &msg, llvm::raw_ostream &s) {62errorHandler().message(msg, s);63}64void lld::warn(const Twine &msg) { errorHandler().warn(msg); }65uint64_t lld::errorCount() { return errorHandler().errorCount; }6667raw_ostream &lld::outs() {68ErrorHandler &e = errorHandler();69return e.outs();70}7172raw_ostream &lld::errs() {73ErrorHandler &e = errorHandler();74return e.errs();75}7677raw_ostream &ErrorHandler::outs() {78if (disableOutput)79return llvm::nulls();80return stdoutOS ? *stdoutOS : llvm::outs();81}8283raw_ostream &ErrorHandler::errs() {84if (disableOutput)85return llvm::nulls();86return stderrOS ? *stderrOS : llvm::errs();87}8889void lld::exitLld(int val) {90if (hasContext()) {91ErrorHandler &e = errorHandler();92// Delete any temporary file, while keeping the memory mapping open.93if (e.outputBuffer)94e.outputBuffer->discard();95}9697// Re-throw a possible signal or exception once/if it was caught by98// safeLldMain().99CrashRecoveryContext::throwIfCrash(val);100101// Dealloc/destroy ManagedStatic variables before calling _exit().102// In an LTO build, allows us to get the output of -time-passes.103// Ensures that the thread pool for the parallel algorithms is stopped to104// avoid intermittent crashes on Windows when exiting.105if (!CrashRecoveryContext::GetCurrent())106llvm_shutdown();107108if (hasContext())109lld::errorHandler().flushStreams();110111// When running inside safeLldMain(), restore the control flow back to the112// CrashRecoveryContext. Otherwise simply use _exit(), meanning no cleanup,113// since we want to avoid further crashes on shutdown.114llvm::sys::Process::Exit(val, /*NoCleanup=*/true);115}116117void lld::diagnosticHandler(const DiagnosticInfo &di) {118SmallString<128> s;119raw_svector_ostream os(s);120DiagnosticPrinterRawOStream dp(os);121122// For an inline asm diagnostic, prepend the module name to get something like123// "$module <inline asm>:1:5: ".124if (auto *dism = dyn_cast<DiagnosticInfoSrcMgr>(&di))125if (dism->isInlineAsmDiag())126os << dism->getModuleName() << ' ';127128di.print(dp);129switch (di.getSeverity()) {130case DS_Error:131error(s);132break;133case DS_Warning:134warn(s);135break;136case DS_Remark:137case DS_Note:138message(s);139break;140}141}142143void lld::checkError(Error e) {144handleAllErrors(std::move(e),145[&](ErrorInfoBase &eib) { error(eib.message()); });146}147148// This is for --vs-diagnostics.149//150// Normally, lld's error message starts with argv[0]. Therefore, it usually151// looks like this:152//153// ld.lld: error: ...154//155// This error message style is unfortunately unfriendly to Visual Studio156// IDE. VS interprets the first word of the first line as an error location157// and make it clickable, thus "ld.lld" in the above message would become a158// clickable text. When you click it, VS opens "ld.lld" executable file with159// a binary editor.160//161// As a workaround, we print out an error location instead of "ld.lld" if162// lld is running in VS diagnostics mode. As a result, error message will163// look like this:164//165// src/foo.c(35): error: ...166//167// This function returns an error location string. An error location is168// extracted from an error message using regexps.169std::string ErrorHandler::getLocation(const Twine &msg) {170if (!vsDiagnostics)171return std::string(logName);172173static std::regex regexes[] = {174std::regex(175R"(^undefined (?:\S+ )?symbol:.*\n)"176R"(>>> referenced by .+\((\S+):(\d+)\))"),177std::regex(178R"(^undefined (?:\S+ )?symbol:.*\n>>> referenced by (\S+):(\d+))"),179std::regex(R"(^undefined symbol:.*\n>>> referenced by (.*):)"),180std::regex(181R"(^duplicate symbol: .*\n>>> defined in (\S+)\n>>> defined in.*)"),182std::regex(183R"(^duplicate symbol: .*\n>>> defined at .+\((\S+):(\d+)\))"),184std::regex(R"(^duplicate symbol: .*\n>>> defined at (\S+):(\d+))"),185std::regex(186R"(.*\n>>> defined in .*\n>>> referenced by .+\((\S+):(\d+)\))"),187std::regex(R"(.*\n>>> defined in .*\n>>> referenced by (\S+):(\d+))"),188std::regex(R"((\S+):(\d+): unclosed quote)"),189};190191std::string str = msg.str();192for (std::regex &re : regexes) {193std::smatch m;194if (!std::regex_search(str, m, re))195continue;196197assert(m.size() == 2 || m.size() == 3);198if (m.size() == 2)199return m.str(1);200return m.str(1) + "(" + m.str(2) + ")";201}202203return std::string(logName);204}205206void ErrorHandler::reportDiagnostic(StringRef location, Colors c,207StringRef diagKind, const Twine &msg) {208SmallString<256> buf;209raw_svector_ostream os(buf);210os << sep << location << ": ";211if (!diagKind.empty()) {212if (lld::errs().colors_enabled()) {213os.enable_colors(true);214os << c << diagKind << ": " << Colors::RESET;215} else {216os << diagKind << ": ";217}218}219os << msg << '\n';220lld::errs() << buf;221}222223void ErrorHandler::log(const Twine &msg) {224if (!verbose || disableOutput)225return;226std::lock_guard<std::mutex> lock(mu);227reportDiagnostic(logName, Colors::RESET, "", msg);228}229230void ErrorHandler::message(const Twine &msg, llvm::raw_ostream &s) {231if (disableOutput)232return;233std::lock_guard<std::mutex> lock(mu);234s << msg << "\n";235s.flush();236}237238void ErrorHandler::warn(const Twine &msg) {239if (fatalWarnings) {240error(msg);241return;242}243244if (suppressWarnings)245return;246247std::lock_guard<std::mutex> lock(mu);248reportDiagnostic(getLocation(msg), Colors::MAGENTA, "warning", msg);249sep = getSeparator(msg);250}251252void ErrorHandler::error(const Twine &msg) {253// If Visual Studio-style error message mode is enabled,254// this particular error is printed out as two errors.255if (vsDiagnostics) {256static std::regex re(R"(^(duplicate symbol: .*))"257R"((\n>>> defined at \S+:\d+.*\n>>>.*))"258R"((\n>>> defined at \S+:\d+.*\n>>>.*))");259std::string str = msg.str();260std::smatch m;261262if (std::regex_match(str, m, re)) {263error(m.str(1) + m.str(2));264error(m.str(1) + m.str(3));265return;266}267}268269bool exit = false;270{271std::lock_guard<std::mutex> lock(mu);272273if (errorLimit == 0 || errorCount < errorLimit) {274reportDiagnostic(getLocation(msg), Colors::RED, "error", msg);275} else if (errorCount == errorLimit) {276reportDiagnostic(logName, Colors::RED, "error", errorLimitExceededMsg);277exit = exitEarly;278}279280sep = getSeparator(msg);281++errorCount;282}283284if (exit)285exitLld(1);286}287288void ErrorHandler::error(const Twine &msg, ErrorTag tag,289ArrayRef<StringRef> args) {290if (errorHandlingScript.empty()) {291error(msg);292return;293}294SmallVector<StringRef, 4> scriptArgs;295scriptArgs.push_back(errorHandlingScript);296switch (tag) {297case ErrorTag::LibNotFound:298scriptArgs.push_back("missing-lib");299break;300case ErrorTag::SymbolNotFound:301scriptArgs.push_back("undefined-symbol");302break;303}304scriptArgs.insert(scriptArgs.end(), args.begin(), args.end());305int res = llvm::sys::ExecuteAndWait(errorHandlingScript, scriptArgs);306if (res == 0) {307return error(msg);308} else {309// Temporarily disable error limit to make sure the two calls to error(...)310// only count as one.311uint64_t currentErrorLimit = errorLimit;312errorLimit = 0;313error(msg);314errorLimit = currentErrorLimit;315--errorCount;316317switch (res) {318case -1:319error("error handling script '" + errorHandlingScript +320"' failed to execute");321break;322case -2:323error("error handling script '" + errorHandlingScript +324"' crashed or timeout");325break;326default:327error("error handling script '" + errorHandlingScript +328"' exited with code " + Twine(res));329}330}331}332333void ErrorHandler::fatal(const Twine &msg) {334error(msg);335exitLld(1);336}337338339