xref: /freebsd/contrib/llvm-project/lld/Common/ErrorHandler.cpp (revision 770cf0a5f02dc8983a89c6568d741fbc25baa999)
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('\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(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(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(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>(&di))
119     if (dism->isInlineAsmDiag())
120       os << dism->getModuleName() << ' ';
121 
122   di.print(dp);
123   switch (di.getSeverity()) {
124   case DS_Error:
125     error(s);
126     break;
127   case DS_Warning:
128     warn(s);
129     break;
130   case DS_Remark:
131   case DS_Note:
132     message(s);
133     break;
134   }
135 }
136 
137 void lld::checkError(Error e) {
138   handleAllErrors(std::move(e),
139                   [&](ErrorInfoBase &eib) { error(eib.message()); });
140 }
141 
142 void lld::checkError(ErrorHandler &eh, Error e) {
143   handleAllErrors(std::move(e),
144                   [&](ErrorInfoBase &eib) { eh.error(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(str, m, re))
194       continue;
195 
196     assert(m.size() == 2 || m.size() == 3);
197     if (m.size() == 2)
198       return m.str(1);
199     return m.str(1) + "(" + m.str(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(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(logName, Colors::RESET, "", 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(getLocation(msg), Colors::MAGENTA, "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(str, m, re)) {
264       error(m.str(1) + m.str(2));
265       error(m.str(1) + m.str(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(getLocation(msg), Colors::RED, "error", msg);
276     } else if (errorCount == errorLimit) {
277       reportDiagnostic(logName, Colors::RED, "error", errorLimitExceededMsg);
278       exit = exitEarly;
279     }
280 
281     ++errorCount;
282   }
283 
284   if (exit)
285     exitLld(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(errorHandlingScript);
296   switch (tag) {
297   case ErrorTag::LibNotFound:
298     scriptArgs.push_back("missing-lib");
299     break;
300   case ErrorTag::SymbolNotFound:
301     scriptArgs.push_back("undefined-symbol");
302     break;
303   }
304   scriptArgs.insert(scriptArgs.end(), args.begin(), args.end());
305   int res = llvm::sys::ExecuteAndWait(errorHandlingScript, 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("error handling script '" + errorHandlingScript +
320             "' failed to execute");
321       break;
322     case -2:
323       error("error handling script '" + errorHandlingScript +
324             "' crashed or timeout");
325       break;
326     default:
327       error("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(1);
336 }
337 
338 SyncStream::~SyncStream() {
339   switch (level) {
340   case DiagLevel::None:
341     break;
342   case DiagLevel::Log:
343     e.log(buf);
344     break;
345   case DiagLevel::Msg:
346     e.message(buf, e.outs());
347     break;
348   case DiagLevel::Warn:
349     e.warn(buf);
350     break;
351   case DiagLevel::Err:
352     e.error(buf);
353     break;
354   case DiagLevel::Fatal:
355     e.fatal(buf);
356     break;
357   }
358 }
359