raw_logging.cc (8972B)
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 <cstdarg> 18 #include <cstddef> 19 #include <cstdio> 20 #include <cstdlib> 21 #include <cstring> 22 #include <string> 23 24 #ifdef __EMSCRIPTEN__ 25 #include <emscripten/console.h> 26 #endif 27 28 #include "absl/base/attributes.h" 29 #include "absl/base/config.h" 30 #include "absl/base/internal/atomic_hook.h" 31 #include "absl/base/internal/errno_saver.h" 32 #include "absl/base/log_severity.h" 33 34 // We know how to perform low-level writes to stderr in POSIX and Windows. For 35 // these platforms, we define the token ABSL_LOW_LEVEL_WRITE_SUPPORTED. 36 // Much of raw_logging.cc becomes a no-op when we can't output messages, 37 // although a FATAL ABSL_RAW_LOG message will still abort the process. 38 39 // ABSL_HAVE_POSIX_WRITE is defined when the platform provides posix write() 40 // (as from unistd.h) 41 // 42 // This preprocessor token is also defined in raw_io.cc. If you need to copy 43 // this, consider moving both to config.h instead. 44 #if defined(__linux__) || defined(__APPLE__) || defined(__FreeBSD__) || \ 45 defined(__hexagon__) || defined(__Fuchsia__) || \ 46 defined(__native_client__) || defined(__OpenBSD__) || \ 47 defined(__EMSCRIPTEN__) || defined(__ASYLO__) 48 49 #include <unistd.h> 50 51 #define ABSL_HAVE_POSIX_WRITE 1 52 #define ABSL_LOW_LEVEL_WRITE_SUPPORTED 1 53 #else 54 #undef ABSL_HAVE_POSIX_WRITE 55 #endif 56 57 // ABSL_HAVE_SYSCALL_WRITE is defined when the platform provides the syscall 58 // syscall(SYS_write, /*int*/ fd, /*char* */ buf, /*size_t*/ len); 59 // for low level operations that want to avoid libc. 60 #if (defined(__linux__) || defined(__FreeBSD__)) && !defined(__ANDROID__) 61 #include <sys/syscall.h> 62 #define ABSL_HAVE_SYSCALL_WRITE 1 63 #define ABSL_LOW_LEVEL_WRITE_SUPPORTED 1 64 #else 65 #undef ABSL_HAVE_SYSCALL_WRITE 66 #endif 67 68 #ifdef _WIN32 69 #include <io.h> 70 71 #define ABSL_HAVE_RAW_IO 1 72 #define ABSL_LOW_LEVEL_WRITE_SUPPORTED 1 73 #else 74 #undef ABSL_HAVE_RAW_IO 75 #endif 76 77 namespace absl { 78 ABSL_NAMESPACE_BEGIN 79 namespace raw_log_internal { 80 namespace { 81 82 // TODO(gfalcon): We want raw-logging to work on as many platforms as possible. 83 // Explicitly `#error` out when not `ABSL_LOW_LEVEL_WRITE_SUPPORTED`, except for 84 // a selected set of platforms for which we expect not to be able to raw log. 85 86 #ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED 87 constexpr char kTruncated[] = " ... (message truncated)\n"; 88 89 // sprintf the format to the buffer, adjusting *buf and *size to reflect the 90 // consumed bytes, and return whether the message fit without truncation. If 91 // truncation occurred, if possible leave room in the buffer for the message 92 // kTruncated[]. 93 bool VADoRawLog(char** buf, int* size, const char* format, va_list ap) 94 ABSL_PRINTF_ATTRIBUTE(3, 0); 95 bool VADoRawLog(char** buf, int* size, const char* format, va_list ap) { 96 if (*size < 0) return false; 97 int n = vsnprintf(*buf, static_cast<size_t>(*size), format, ap); 98 bool result = true; 99 if (n < 0 || n > *size) { 100 result = false; 101 if (static_cast<size_t>(*size) > sizeof(kTruncated)) { 102 n = *size - static_cast<int>(sizeof(kTruncated)); 103 } else { 104 n = 0; // no room for truncation message 105 } 106 } 107 *size -= n; 108 *buf += n; 109 return result; 110 } 111 #endif // ABSL_LOW_LEVEL_WRITE_SUPPORTED 112 113 constexpr int kLogBufSize = 3000; 114 115 // CAVEAT: vsnprintf called from *DoRawLog below has some (exotic) code paths 116 // that invoke malloc() and getenv() that might acquire some locks. 117 118 // Helper for RawLog below. 119 // *DoRawLog writes to *buf of *size and move them past the written portion. 120 // It returns true iff there was no overflow or error. 121 bool DoRawLog(char** buf, int* size, const char* format, ...) 122 ABSL_PRINTF_ATTRIBUTE(3, 4); 123 bool DoRawLog(char** buf, int* size, const char* format, ...) { 124 if (*size < 0) return false; 125 va_list ap; 126 va_start(ap, format); 127 int n = vsnprintf(*buf, static_cast<size_t>(*size), format, ap); 128 va_end(ap); 129 if (n < 0 || n > *size) return false; 130 *size -= n; 131 *buf += n; 132 return true; 133 } 134 135 bool DefaultLogFilterAndPrefix(absl::LogSeverity, const char* file, int line, 136 char** buf, int* buf_size) { 137 DoRawLog(buf, buf_size, "[%s : %d] RAW: ", file, line); 138 return true; 139 } 140 141 ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES 142 absl::base_internal::AtomicHook<LogFilterAndPrefixHook> 143 log_filter_and_prefix_hook(DefaultLogFilterAndPrefix); 144 ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES 145 absl::base_internal::AtomicHook<AbortHook> abort_hook; 146 147 void RawLogVA(absl::LogSeverity severity, const char* file, int line, 148 const char* format, va_list ap) ABSL_PRINTF_ATTRIBUTE(4, 0); 149 void RawLogVA(absl::LogSeverity severity, const char* file, int line, 150 const char* format, va_list ap) { 151 char buffer[kLogBufSize]; 152 char* buf = buffer; 153 int size = sizeof(buffer); 154 #ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED 155 bool enabled = true; 156 #else 157 bool enabled = false; 158 #endif 159 160 #ifdef ABSL_MIN_LOG_LEVEL 161 if (severity < static_cast<absl::LogSeverity>(ABSL_MIN_LOG_LEVEL) && 162 severity < absl::LogSeverity::kFatal) { 163 enabled = false; 164 } 165 #endif 166 167 enabled = log_filter_and_prefix_hook(severity, file, line, &buf, &size); 168 const char* const prefix_end = buf; 169 170 #ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED 171 if (enabled) { 172 bool no_chop = VADoRawLog(&buf, &size, format, ap); 173 if (no_chop) { 174 DoRawLog(&buf, &size, "\n"); 175 } else { 176 DoRawLog(&buf, &size, "%s", kTruncated); 177 } 178 AsyncSignalSafeWriteError(buffer, static_cast<size_t>(buf - buffer)); 179 } 180 #else 181 static_cast<void>(format); 182 static_cast<void>(ap); 183 static_cast<void>(enabled); 184 #endif 185 186 // Abort the process after logging a FATAL message, even if the output itself 187 // was suppressed. 188 if (severity == absl::LogSeverity::kFatal) { 189 abort_hook(file, line, buffer, prefix_end, buffer + kLogBufSize); 190 abort(); 191 } 192 } 193 194 // Non-formatting version of RawLog(). 195 // 196 // TODO(gfalcon): When string_view no longer depends on base, change this 197 // interface to take its message as a string_view instead. 198 void DefaultInternalLog(absl::LogSeverity severity, const char* file, int line, 199 const std::string& message) { 200 RawLog(severity, file, line, "%.*s", static_cast<int>(message.size()), 201 message.data()); 202 } 203 204 } // namespace 205 206 void AsyncSignalSafeWriteError(const char* s, size_t len) { 207 if (!len) return; 208 absl::base_internal::ErrnoSaver errno_saver; 209 #if defined(__EMSCRIPTEN__) 210 // In WebAssembly, bypass filesystem emulation via fwrite. 211 if (s[len - 1] == '\n') { 212 // Skip a trailing newline character as emscripten_errn adds one itself. 213 len--; 214 } 215 // emscripten_errn was introduced in 3.1.41 but broken in standalone mode 216 // until 3.1.43. 217 #if ABSL_INTERNAL_EMSCRIPTEN_VERSION >= 3001043 218 emscripten_errn(s, len); 219 #else 220 char buf[kLogBufSize]; 221 if (len >= kLogBufSize) { 222 len = kLogBufSize - 1; 223 constexpr size_t trunc_len = sizeof(kTruncated) - 2; 224 memcpy(buf + len - trunc_len, kTruncated, trunc_len); 225 buf[len] = '\0'; 226 len -= trunc_len; 227 } else { 228 buf[len] = '\0'; 229 } 230 memcpy(buf, s, len); 231 _emscripten_err(buf); 232 #endif 233 #elif defined(ABSL_HAVE_SYSCALL_WRITE) 234 // We prefer calling write via `syscall` to minimize the risk of libc doing 235 // something "helpful". 236 syscall(SYS_write, STDERR_FILENO, s, len); 237 #elif defined(ABSL_HAVE_POSIX_WRITE) 238 write(STDERR_FILENO, s, len); 239 #elif defined(ABSL_HAVE_RAW_IO) 240 _write(/* stderr */ 2, s, static_cast<unsigned>(len)); 241 #else 242 // stderr logging unsupported on this platform 243 (void)s; 244 (void)len; 245 #endif 246 } 247 248 void RawLog(absl::LogSeverity severity, const char* file, int line, 249 const char* format, ...) { 250 va_list ap; 251 va_start(ap, format); 252 RawLogVA(severity, file, line, format, ap); 253 va_end(ap); 254 } 255 256 bool RawLoggingFullySupported() { 257 #ifdef ABSL_LOW_LEVEL_WRITE_SUPPORTED 258 return true; 259 #else // !ABSL_LOW_LEVEL_WRITE_SUPPORTED 260 return false; 261 #endif // !ABSL_LOW_LEVEL_WRITE_SUPPORTED 262 } 263 264 ABSL_INTERNAL_ATOMIC_HOOK_ATTRIBUTES ABSL_DLL 265 absl::base_internal::AtomicHook<InternalLogFunction> 266 internal_log_function(DefaultInternalLog); 267 268 void RegisterLogFilterAndPrefixHook(LogFilterAndPrefixHook func) { 269 log_filter_and_prefix_hook.Store(func); 270 } 271 272 void RegisterAbortHook(AbortHook func) { abort_hook.Store(func); } 273 274 void RegisterInternalLogFunction(InternalLogFunction func) { 275 internal_log_function.Store(func); 276 } 277 278 } // namespace raw_log_internal 279 ABSL_NAMESPACE_END 280 } // namespace absl