xref: /third_party/gn/src/base/logging.cc (revision 6d528ed9)
1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/logging.h"
6
7#include <limits.h>
8#include <stdint.h>
9
10#include <iterator>
11#include <thread>
12
13#include "util/build_config.h"
14
15#if defined(OS_WIN)
16#include <io.h>
17#include <windows.h>
18// Windows warns on using write().  It prefers _write().
19#define write(fd, buf, count) _write(fd, buf, static_cast<unsigned int>(count))
20// Windows doesn't define STDERR_FILENO.  Define it here.
21#define STDERR_FILENO 2
22
23#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
24#include <sys/time.h>
25#include <time.h>
26#endif
27
28#if defined(OS_POSIX) || defined(OS_FUCHSIA)
29#include <errno.h>
30#include <pthread.h>
31#include <stdio.h>
32#include <stdlib.h>
33#include <string.h>
34#include <sys/stat.h>
35#include <unistd.h>
36#if !defined(OS_ZOS)
37#include <paths.h>
38#endif
39#endif
40
41#include <algorithm>
42#include <cstring>
43#include <ctime>
44#include <iomanip>
45#include <ostream>
46#include <string>
47#include <string_view>
48#include <utility>
49
50#include "base/posix/eintr_wrapper.h"
51#include "base/strings/string_util.h"
52#include "base/strings/stringprintf.h"
53#include "base/strings/utf_string_conversions.h"
54
55#if defined(OS_POSIX) || defined(OS_FUCHSIA)
56#include "base/posix/safe_strerror.h"
57#endif
58
59namespace logging {
60
61namespace {
62
63const char* const log_severity_names[] = {"INFO", "WARNING", "ERROR", "FATAL"};
64static_assert(LOG_NUM_SEVERITIES == std::size(log_severity_names),
65              "Incorrect number of log_severity_names");
66
67const char* log_severity_name(int severity) {
68  if (severity >= 0 && severity < LOG_NUM_SEVERITIES)
69    return log_severity_names[severity];
70  return "UNKNOWN";
71}
72
73int g_min_log_level = 0;
74
75// For LOG_ERROR and above, always print to stderr.
76const int kAlwaysPrintErrorLevel = LOG_ERROR;
77
78}  // namespace
79
80#if defined(DCHECK_IS_CONFIGURABLE)
81// In DCHECK-enabled Chrome builds, allow the meaning of LOG_DCHECK to be
82// determined at run-time. We default it to INFO, to avoid it triggering
83// crashes before the run-time has explicitly chosen the behaviour.
84logging::LogSeverity LOG_DCHECK = LOG_INFO;
85#endif  // DCHECK_IS_CONFIGURABLE
86
87// This is never instantiated, it's just used for EAT_STREAM_PARAMETERS to have
88// an object of the correct type on the LHS of the unused part of the ternary
89// operator.
90std::ostream* g_swallow_stream;
91
92void SetMinLogLevel(int level) {
93  g_min_log_level = std::min(LOG_FATAL, level);
94}
95
96int GetMinLogLevel() {
97  return g_min_log_level;
98}
99
100bool ShouldCreateLogMessage(int severity) {
101  if (severity < g_min_log_level)
102    return false;
103
104  // Return true here unless we know ~LogMessage won't do anything. Note that
105  // ~LogMessage writes to stderr if severity_ >= kAlwaysPrintErrorLevel, even
106  // when g_logging_destination is LOG_NONE.
107  return severity >= kAlwaysPrintErrorLevel;
108}
109
110// Explicit instantiations for commonly used comparisons.
111template std::string* MakeCheckOpString<int, int>(const int&,
112                                                  const int&,
113                                                  const char* names);
114template std::string* MakeCheckOpString<unsigned long, unsigned long>(
115    const unsigned long&,
116    const unsigned long&,
117    const char* names);
118template std::string* MakeCheckOpString<unsigned long, unsigned int>(
119    const unsigned long&,
120    const unsigned int&,
121    const char* names);
122template std::string* MakeCheckOpString<unsigned int, unsigned long>(
123    const unsigned int&,
124    const unsigned long&,
125    const char* names);
126template std::string* MakeCheckOpString<std::string, std::string>(
127    const std::string&,
128    const std::string&,
129    const char* name);
130
131void MakeCheckOpValueString(std::ostream* os, std::nullptr_t p) {
132  (*os) << "nullptr";
133}
134
135#if defined(OS_WIN)
136LogMessage::SaveLastError::SaveLastError() : last_error_(::GetLastError()) {}
137
138LogMessage::SaveLastError::~SaveLastError() {
139  ::SetLastError(last_error_);
140}
141#endif  // defined(OS_WIN)
142
143LogMessage::LogMessage(const char* file, int line, LogSeverity severity)
144    : severity_(severity) {
145  Init(file, line);
146}
147
148LogMessage::LogMessage(const char* file, int line, const char* condition)
149    : severity_(LOG_FATAL) {
150  Init(file, line);
151  stream_ << "Check failed: " << condition << ". ";
152}
153
154LogMessage::LogMessage(const char* file, int line, std::string* result)
155    : severity_(LOG_FATAL) {
156  Init(file, line);
157  stream_ << "Check failed: " << *result;
158  delete result;
159}
160
161LogMessage::LogMessage(const char* file,
162                       int line,
163                       LogSeverity severity,
164                       std::string* result)
165    : severity_(severity) {
166  Init(file, line);
167  stream_ << "Check failed: " << *result;
168  delete result;
169}
170
171LogMessage::~LogMessage() {
172  if (severity_ == LOG_FATAL) {
173    stream_ << std::endl;  // Newline to separate from log message.
174  }
175  stream_ << std::endl;
176  std::string str_newline(stream_.str());
177
178#if defined(OS_WIN)
179  OutputDebugStringA(str_newline.c_str());
180#endif
181  fwrite(str_newline.data(), str_newline.size(), 1, stderr);
182  fflush(stderr);
183
184  if (severity_ == LOG_FATAL) {
185    abort();
186  }
187}
188
189// writes the common header info to the stream
190void LogMessage::Init(const char* file, int line) {
191  std::string_view filename(file);
192  size_t last_slash_pos = filename.find_last_of("\\/");
193  if (last_slash_pos != std::string_view::npos)
194    filename.remove_prefix(last_slash_pos + 1);
195
196  // TODO(darin): It might be nice if the columns were fixed width.
197
198  stream_ << '[';
199  stream_ << std::this_thread::get_id() << ':';
200#if defined(OS_WIN)
201  SYSTEMTIME local_time;
202  GetLocalTime(&local_time);
203  stream_ << std::setfill('0') << std::setw(2) << local_time.wMonth
204          << std::setw(2) << local_time.wDay << '/' << std::setw(2)
205          << local_time.wHour << std::setw(2) << local_time.wMinute
206          << std::setw(2) << local_time.wSecond << '.' << std::setw(3)
207          << local_time.wMilliseconds << ':';
208#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
209  timeval tv;
210  gettimeofday(&tv, nullptr);
211  time_t t = tv.tv_sec;
212  struct tm local_time;
213  localtime_r(&t, &local_time);
214  struct tm* tm_time = &local_time;
215  stream_ << std::setfill('0') << std::setw(2) << 1 + tm_time->tm_mon
216          << std::setw(2) << tm_time->tm_mday << '/' << std::setw(2)
217          << tm_time->tm_hour << std::setw(2) << tm_time->tm_min << std::setw(2)
218          << tm_time->tm_sec << '.' << std::setw(6) << tv.tv_usec << ':';
219#else
220#error Unsupported platform
221#endif
222  if (severity_ >= 0)
223    stream_ << log_severity_name(severity_);
224  else
225    stream_ << "VERBOSE" << -severity_;
226
227  stream_ << ":" << filename << "(" << line << ")] ";
228
229  message_start_ = stream_.str().length();
230}
231
232#if defined(OS_WIN)
233// This has already been defined in the header, but defining it again as DWORD
234// ensures that the type used in the header is equivalent to DWORD. If not,
235// the redefinition is a compile error.
236typedef DWORD SystemErrorCode;
237#endif
238
239SystemErrorCode GetLastSystemErrorCode() {
240#if defined(OS_WIN)
241  return ::GetLastError();
242#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
243  return errno;
244#endif
245}
246
247std::string SystemErrorCodeToString(SystemErrorCode error_code) {
248#if defined(OS_WIN)
249  const int kErrorMessageBufferSize = 256;
250  char msgbuf[kErrorMessageBufferSize];
251  DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
252  DWORD len = FormatMessageA(flags, nullptr, error_code, 0, msgbuf,
253                             std::size(msgbuf), nullptr);
254  if (len) {
255    // Messages returned by system end with line breaks.
256    return base::CollapseWhitespaceASCII(msgbuf, true) +
257           base::StringPrintf(" (0x%lX)", error_code);
258  }
259  return base::StringPrintf("Error (0x%lX) while retrieving error. (0x%lX)",
260                            GetLastError(), error_code);
261#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
262  return base::safe_strerror(error_code) +
263         base::StringPrintf(" (%d)", error_code);
264#endif  // defined(OS_WIN)
265}
266
267#if defined(OS_WIN)
268Win32ErrorLogMessage::Win32ErrorLogMessage(const char* file,
269                                           int line,
270                                           LogSeverity severity,
271                                           SystemErrorCode err)
272    : err_(err), log_message_(file, line, severity) {}
273
274Win32ErrorLogMessage::~Win32ErrorLogMessage() {
275  stream() << ": " << SystemErrorCodeToString(err_);
276}
277#elif defined(OS_POSIX) || defined(OS_FUCHSIA)
278ErrnoLogMessage::ErrnoLogMessage(const char* file,
279                                 int line,
280                                 LogSeverity severity,
281                                 SystemErrorCode err)
282    : err_(err), log_message_(file, line, severity) {}
283
284ErrnoLogMessage::~ErrnoLogMessage() {
285  stream() << ": " << SystemErrorCodeToString(err_);
286}
287#endif  // defined(OS_WIN)
288
289void RawLog(int level, const char* message) {
290  if (level >= g_min_log_level && message) {
291    size_t bytes_written = 0;
292    const size_t message_len = strlen(message);
293    int rv;
294    while (bytes_written < message_len) {
295      rv = HANDLE_EINTR(write(STDERR_FILENO, message + bytes_written,
296                              message_len - bytes_written));
297      if (rv < 0) {
298        // Give up, nothing we can do now.
299        break;
300      }
301      bytes_written += rv;
302    }
303
304    if (message_len > 0 && message[message_len - 1] != '\n') {
305      do {
306        rv = HANDLE_EINTR(write(STDERR_FILENO, "\n", 1));
307        if (rv < 0) {
308          // Give up, nothing we can do now.
309          break;
310        }
311      } while (rv != 1);
312    }
313  }
314
315  if (level == LOG_FATAL)
316    abort();
317}
318
319// This was defined at the beginning of this file.
320#undef write
321
322void LogErrorNotReached(const char* file, int line) {
323  LogMessage(file, line, LOG_ERROR).stream() << "NOTREACHED() hit.";
324}
325
326}  // namespace logging
327