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