1 // Copyright 2012 The Chromium Authors
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 "partition_alloc/partition_alloc_base/log_message.h"
6 
7 // TODO(1151236): After finishing copying //base files to PA library, remove
8 // defined(BASE_CHECK_H_) from here.
9 #if defined(                                                                                 \
10     BASE_ALLOCATOR_PARTITION_ALLOCATOR_SRC_PARTITION_ALLOC_PARTITION_ALLOC_BASE_CHECK_H_) || \
11     defined(BASE_CHECK_H_) ||                                                                \
12     defined(                                                                                 \
13         BASE_ALLOCATOR_PARTITION_ALLOCATOR_SRC_PARTITION_ALLOC_PARTITION_ALLOC_CHECK_H_)
14 #error "log_message.h should not include check.h"
15 #endif
16 
17 #include "build/build_config.h"
18 #include "partition_alloc/partition_alloc_base/component_export.h"
19 #include "partition_alloc/partition_alloc_base/debug/alias.h"
20 #include "partition_alloc/partition_alloc_base/debug/stack_trace.h"
21 #include "partition_alloc/partition_alloc_base/immediate_crash.h"
22 #include "partition_alloc/partition_alloc_base/logging.h"
23 #include "partition_alloc/partition_alloc_base/strings/safe_sprintf.h"
24 #include "partition_alloc/partition_alloc_base/strings/string_util.h"
25 #include "partition_alloc/partition_alloc_base/strings/stringprintf.h"
26 
27 #if BUILDFLAG(IS_WIN)
28 #include <windows.h>
29 
30 #include <io.h>
31 #endif
32 
33 #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
34 #include <unistd.h>
35 
36 #include <cerrno>
37 #include <cstdio>
38 #include <cstdlib>
39 #include <cstring>
40 #endif
41 
42 #if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
43 #include "partition_alloc/partition_alloc_base/posix/safe_strerror.h"
44 #endif
45 
46 namespace partition_alloc::internal::logging {
47 
48 namespace {
49 
50 const char* const log_severity_names[] = {"INFO", "WARNING", "ERROR", "FATAL"};
51 static_assert(LOGGING_NUM_SEVERITIES == std::size(log_severity_names),
52               "Incorrect number of log_severity_names");
53 
log_severity_name(int severity)54 const char* log_severity_name(int severity) {
55   if (severity >= 0 && severity < LOGGING_NUM_SEVERITIES) {
56     return log_severity_names[severity];
57   }
58   return "UNKNOWN";
59 }
60 
61 // A log message handler that gets notified of every log message we process.
62 LogMessageHandlerFunction g_log_message_handler = nullptr;
63 
64 }  // namespace
65 
66 #if BUILDFLAG(PA_DCHECK_IS_CONFIGURABLE)
67 // In DCHECK-enabled Chrome builds, allow the meaning of LOGGING_DCHECK to be
68 // determined at run-time. We default it to ERROR, to avoid it triggering
69 // crashes before the run-time has explicitly chosen the behaviour.
70 PA_COMPONENT_EXPORT(PARTITION_ALLOC_BASE)
71 logging::LogSeverity LOGGING_DCHECK = LOGGING_ERROR;
72 #endif  // BUILDFLAG(PA_DCHECK_IS_CONFIGURABLE)
73 
74 // This is never instantiated, it's just used for EAT_STREAM_PARAMETERS to have
75 // an object of the correct type on the LHS of the unused part of the ternary
76 // operator.
77 base::strings::CStringBuilder* g_swallow_stream;
78 
SetLogMessageHandler(LogMessageHandlerFunction handler)79 void SetLogMessageHandler(LogMessageHandlerFunction handler) {
80   g_log_message_handler = handler;
81 }
82 
GetLogMessageHandler()83 LogMessageHandlerFunction GetLogMessageHandler() {
84   return g_log_message_handler;
85 }
86 
LogMessage(const char * file,int line,LogSeverity severity)87 LogMessage::LogMessage(const char* file, int line, LogSeverity severity)
88     : severity_(severity), file_(file), line_(line) {
89   Init(file, line);
90 }
91 
LogMessage(const char * file,int line,const char * condition)92 LogMessage::LogMessage(const char* file, int line, const char* condition)
93     : severity_(LOGGING_FATAL), file_(file), line_(line) {
94   Init(file, line);
95   stream_ << "Check failed: " << condition << ". ";
96 }
97 
~LogMessage()98 LogMessage::~LogMessage() {
99   stream_ << '\n';
100   const char* str_newline = stream_.c_str();
101 
102   // Give any log message handler first dibs on the message.
103   if (g_log_message_handler &&
104       g_log_message_handler(severity_, file_, line_, message_start_,
105                             str_newline)) {
106     // The handler took care of it, no further processing.
107     return;
108   }
109 
110   // Always use RawLog() if g_log_message_handler doesn't filter messages.
111   RawLog(severity_, str_newline);
112 
113   // TODO(1293552): Enable a stack trace on a fatal on fuchsia.
114 #if !defined(OFFICIAL_BUILD) && (BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_WIN)) && \
115     !defined(__UCLIBC__) && !BUILDFLAG(IS_AIX)
116   // TODO(1293552): Show a stack trace on a fatal, unless a debugger is
117   // attached.
118   if (severity_ == LOGGING_FATAL) {
119     constexpr size_t kMaxTracesOfLoggingFatal = 32u;
120     const void* traces[kMaxTracesOfLoggingFatal];
121     size_t num_traces =
122         base::debug::CollectStackTrace(traces, kMaxTracesOfLoggingFatal);
123     base::debug::PrintStackTrace(traces, num_traces);
124   }
125 #endif
126 
127   if (severity_ == LOGGING_FATAL) {
128     PA_IMMEDIATE_CRASH();
129   }
130 }
131 
132 // writes the common header info to the stream
Init(const char * file,int line)133 void LogMessage::Init(const char* file, int line) {
134   const char* last_slash_pos = base::strings::FindLastOf(file, "\\/");
135   const char* filename = last_slash_pos ? last_slash_pos + 1 : file;
136 
137   {
138     // TODO(darin): It might be nice if the columns were fixed width.
139     stream_ << '[';
140     // TODO(1151236): show process id, thread id, timestamp and so on
141     // if needed.
142     if (severity_ >= 0) {
143       stream_ << log_severity_name(severity_);
144     } else {
145       stream_ << "VERBOSE" << -severity_;
146     }
147     stream_ << ":" << filename << "(" << line << ")] ";
148   }
149   message_start_ = strlen(stream_.c_str());
150 }
151 
152 #if BUILDFLAG(IS_WIN)
153 // This has already been defined in the header, but defining it again as DWORD
154 // ensures that the type used in the header is equivalent to DWORD. If not,
155 // the redefinition is a compile error.
156 typedef DWORD SystemErrorCode;
157 #endif
158 
GetLastSystemErrorCode()159 SystemErrorCode GetLastSystemErrorCode() {
160 #if BUILDFLAG(IS_WIN)
161   return ::GetLastError();
162 #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
163   return errno;
164 #endif
165 }
166 
SystemErrorCodeToStream(base::strings::CStringBuilder & os,SystemErrorCode error_code)167 void SystemErrorCodeToStream(base::strings::CStringBuilder& os,
168                              SystemErrorCode error_code) {
169   char buffer[256];
170 #if BUILDFLAG(IS_WIN)
171   const int kErrorMessageBufferSize = 256;
172   char msgbuf[kErrorMessageBufferSize];
173   DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS;
174   DWORD len = FormatMessageA(flags, nullptr, error_code, 0, msgbuf,
175                              std::size(msgbuf), nullptr);
176   if (len) {
177     // Messages returned by system end with line breaks.
178     const char* whitespace_pos = base::strings::FindLastNotOf(msgbuf, "\n\r ");
179     if (whitespace_pos) {
180       size_t whitespace_index = whitespace_pos - msgbuf + 1;
181       msgbuf[whitespace_index] = '\0';
182     }
183     base::strings::SafeSPrintf(buffer, "%s (0x%x)", msgbuf, error_code);
184     os << buffer;
185     return;
186   }
187   base::strings::SafeSPrintf(buffer,
188                              "Error (0x%x) while retrieving error. (0x%x)",
189                              GetLastError(), error_code);
190   os << buffer;
191 #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
192   base::safe_strerror_r(error_code, buffer, sizeof(buffer));
193   os << buffer << " (" << error_code << ")";
194 #endif  // BUILDFLAG(IS_WIN)
195 }
196 
197 #if BUILDFLAG(IS_WIN)
Win32ErrorLogMessage(const char * file,int line,LogSeverity severity,SystemErrorCode err)198 Win32ErrorLogMessage::Win32ErrorLogMessage(const char* file,
199                                            int line,
200                                            LogSeverity severity,
201                                            SystemErrorCode err)
202     : LogMessage(file, line, severity), err_(err) {}
203 
~Win32ErrorLogMessage()204 Win32ErrorLogMessage::~Win32ErrorLogMessage() {
205   stream() << ": ";
206   SystemErrorCodeToStream(stream(), err_);
207   // We're about to crash (CHECK). Put |err_| on the stack (by placing it in a
208   // field) and use Alias in hopes that it makes it into crash dumps.
209   DWORD last_error = err_;
210   base::debug::Alias(&last_error);
211 }
212 #elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
ErrnoLogMessage(const char * file,int line,LogSeverity severity,SystemErrorCode err)213 ErrnoLogMessage::ErrnoLogMessage(const char* file,
214                                  int line,
215                                  LogSeverity severity,
216                                  SystemErrorCode err)
217     : LogMessage(file, line, severity), err_(err) {}
218 
~ErrnoLogMessage()219 ErrnoLogMessage::~ErrnoLogMessage() {
220   stream() << ": ";
221   SystemErrorCodeToStream(stream(), err_);
222   // We're about to crash (CHECK). Put |err_| on the stack (by placing it in a
223   // field) and use Alias in hopes that it makes it into crash dumps.
224   int last_error = err_;
225   base::debug::Alias(&last_error);
226 }
227 #endif  // BUILDFLAG(IS_WIN)
228 
229 }  // namespace partition_alloc::internal::logging
230