1 | // Copyright 2017 The Abseil Authors. |
2 | // |
3 | // Licensed under the Apache License, Version 2.0 (the "License"); |
4 | // you may not use this file except in compliance with the License. |
5 | // You may obtain a copy of the License at |
6 | // |
7 | // https://www.apache.org/licenses/LICENSE-2.0 |
8 | // |
9 | // Unless required by applicable law or agreed to in writing, software |
10 | // distributed under the License is distributed on an "AS IS" BASIS, |
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
12 | // See the License for the specific language governing permissions and |
13 | // limitations under the License. |
14 | |
15 | #include "absl/base/internal/raw_logging.h" |
16 | |
17 | #include <stddef.h> |
18 | #include <cstdarg> |
19 | #include <cstdio> |
20 | #include <cstdlib> |
21 | #include <cstring> |
22 | |
23 | #include "absl/base/attributes.h" |
24 | #include "absl/base/config.h" |
25 | #include "absl/base/internal/atomic_hook.h" |
26 | #include "absl/base/log_severity.h" |
27 | |
28 | // We know how to perform low-level writes to stderr in POSIX and Windows. For |
29 | // these platforms, we define the token ABSL_LOW_LEVEL_WRITE_SUPPORTED. |
30 | // Much of raw_logging.cc becomes a no-op when we can't output messages, |
31 | // although a FATAL ABSL_RAW_LOG message will still abort the process. |
32 | |
33 | // ABSL_HAVE_POSIX_WRITE is defined when the platform provides posix write() |
34 | // (as from unistd.h) |
35 | // |
36 | // This preprocessor token is also defined in raw_io.cc. If you need to copy |
37 | // this, consider moving both to config.h instead. |
38 | #if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ |
39 | defined(__Fuchsia__) || defined(__native_client__) || \ |
40 | defined(__EMSCRIPTEN__) |
41 | #include <unistd.h> |
42 | |
43 | |
44 | #define ABSL_HAVE_POSIX_WRITE 1 |
45 | #define ABSL_LOW_LEVEL_WRITE_SUPPORTED 1 |
46 | #else |
47 | #undef ABSL_HAVE_POSIX_WRITE |
48 | #endif |
49 | |
50 | // ABSL_HAVE_SYSCALL_WRITE is defined when the platform provides the syscall |
51 | // syscall(SYS_write, /*int*/ fd, /*char* */ buf, /*size_t*/ len); |
52 | // for low level operations that want to avoid libc. |
53 | #if (defined(__linux__) || defined(__FreeBSD__)) && !defined(__ANDROID__) |
54 | #include <sys/syscall.h> |
55 | #define ABSL_HAVE_SYSCALL_WRITE 1 |
56 | #define ABSL_LOW_LEVEL_WRITE_SUPPORTED 1 |
57 | #else |
58 | #undef ABSL_HAVE_SYSCALL_WRITE |
59 | #endif |
60 | |
61 | #ifdef _WIN32 |
62 | #include <io.h> |
63 | |
64 | #define ABSL_HAVE_RAW_IO 1 |
65 | #define ABSL_LOW_LEVEL_WRITE_SUPPORTED 1 |
66 | #else |
67 | #undef ABSL_HAVE_RAW_IO |
68 | #endif |
69 | |
70 | // TODO(gfalcon): We want raw-logging to work on as many platforms as possible. |
71 | // Explicitly #error out when not ABSL_LOW_LEVEL_WRITE_SUPPORTED, except for a |
72 | // whitelisted set of platforms for which we expect not to be able to raw log. |
73 | |
74 | ABSL_CONST_INIT static absl::base_internal::AtomicHook< |
75 | absl::raw_logging_internal::LogPrefixHook> log_prefix_hook; |
76 | ABSL_CONST_INIT static absl::base_internal::AtomicHook< |
77 | absl::raw_logging_internal::AbortHook> abort_hook; |
78 | |
79 | #ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED |
80 | static const char kTruncated[] = " ... (message truncated)\n" ; |
81 | |
82 | // sprintf the format to the buffer, adjusting *buf and *size to reflect the |
83 | // consumed bytes, and return whether the message fit without truncation. If |
84 | // truncation occurred, if possible leave room in the buffer for the message |
85 | // kTruncated[]. |
86 | inline static bool VADoRawLog(char** buf, int* size, const char* format, |
87 | va_list ap) ABSL_PRINTF_ATTRIBUTE(3, 0); |
88 | inline static bool VADoRawLog(char** buf, int* size, |
89 | const char* format, va_list ap) { |
90 | int n = vsnprintf(*buf, *size, format, ap); |
91 | bool result = true; |
92 | if (n < 0 || n > *size) { |
93 | result = false; |
94 | if (static_cast<size_t>(*size) > sizeof(kTruncated)) { |
95 | n = *size - sizeof(kTruncated); // room for truncation message |
96 | } else { |
97 | n = 0; // no room for truncation message |
98 | } |
99 | } |
100 | *size -= n; |
101 | *buf += n; |
102 | return result; |
103 | } |
104 | #endif // ABSL_LOW_LEVEL_WRITE_SUPPORTED |
105 | |
106 | static constexpr int kLogBufSize = 3000; |
107 | |
108 | namespace { |
109 | |
110 | // CAVEAT: vsnprintf called from *DoRawLog below has some (exotic) code paths |
111 | // that invoke malloc() and getenv() that might acquire some locks. |
112 | |
113 | // Helper for RawLog below. |
114 | // *DoRawLog writes to *buf of *size and move them past the written portion. |
115 | // It returns true iff there was no overflow or error. |
116 | bool DoRawLog(char** buf, int* size, const char* format, ...) |
117 | ABSL_PRINTF_ATTRIBUTE(3, 4); |
118 | bool DoRawLog(char** buf, int* size, const char* format, ...) { |
119 | va_list ap; |
120 | va_start(ap, format); |
121 | int n = vsnprintf(*buf, *size, format, ap); |
122 | va_end(ap); |
123 | if (n < 0 || n > *size) return false; |
124 | *size -= n; |
125 | *buf += n; |
126 | return true; |
127 | } |
128 | |
129 | void RawLogVA(absl::LogSeverity severity, const char* file, int line, |
130 | const char* format, va_list ap) ABSL_PRINTF_ATTRIBUTE(4, 0); |
131 | void RawLogVA(absl::LogSeverity severity, const char* file, int line, |
132 | const char* format, va_list ap) { |
133 | char buffer[kLogBufSize]; |
134 | char* buf = buffer; |
135 | int size = sizeof(buffer); |
136 | #ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED |
137 | bool enabled = true; |
138 | #else |
139 | bool enabled = false; |
140 | #endif |
141 | |
142 | #ifdef ABSL_MIN_LOG_LEVEL |
143 | if (severity < static_cast<absl::LogSeverity>(ABSL_MIN_LOG_LEVEL) && |
144 | severity < absl::LogSeverity::kFatal) { |
145 | enabled = false; |
146 | } |
147 | #endif |
148 | |
149 | auto log_prefix_hook_ptr = log_prefix_hook.Load(); |
150 | if (log_prefix_hook_ptr) { |
151 | enabled = log_prefix_hook_ptr(severity, file, line, &buf, &size); |
152 | } else { |
153 | if (enabled) { |
154 | DoRawLog(&buf, &size, "[%s : %d] RAW: " , file, line); |
155 | } |
156 | } |
157 | const char* const prefix_end = buf; |
158 | |
159 | #ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED |
160 | if (enabled) { |
161 | bool no_chop = VADoRawLog(&buf, &size, format, ap); |
162 | if (no_chop) { |
163 | DoRawLog(&buf, &size, "\n" ); |
164 | } else { |
165 | DoRawLog(&buf, &size, "%s" , kTruncated); |
166 | } |
167 | absl::raw_logging_internal::SafeWriteToStderr(buffer, strlen(buffer)); |
168 | } |
169 | #else |
170 | static_cast<void>(format); |
171 | static_cast<void>(ap); |
172 | #endif |
173 | |
174 | // Abort the process after logging a FATAL message, even if the output itself |
175 | // was suppressed. |
176 | if (severity == absl::LogSeverity::kFatal) { |
177 | abort_hook(file, line, buffer, prefix_end, buffer + kLogBufSize); |
178 | abort(); |
179 | } |
180 | } |
181 | |
182 | } // namespace |
183 | |
184 | namespace absl { |
185 | namespace raw_logging_internal { |
186 | void SafeWriteToStderr(const char *s, size_t len) { |
187 | #if defined(ABSL_HAVE_SYSCALL_WRITE) |
188 | syscall(SYS_write, STDERR_FILENO, s, len); |
189 | #elif defined(ABSL_HAVE_POSIX_WRITE) |
190 | write(STDERR_FILENO, s, len); |
191 | #elif defined(ABSL_HAVE_RAW_IO) |
192 | _write(/* stderr */ 2, s, len); |
193 | #else |
194 | // stderr logging unsupported on this platform |
195 | (void) s; |
196 | (void) len; |
197 | #endif |
198 | } |
199 | |
200 | void RawLog(absl::LogSeverity severity, const char* file, int line, |
201 | const char* format, ...) ABSL_PRINTF_ATTRIBUTE(4, 5); |
202 | void RawLog(absl::LogSeverity severity, const char* file, int line, |
203 | const char* format, ...) { |
204 | va_list ap; |
205 | va_start(ap, format); |
206 | RawLogVA(severity, file, line, format, ap); |
207 | va_end(ap); |
208 | } |
209 | |
210 | // Non-formatting version of RawLog(). |
211 | // |
212 | // TODO(gfalcon): When string_view no longer depends on base, change this |
213 | // interface to take its message as a string_view instead. |
214 | static void DefaultInternalLog(absl::LogSeverity severity, const char* file, |
215 | int line, const std::string& message) { |
216 | RawLog(severity, file, line, "%s" , message.c_str()); |
217 | } |
218 | |
219 | bool RawLoggingFullySupported() { |
220 | #ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED |
221 | return true; |
222 | #else // !ABSL_LOW_LEVEL_WRITE_SUPPORTED |
223 | return false; |
224 | #endif // !ABSL_LOW_LEVEL_WRITE_SUPPORTED |
225 | } |
226 | |
227 | ABSL_CONST_INIT absl::base_internal::AtomicHook<InternalLogFunction> |
228 | internal_log_function(DefaultInternalLog); |
229 | |
230 | void RegisterInternalLogFunction(InternalLogFunction func) { |
231 | internal_log_function.Store(func); |
232 | } |
233 | |
234 | } // namespace raw_logging_internal |
235 | } // namespace absl |
236 | |