ErrorHandler.cpp revision 360784
1//===- ErrorHandler.cpp ---------------------------------------------------===// 2// 3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4// See https://llvm.org/LICENSE.txt for license information. 5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6// 7//===----------------------------------------------------------------------===// 8 9#include "lld/Common/ErrorHandler.h" 10 11#include "lld/Common/Threads.h" 12 13#include "llvm/ADT/Twine.h" 14#include "llvm/IR/DiagnosticInfo.h" 15#include "llvm/IR/DiagnosticPrinter.h" 16#include "llvm/Support/ManagedStatic.h" 17#include "llvm/Support/raw_ostream.h" 18#include <mutex> 19#include <regex> 20 21#if !defined(_MSC_VER) && !defined(__MINGW32__) 22#include <unistd.h> 23#endif 24 25using namespace llvm; 26using namespace lld; 27 28// The functions defined in this file can be called from multiple threads, 29// but lld::outs() or lld::errs() are not thread-safe. We protect them using a 30// mutex. 31static std::mutex mu; 32 33// We want to separate multi-line messages with a newline. `sep` is "\n" 34// if the last messages was multi-line. Otherwise "". 35static StringRef sep; 36 37static StringRef getSeparator(const Twine &msg) { 38 if (StringRef(msg.str()).contains('\n')) 39 return "\n"; 40 return ""; 41} 42 43raw_ostream *lld::stdoutOS; 44raw_ostream *lld::stderrOS; 45 46raw_ostream &lld::outs() { return stdoutOS ? *stdoutOS : llvm::outs(); } 47raw_ostream &lld::errs() { return stderrOS ? *stderrOS : llvm::errs(); } 48 49ErrorHandler &lld::errorHandler() { 50 static ErrorHandler handler; 51 return handler; 52} 53 54void lld::exitLld(int val) { 55 // Delete any temporary file, while keeping the memory mapping open. 56 if (errorHandler().outputBuffer) 57 errorHandler().outputBuffer->discard(); 58 59 // Dealloc/destroy ManagedStatic variables before calling _exit(). 60 // In an LTO build, allows us to get the output of -time-passes. 61 // Ensures that the thread pool for the parallel algorithms is stopped to 62 // avoid intermittent crashes on Windows when exiting. 63 llvm_shutdown(); 64 65 lld::outs().flush(); 66 lld::errs().flush(); 67 _exit(val); 68} 69 70void lld::diagnosticHandler(const DiagnosticInfo &di) { 71 SmallString<128> s; 72 raw_svector_ostream os(s); 73 DiagnosticPrinterRawOStream dp(os); 74 di.print(dp); 75 switch (di.getSeverity()) { 76 case DS_Error: 77 error(s); 78 break; 79 case DS_Warning: 80 warn(s); 81 break; 82 case DS_Remark: 83 case DS_Note: 84 message(s); 85 break; 86 } 87} 88 89void lld::checkError(Error e) { 90 handleAllErrors(std::move(e), 91 [&](ErrorInfoBase &eib) { error(eib.message()); }); 92} 93 94// This is for --vs-diagnostics. 95// 96// Normally, lld's error message starts with argv[0]. Therefore, it usually 97// looks like this: 98// 99// ld.lld: error: ... 100// 101// This error message style is unfortunately unfriendly to Visual Studio 102// IDE. VS interprets the first word of the first line as an error location 103// and make it clickable, thus "ld.lld" in the above message would become a 104// clickable text. When you click it, VS opens "ld.lld" executable file with 105// a binary editor. 106// 107// As a workaround, we print out an error location instead of "ld.lld" if 108// lld is running in VS diagnostics mode. As a result, error message will 109// look like this: 110// 111// src/foo.c(35): error: ... 112// 113// This function returns an error location string. An error location is 114// extracted from an error message using regexps. 115std::string ErrorHandler::getLocation(const Twine &msg) { 116 if (!vsDiagnostics) 117 return logName; 118 119 static std::regex regexes[] = { 120 std::regex( 121 R"(^undefined (?:\S+ )?symbol:.*\n)" 122 R"(>>> referenced by .+\((\S+):(\d+)\))"), 123 std::regex( 124 R"(^undefined (?:\S+ )?symbol:.*\n>>> referenced by (\S+):(\d+))"), 125 std::regex(R"(^undefined symbol:.*\n>>> referenced by (.*):)"), 126 std::regex( 127 R"(^duplicate symbol: .*\n>>> defined in (\S+)\n>>> defined in.*)"), 128 std::regex( 129 R"(^duplicate symbol: .*\n>>> defined at .+\((\S+):(\d+)\))"), 130 std::regex(R"(^duplicate symbol: .*\n>>> defined at (\S+):(\d+))"), 131 std::regex( 132 R"(.*\n>>> defined in .*\n>>> referenced by .+\((\S+):(\d+)\))"), 133 std::regex(R"(.*\n>>> defined in .*\n>>> referenced by (\S+):(\d+))"), 134 std::regex(R"((\S+):(\d+): unclosed quote)"), 135 }; 136 137 std::string str = msg.str(); 138 for (std::regex &re : regexes) { 139 std::smatch m; 140 if (!std::regex_search(str, m, re)) 141 continue; 142 143 assert(m.size() == 2 || m.size() == 3); 144 if (m.size() == 2) 145 return m.str(1); 146 return m.str(1) + "(" + m.str(2) + ")"; 147 } 148 149 return logName; 150} 151 152void ErrorHandler::log(const Twine &msg) { 153 if (!verbose) 154 return; 155 std::lock_guard<std::mutex> lock(mu); 156 lld::errs() << logName << ": " << msg << "\n"; 157} 158 159void ErrorHandler::message(const Twine &msg) { 160 std::lock_guard<std::mutex> lock(mu); 161 lld::outs() << msg << "\n"; 162 lld::outs().flush(); 163} 164 165void ErrorHandler::warn(const Twine &msg) { 166 if (fatalWarnings) { 167 error(msg); 168 return; 169 } 170 171 std::lock_guard<std::mutex> lock(mu); 172 lld::errs() << sep << getLocation(msg) << ": " << Colors::MAGENTA 173 << "warning: " << Colors::RESET << msg << "\n"; 174 sep = getSeparator(msg); 175} 176 177void ErrorHandler::error(const Twine &msg) { 178 // If Visual Studio-style error message mode is enabled, 179 // this particular error is printed out as two errors. 180 if (vsDiagnostics) { 181 static std::regex re(R"(^(duplicate symbol: .*))" 182 R"((\n>>> defined at \S+:\d+.*\n>>>.*))" 183 R"((\n>>> defined at \S+:\d+.*\n>>>.*))"); 184 std::string str = msg.str(); 185 std::smatch m; 186 187 if (std::regex_match(str, m, re)) { 188 error(m.str(1) + m.str(2)); 189 error(m.str(1) + m.str(3)); 190 return; 191 } 192 } 193 194 std::lock_guard<std::mutex> lock(mu); 195 196 if (errorLimit == 0 || errorCount < errorLimit) { 197 lld::errs() << sep << getLocation(msg) << ": " << Colors::RED 198 << "error: " << Colors::RESET << msg << "\n"; 199 } else if (errorCount == errorLimit) { 200 lld::errs() << sep << getLocation(msg) << ": " << Colors::RED 201 << "error: " << Colors::RESET << errorLimitExceededMsg << "\n"; 202 if (exitEarly) 203 exitLld(1); 204 } 205 206 sep = getSeparator(msg); 207 ++errorCount; 208} 209 210void ErrorHandler::fatal(const Twine &msg) { 211 error(msg); 212 exitLld(1); 213} 214