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 | |