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