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 "llvm/Support/Parallel.h" 12 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 <mutex> 22 #include <regex> 23 24 using namespace llvm; 25 using namespace lld; 26 27 // The functions defined in this file can be called from multiple threads, 28 // but lld::outs() or lld::errs() are not thread-safe. We protect them using a 29 // mutex. 30 static std::mutex mu; 31 32 // We want to separate multi-line messages with a newline. `sep` is "\n" 33 // if the last messages was multi-line. Otherwise "". 34 static StringRef sep; 35 36 static StringRef getSeparator(const Twine &msg) { 37 if (StringRef(msg.str()).contains('\n')) 38 return "\n"; 39 return ""; 40 } 41 42 raw_ostream *lld::stdoutOS; 43 raw_ostream *lld::stderrOS; 44 45 ErrorHandler &lld::errorHandler() { 46 static ErrorHandler handler; 47 return handler; 48 } 49 50 raw_ostream &lld::outs() { 51 if (errorHandler().disableOutput) 52 return llvm::nulls(); 53 return stdoutOS ? *stdoutOS : llvm::outs(); 54 } 55 56 raw_ostream &lld::errs() { 57 if (errorHandler().disableOutput) 58 return llvm::nulls(); 59 return stderrOS ? *stderrOS : llvm::errs(); 60 } 61 62 void lld::exitLld(int val) { 63 // Delete any temporary file, while keeping the memory mapping open. 64 if (errorHandler().outputBuffer) 65 errorHandler().outputBuffer->discard(); 66 67 // Dealloc/destroy ManagedStatic variables before calling _exit(). 68 // In an LTO build, allows us to get the output of -time-passes. 69 // Ensures that the thread pool for the parallel algorithms is stopped to 70 // avoid intermittent crashes on Windows when exiting. 71 if (!CrashRecoveryContext::GetCurrent()) 72 llvm_shutdown(); 73 74 { 75 std::lock_guard<std::mutex> lock(mu); 76 lld::outs().flush(); 77 lld::errs().flush(); 78 } 79 llvm::sys::Process::Exit(val); 80 } 81 82 void lld::diagnosticHandler(const DiagnosticInfo &di) { 83 SmallString<128> s; 84 raw_svector_ostream os(s); 85 DiagnosticPrinterRawOStream dp(os); 86 di.print(dp); 87 switch (di.getSeverity()) { 88 case DS_Error: 89 error(s); 90 break; 91 case DS_Warning: 92 warn(s); 93 break; 94 case DS_Remark: 95 case DS_Note: 96 message(s); 97 break; 98 } 99 } 100 101 void lld::checkError(Error e) { 102 handleAllErrors(std::move(e), 103 [&](ErrorInfoBase &eib) { error(eib.message()); }); 104 } 105 106 // This is for --vs-diagnostics. 107 // 108 // Normally, lld's error message starts with argv[0]. Therefore, it usually 109 // looks like this: 110 // 111 // ld.lld: error: ... 112 // 113 // This error message style is unfortunately unfriendly to Visual Studio 114 // IDE. VS interprets the first word of the first line as an error location 115 // and make it clickable, thus "ld.lld" in the above message would become a 116 // clickable text. When you click it, VS opens "ld.lld" executable file with 117 // a binary editor. 118 // 119 // As a workaround, we print out an error location instead of "ld.lld" if 120 // lld is running in VS diagnostics mode. As a result, error message will 121 // look like this: 122 // 123 // src/foo.c(35): error: ... 124 // 125 // This function returns an error location string. An error location is 126 // extracted from an error message using regexps. 127 std::string ErrorHandler::getLocation(const Twine &msg) { 128 if (!vsDiagnostics) 129 return std::string(logName); 130 131 static std::regex regexes[] = { 132 std::regex( 133 R"(^undefined (?:\S+ )?symbol:.*\n)" 134 R"(>>> referenced by .+\((\S+):(\d+)\))"), 135 std::regex( 136 R"(^undefined (?:\S+ )?symbol:.*\n>>> referenced by (\S+):(\d+))"), 137 std::regex(R"(^undefined symbol:.*\n>>> referenced by (.*):)"), 138 std::regex( 139 R"(^duplicate symbol: .*\n>>> defined in (\S+)\n>>> defined in.*)"), 140 std::regex( 141 R"(^duplicate symbol: .*\n>>> defined at .+\((\S+):(\d+)\))"), 142 std::regex(R"(^duplicate symbol: .*\n>>> defined at (\S+):(\d+))"), 143 std::regex( 144 R"(.*\n>>> defined in .*\n>>> referenced by .+\((\S+):(\d+)\))"), 145 std::regex(R"(.*\n>>> defined in .*\n>>> referenced by (\S+):(\d+))"), 146 std::regex(R"((\S+):(\d+): unclosed quote)"), 147 }; 148 149 std::string str = msg.str(); 150 for (std::regex &re : regexes) { 151 std::smatch m; 152 if (!std::regex_search(str, m, re)) 153 continue; 154 155 assert(m.size() == 2 || m.size() == 3); 156 if (m.size() == 2) 157 return m.str(1); 158 return m.str(1) + "(" + m.str(2) + ")"; 159 } 160 161 return std::string(logName); 162 } 163 164 void ErrorHandler::log(const Twine &msg) { 165 if (!verbose || disableOutput) 166 return; 167 std::lock_guard<std::mutex> lock(mu); 168 lld::errs() << logName << ": " << msg << "\n"; 169 } 170 171 void ErrorHandler::message(const Twine &msg) { 172 if (disableOutput) 173 return; 174 std::lock_guard<std::mutex> lock(mu); 175 lld::outs() << msg << "\n"; 176 lld::outs().flush(); 177 } 178 179 void ErrorHandler::warn(const Twine &msg) { 180 if (fatalWarnings) { 181 error(msg); 182 return; 183 } 184 185 std::lock_guard<std::mutex> lock(mu); 186 lld::errs() << sep << getLocation(msg) << ": " << Colors::MAGENTA 187 << "warning: " << Colors::RESET << msg << "\n"; 188 sep = getSeparator(msg); 189 } 190 191 void ErrorHandler::error(const Twine &msg) { 192 // If Visual Studio-style error message mode is enabled, 193 // this particular error is printed out as two errors. 194 if (vsDiagnostics) { 195 static std::regex re(R"(^(duplicate symbol: .*))" 196 R"((\n>>> defined at \S+:\d+.*\n>>>.*))" 197 R"((\n>>> defined at \S+:\d+.*\n>>>.*))"); 198 std::string str = msg.str(); 199 std::smatch m; 200 201 if (std::regex_match(str, m, re)) { 202 error(m.str(1) + m.str(2)); 203 error(m.str(1) + m.str(3)); 204 return; 205 } 206 } 207 208 bool exit = false; 209 { 210 std::lock_guard<std::mutex> lock(mu); 211 212 if (errorLimit == 0 || errorCount < errorLimit) { 213 lld::errs() << sep << getLocation(msg) << ": " << Colors::RED 214 << "error: " << Colors::RESET << msg << "\n"; 215 } else if (errorCount == errorLimit) { 216 lld::errs() << sep << getLocation(msg) << ": " << Colors::RED 217 << "error: " << Colors::RESET << errorLimitExceededMsg 218 << "\n"; 219 exit = exitEarly; 220 } 221 222 sep = getSeparator(msg); 223 ++errorCount; 224 } 225 226 if (exit) 227 exitLld(1); 228 } 229 230 void ErrorHandler::error(const Twine &msg, ErrorTag tag, 231 ArrayRef<StringRef> args) { 232 if (errorHandlingScript.empty()) { 233 error(msg); 234 return; 235 } 236 SmallVector<StringRef, 4> scriptArgs; 237 scriptArgs.push_back(errorHandlingScript); 238 switch (tag) { 239 case ErrorTag::LibNotFound: 240 scriptArgs.push_back("missing-lib"); 241 break; 242 case ErrorTag::SymbolNotFound: 243 scriptArgs.push_back("undefined-symbol"); 244 break; 245 } 246 scriptArgs.insert(scriptArgs.end(), args.begin(), args.end()); 247 int res = llvm::sys::ExecuteAndWait(errorHandlingScript, scriptArgs); 248 if (res == 0) { 249 return error(msg); 250 } else { 251 // Temporarily disable error limit to make sure the two calls to error(...) 252 // only count as one. 253 uint64_t currentErrorLimit = errorLimit; 254 errorLimit = 0; 255 error(msg); 256 errorLimit = currentErrorLimit; 257 --errorCount; 258 259 switch (res) { 260 case -1: 261 error("error handling script '" + errorHandlingScript + 262 "' failed to execute"); 263 break; 264 case -2: 265 error("error handling script '" + errorHandlingScript + 266 "' crashed or timeout"); 267 break; 268 default: 269 error("error handling script '" + errorHandlingScript + 270 "' exited with code " + Twine(res)); 271 } 272 } 273 } 274 275 void ErrorHandler::fatal(const Twine &msg) { 276 error(msg); 277 exitLld(1); 278 } 279