| 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/CommonLinkerContext.h" |
| 12 | #include "llvm/ADT/Twine.h" |
| 13 | #include "llvm/IR/DiagnosticInfo.h" |
| 14 | #include "llvm/IR/DiagnosticPrinter.h" |
| 15 | #include "llvm/Support/CrashRecoveryContext.h" |
| 16 | #include "llvm/Support/ManagedStatic.h" |
| 17 | #include "llvm/Support/Process.h" |
| 18 | #include "llvm/Support/Program.h" |
| 19 | #include "llvm/Support/raw_ostream.h" |
| 20 | #include <regex> |
| 21 | |
| 22 | using namespace llvm; |
| 23 | using namespace lld; |
| 24 | |
| 25 | static StringRef getSeparator(const Twine &msg) { |
| 26 | if (StringRef(msg.str()).contains(C: '\n')) |
| 27 | return "\n" ; |
| 28 | return "" ; |
| 29 | } |
| 30 | |
| 31 | ErrorHandler::~ErrorHandler() { |
| 32 | if (cleanupCallback) |
| 33 | cleanupCallback(); |
| 34 | } |
| 35 | |
| 36 | void ErrorHandler::initialize(llvm::raw_ostream &stdoutOS, |
| 37 | llvm::raw_ostream &stderrOS, bool exitEarly, |
| 38 | bool disableOutput) { |
| 39 | this->stdoutOS = &stdoutOS; |
| 40 | this->stderrOS = &stderrOS; |
| 41 | stderrOS.enable_colors(enable: stderrOS.has_colors()); |
| 42 | this->exitEarly = exitEarly; |
| 43 | this->disableOutput = disableOutput; |
| 44 | } |
| 45 | |
| 46 | void ErrorHandler::flushStreams() { |
| 47 | std::lock_guard<std::mutex> lock(mu); |
| 48 | outs().flush(); |
| 49 | errs().flush(); |
| 50 | } |
| 51 | |
| 52 | ErrorHandler &lld::errorHandler() { return context().e; } |
| 53 | |
| 54 | void lld::error(const Twine &msg) { errorHandler().error(msg); } |
| 55 | void lld::error(const Twine &msg, ErrorTag tag, ArrayRef<StringRef> args) { |
| 56 | errorHandler().error(msg, tag, args); |
| 57 | } |
| 58 | void lld::fatal(const Twine &msg) { errorHandler().fatal(msg); } |
| 59 | void lld::log(const Twine &msg) { errorHandler().log(msg); } |
| 60 | void lld::message(const Twine &msg, llvm::raw_ostream &s) { |
| 61 | errorHandler().message(msg, s); |
| 62 | } |
| 63 | void lld::warn(const Twine &msg) { errorHandler().warn(msg); } |
| 64 | uint64_t lld::errorCount() { return errorHandler().errorCount; } |
| 65 | |
| 66 | raw_ostream &lld::outs() { |
| 67 | ErrorHandler &e = errorHandler(); |
| 68 | return e.outs(); |
| 69 | } |
| 70 | |
| 71 | raw_ostream &ErrorHandler::outs() { |
| 72 | if (disableOutput) |
| 73 | return llvm::nulls(); |
| 74 | return stdoutOS ? *stdoutOS : llvm::outs(); |
| 75 | } |
| 76 | |
| 77 | raw_ostream &ErrorHandler::errs() { |
| 78 | if (disableOutput) |
| 79 | return llvm::nulls(); |
| 80 | return stderrOS ? *stderrOS : llvm::errs(); |
| 81 | } |
| 82 | |
| 83 | void lld::exitLld(int val) { |
| 84 | if (hasContext()) { |
| 85 | ErrorHandler &e = errorHandler(); |
| 86 | // Delete any temporary file, while keeping the memory mapping open. |
| 87 | if (e.outputBuffer) |
| 88 | e.outputBuffer->discard(); |
| 89 | } |
| 90 | |
| 91 | // Re-throw a possible signal or exception once/if it was caught by |
| 92 | // safeLldMain(). |
| 93 | CrashRecoveryContext::throwIfCrash(RetCode: val); |
| 94 | |
| 95 | // Dealloc/destroy ManagedStatic variables before calling _exit(). |
| 96 | // In an LTO build, allows us to get the output of -time-passes. |
| 97 | // Ensures that the thread pool for the parallel algorithms is stopped to |
| 98 | // avoid intermittent crashes on Windows when exiting. |
| 99 | if (!CrashRecoveryContext::GetCurrent()) |
| 100 | llvm_shutdown(); |
| 101 | |
| 102 | if (hasContext()) |
| 103 | lld::errorHandler().flushStreams(); |
| 104 | |
| 105 | // When running inside safeLldMain(), restore the control flow back to the |
| 106 | // CrashRecoveryContext. Otherwise simply use _exit(), meanning no cleanup, |
| 107 | // since we want to avoid further crashes on shutdown. |
| 108 | llvm::sys::Process::Exit(RetCode: val, /*NoCleanup=*/true); |
| 109 | } |
| 110 | |
| 111 | void lld::diagnosticHandler(const DiagnosticInfo &di) { |
| 112 | SmallString<128> s; |
| 113 | raw_svector_ostream os(s); |
| 114 | DiagnosticPrinterRawOStream dp(os); |
| 115 | |
| 116 | // For an inline asm diagnostic, prepend the module name to get something like |
| 117 | // "$module <inline asm>:1:5: ". |
| 118 | if (auto *dism = dyn_cast<DiagnosticInfoSrcMgr>(Val: &di)) |
| 119 | if (dism->isInlineAsmDiag()) |
| 120 | os << dism->getModuleName() << ' '; |
| 121 | |
| 122 | di.print(DP&: dp); |
| 123 | switch (di.getSeverity()) { |
| 124 | case DS_Error: |
| 125 | error(msg: s); |
| 126 | break; |
| 127 | case DS_Warning: |
| 128 | warn(msg: s); |
| 129 | break; |
| 130 | case DS_Remark: |
| 131 | case DS_Note: |
| 132 | message(msg: s); |
| 133 | break; |
| 134 | } |
| 135 | } |
| 136 | |
| 137 | void lld::checkError(Error e) { |
| 138 | handleAllErrors(E: std::move(e), |
| 139 | Handlers: [&](ErrorInfoBase &eib) { error(msg: eib.message()); }); |
| 140 | } |
| 141 | |
| 142 | void lld::checkError(ErrorHandler &eh, Error e) { |
| 143 | handleAllErrors(E: std::move(e), |
| 144 | Handlers: [&](ErrorInfoBase &eib) { eh.error(msg: eib.message()); }); |
| 145 | } |
| 146 | |
| 147 | // This is for --vs-diagnostics. |
| 148 | // |
| 149 | // Normally, lld's error message starts with argv[0]. Therefore, it usually |
| 150 | // looks like this: |
| 151 | // |
| 152 | // ld.lld: error: ... |
| 153 | // |
| 154 | // This error message style is unfortunately unfriendly to Visual Studio |
| 155 | // IDE. VS interprets the first word of the first line as an error location |
| 156 | // and make it clickable, thus "ld.lld" in the above message would become a |
| 157 | // clickable text. When you click it, VS opens "ld.lld" executable file with |
| 158 | // a binary editor. |
| 159 | // |
| 160 | // As a workaround, we print out an error location instead of "ld.lld" if |
| 161 | // lld is running in VS diagnostics mode. As a result, error message will |
| 162 | // look like this: |
| 163 | // |
| 164 | // src/foo.c(35): error: ... |
| 165 | // |
| 166 | // This function returns an error location string. An error location is |
| 167 | // extracted from an error message using regexps. |
| 168 | std::string ErrorHandler::getLocation(const Twine &msg) { |
| 169 | if (!vsDiagnostics) |
| 170 | return std::string(logName); |
| 171 | |
| 172 | static std::regex regexes[] = { |
| 173 | std::regex( |
| 174 | R"(^undefined (?:\S+ )?symbol:.*\n)" |
| 175 | R"(>>> referenced by .+\((\S+):(\d+)\))" ), |
| 176 | std::regex( |
| 177 | R"(^undefined (?:\S+ )?symbol:.*\n>>> referenced by (\S+):(\d+))" ), |
| 178 | std::regex(R"(^undefined symbol:.*\n>>> referenced by (.*):)" ), |
| 179 | std::regex( |
| 180 | R"(^duplicate symbol: .*\n>>> defined in (\S+)\n>>> defined in.*)" ), |
| 181 | std::regex( |
| 182 | R"(^duplicate symbol: .*\n>>> defined at .+\((\S+):(\d+)\))" ), |
| 183 | std::regex(R"(^duplicate symbol: .*\n>>> defined at (\S+):(\d+))" ), |
| 184 | std::regex( |
| 185 | R"(.*\n>>> defined in .*\n>>> referenced by .+\((\S+):(\d+)\))" ), |
| 186 | std::regex(R"(.*\n>>> defined in .*\n>>> referenced by (\S+):(\d+))" ), |
| 187 | std::regex(R"((\S+):(\d+): unclosed quote)" ), |
| 188 | }; |
| 189 | |
| 190 | std::string str = msg.str(); |
| 191 | for (std::regex &re : regexes) { |
| 192 | std::smatch m; |
| 193 | if (!std::regex_search(s: str, m&: m, e: re)) |
| 194 | continue; |
| 195 | |
| 196 | assert(m.size() == 2 || m.size() == 3); |
| 197 | if (m.size() == 2) |
| 198 | return m.str(sub: 1); |
| 199 | return m.str(sub: 1) + "(" + m.str(sub: 2) + ")" ; |
| 200 | } |
| 201 | |
| 202 | return std::string(logName); |
| 203 | } |
| 204 | |
| 205 | void ErrorHandler::reportDiagnostic(StringRef location, Colors c, |
| 206 | StringRef diagKind, const Twine &msg) { |
| 207 | SmallString<256> buf; |
| 208 | raw_svector_ostream os(buf); |
| 209 | os << sep << location << ": " ; |
| 210 | if (!diagKind.empty()) { |
| 211 | if (errs().colors_enabled()) { |
| 212 | os.enable_colors(enable: true); |
| 213 | os << c << diagKind << ": " << Colors::RESET; |
| 214 | } else { |
| 215 | os << diagKind << ": " ; |
| 216 | } |
| 217 | } |
| 218 | os << msg << '\n'; |
| 219 | errs() << buf; |
| 220 | // If msg contains a newline, ensure that the next diagnostic is preceded by |
| 221 | // a blank line separator. |
| 222 | sep = getSeparator(msg); |
| 223 | } |
| 224 | |
| 225 | void ErrorHandler::log(const Twine &msg) { |
| 226 | if (!verbose || disableOutput) |
| 227 | return; |
| 228 | std::lock_guard<std::mutex> lock(mu); |
| 229 | reportDiagnostic(location: logName, c: Colors::RESET, diagKind: "" , msg); |
| 230 | } |
| 231 | |
| 232 | void ErrorHandler::message(const Twine &msg, llvm::raw_ostream &s) { |
| 233 | if (disableOutput) |
| 234 | return; |
| 235 | std::lock_guard<std::mutex> lock(mu); |
| 236 | s << msg << "\n" ; |
| 237 | s.flush(); |
| 238 | } |
| 239 | |
| 240 | void ErrorHandler::warn(const Twine &msg) { |
| 241 | if (fatalWarnings) { |
| 242 | error(msg); |
| 243 | return; |
| 244 | } |
| 245 | |
| 246 | if (suppressWarnings) |
| 247 | return; |
| 248 | |
| 249 | std::lock_guard<std::mutex> lock(mu); |
| 250 | reportDiagnostic(location: getLocation(msg), c: Colors::MAGENTA, diagKind: "warning" , msg); |
| 251 | } |
| 252 | |
| 253 | void ErrorHandler::error(const Twine &msg) { |
| 254 | // If Visual Studio-style error message mode is enabled, |
| 255 | // this particular error is printed out as two errors. |
| 256 | if (vsDiagnostics) { |
| 257 | static std::regex re(R"(^(duplicate symbol: .*))" |
| 258 | R"((\n>>> defined at \S+:\d+.*\n>>>.*))" |
| 259 | R"((\n>>> defined at \S+:\d+.*\n>>>.*))" ); |
| 260 | std::string str = msg.str(); |
| 261 | std::smatch m; |
| 262 | |
| 263 | if (std::regex_match(s: str, m&: m, re: re)) { |
| 264 | error(msg: m.str(sub: 1) + m.str(sub: 2)); |
| 265 | error(msg: m.str(sub: 1) + m.str(sub: 3)); |
| 266 | return; |
| 267 | } |
| 268 | } |
| 269 | |
| 270 | bool exit = false; |
| 271 | { |
| 272 | std::lock_guard<std::mutex> lock(mu); |
| 273 | |
| 274 | if (errorLimit == 0 || errorCount < errorLimit) { |
| 275 | reportDiagnostic(location: getLocation(msg), c: Colors::RED, diagKind: "error" , msg); |
| 276 | } else if (errorCount == errorLimit) { |
| 277 | reportDiagnostic(location: logName, c: Colors::RED, diagKind: "error" , msg: errorLimitExceededMsg); |
| 278 | exit = exitEarly; |
| 279 | } |
| 280 | |
| 281 | ++errorCount; |
| 282 | } |
| 283 | |
| 284 | if (exit) |
| 285 | exitLld(val: 1); |
| 286 | } |
| 287 | |
| 288 | void ErrorHandler::error(const Twine &msg, ErrorTag tag, |
| 289 | ArrayRef<StringRef> args) { |
| 290 | if (errorHandlingScript.empty() || disableOutput) { |
| 291 | error(msg); |
| 292 | return; |
| 293 | } |
| 294 | SmallVector<StringRef, 4> scriptArgs; |
| 295 | scriptArgs.push_back(Elt: errorHandlingScript); |
| 296 | switch (tag) { |
| 297 | case ErrorTag::LibNotFound: |
| 298 | scriptArgs.push_back(Elt: "missing-lib" ); |
| 299 | break; |
| 300 | case ErrorTag::SymbolNotFound: |
| 301 | scriptArgs.push_back(Elt: "undefined-symbol" ); |
| 302 | break; |
| 303 | } |
| 304 | scriptArgs.insert(I: scriptArgs.end(), From: args.begin(), To: args.end()); |
| 305 | int res = llvm::sys::ExecuteAndWait(Program: errorHandlingScript, Args: scriptArgs); |
| 306 | if (res == 0) { |
| 307 | return error(msg); |
| 308 | } else { |
| 309 | // Temporarily disable error limit to make sure the two calls to error(...) |
| 310 | // only count as one. |
| 311 | uint64_t currentErrorLimit = errorLimit; |
| 312 | errorLimit = 0; |
| 313 | error(msg); |
| 314 | errorLimit = currentErrorLimit; |
| 315 | --errorCount; |
| 316 | |
| 317 | switch (res) { |
| 318 | case -1: |
| 319 | error(msg: "error handling script '" + errorHandlingScript + |
| 320 | "' failed to execute" ); |
| 321 | break; |
| 322 | case -2: |
| 323 | error(msg: "error handling script '" + errorHandlingScript + |
| 324 | "' crashed or timeout" ); |
| 325 | break; |
| 326 | default: |
| 327 | error(msg: "error handling script '" + errorHandlingScript + |
| 328 | "' exited with code " + Twine(res)); |
| 329 | } |
| 330 | } |
| 331 | } |
| 332 | |
| 333 | void ErrorHandler::fatal(const Twine &msg) { |
| 334 | error(msg); |
| 335 | exitLld(val: 1); |
| 336 | } |
| 337 | |
| 338 | SyncStream::~SyncStream() { |
| 339 | switch (level) { |
| 340 | case DiagLevel::None: |
| 341 | break; |
| 342 | case DiagLevel::Log: |
| 343 | e.log(msg: buf); |
| 344 | break; |
| 345 | case DiagLevel::Msg: |
| 346 | e.message(msg: buf, s&: e.outs()); |
| 347 | break; |
| 348 | case DiagLevel::Warn: |
| 349 | e.warn(msg: buf); |
| 350 | break; |
| 351 | case DiagLevel::Err: |
| 352 | e.error(msg: buf); |
| 353 | break; |
| 354 | case DiagLevel::Fatal: |
| 355 | e.fatal(msg: buf); |
| 356 | break; |
| 357 | } |
| 358 | } |
| 359 | |