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