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