| 1 | /* |
|---|
| 2 | * Copyright (C) 2003-2021 Apple Inc. All rights reserved. |
|---|
| 3 | * Copyright (C) 2007-2009 Torch Mobile, Inc. |
|---|
| 4 | * Copyright (C) 2011 University of Szeged. All rights reserved. |
|---|
| 5 | * |
|---|
| 6 | * Redistribution and use in source and binary forms, with or without |
|---|
| 7 | * modification, are permitted provided that the following conditions |
|---|
| 8 | * are met: |
|---|
| 9 | * 1. Redistributions of source code must retain the above copyright |
|---|
| 10 | * notice, this list of conditions and the following disclaimer. |
|---|
| 11 | * 2. Redistributions in binary form must reproduce the above copyright |
|---|
| 12 | * notice, this list of conditions and the following disclaimer in the |
|---|
| 13 | * documentation and/or other materials provided with the distribution. |
|---|
| 14 | * |
|---|
| 15 | * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY |
|---|
| 16 | * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
|---|
| 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
|---|
| 18 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR |
|---|
| 19 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
|---|
| 20 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
|---|
| 21 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
|---|
| 22 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY |
|---|
| 23 | * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
|---|
| 24 | * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
|---|
| 25 | * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
|---|
| 26 | */ |
|---|
| 27 | |
|---|
| 28 | #include "config.h" |
|---|
| 29 | #include <wtf/Assertions.h> |
|---|
| 30 | |
|---|
| 31 | #include <mutex> |
|---|
| 32 | #include <stdio.h> |
|---|
| 33 | #include <string.h> |
|---|
| 34 | #include <wtf/Compiler.h> |
|---|
| 35 | #include <wtf/Lock.h> |
|---|
| 36 | #include <wtf/Locker.h> |
|---|
| 37 | #include <wtf/LoggingAccumulator.h> |
|---|
| 38 | #include <wtf/PrintStream.h> |
|---|
| 39 | #include <wtf/StackTrace.h> |
|---|
| 40 | #include <wtf/WTFConfig.h> |
|---|
| 41 | #include <wtf/text/CString.h> |
|---|
| 42 | #include <wtf/text/StringBuilder.h> |
|---|
| 43 | #include <wtf/text/WTFString.h> |
|---|
| 44 | |
|---|
| 45 | #if USE(CF) |
|---|
| 46 | #include <CoreFoundation/CFString.h> |
|---|
| 47 | #endif // USE(CF) |
|---|
| 48 | |
|---|
| 49 | #if COMPILER(MSVC) |
|---|
| 50 | #include <crtdbg.h> |
|---|
| 51 | #endif |
|---|
| 52 | |
|---|
| 53 | #if OS(WINDOWS) |
|---|
| 54 | #include <windows.h> |
|---|
| 55 | #endif |
|---|
| 56 | |
|---|
| 57 | #if OS(DARWIN) |
|---|
| 58 | #include <sys/sysctl.h> |
|---|
| 59 | #include <unistd.h> |
|---|
| 60 | #endif |
|---|
| 61 | |
|---|
| 62 | #if USE(JOURNALD) |
|---|
| 63 | #include <wtf/StringPrintStream.h> |
|---|
| 64 | #endif |
|---|
| 65 | |
|---|
| 66 | #if PLATFORM(COCOA) |
|---|
| 67 | #import <wtf/spi/cocoa/OSLogSPI.h> |
|---|
| 68 | #endif |
|---|
| 69 | |
|---|
| 70 | namespace WTF { |
|---|
| 71 | |
|---|
| 72 | WTF_ATTRIBUTE_PRINTF(1, 0) static String createWithFormatAndArguments(const char* format, va_list args) |
|---|
| 73 | { |
|---|
| 74 | va_list argsCopy; |
|---|
| 75 | va_copy(argsCopy, args); |
|---|
| 76 | |
|---|
| 77 | ALLOW_NONLITERAL_FORMAT_BEGIN |
|---|
| 78 | |
|---|
| 79 | #if USE(CF) && !OS(WINDOWS) |
|---|
| 80 | if (strstr(format, "%@")) { |
|---|
| 81 | auto cfFormat = adoptCF(CFStringCreateWithCString(kCFAllocatorDefault, format, kCFStringEncodingUTF8)); |
|---|
| 82 | auto result = adoptCF(CFStringCreateWithFormatAndArguments(kCFAllocatorDefault, nullptr, cfFormat.get(), args)); |
|---|
| 83 | va_end(argsCopy); |
|---|
| 84 | return result.get(); |
|---|
| 85 | } |
|---|
| 86 | #endif |
|---|
| 87 | |
|---|
| 88 | // Do the format once to get the length. |
|---|
| 89 | #if COMPILER(MSVC) |
|---|
| 90 | int result = _vscprintf(format, args); |
|---|
| 91 | #else |
|---|
| 92 | char ch; |
|---|
| 93 | int result = vsnprintf(&ch, 1, format, args); |
|---|
| 94 | #endif |
|---|
| 95 | |
|---|
| 96 | if (!result) { |
|---|
| 97 | va_end(argsCopy); |
|---|
| 98 | return emptyString(); |
|---|
| 99 | } |
|---|
| 100 | if (result < 0) { |
|---|
| 101 | va_end(argsCopy); |
|---|
| 102 | return { }; |
|---|
| 103 | } |
|---|
| 104 | |
|---|
| 105 | Vector<char, 256> buffer; |
|---|
| 106 | unsigned length = result; |
|---|
| 107 | buffer.grow(length + 1); |
|---|
| 108 | |
|---|
| 109 | // Now do the formatting again, guaranteed to fit. |
|---|
| 110 | vsnprintf(buffer.data(), buffer.size(), format, argsCopy); |
|---|
| 111 | va_end(argsCopy); |
|---|
| 112 | |
|---|
| 113 | ALLOW_NONLITERAL_FORMAT_END |
|---|
| 114 | |
|---|
| 115 | return StringImpl::create(reinterpret_cast<const LChar*>(buffer.data()), length); |
|---|
| 116 | } |
|---|
| 117 | |
|---|
| 118 | #if PLATFORM(COCOA) |
|---|
| 119 | void disableForwardingVPrintfStdErrToOSLog() |
|---|
| 120 | { |
|---|
| 121 | g_wtfConfig.disableForwardingVPrintfStdErrToOSLog = true; |
|---|
| 122 | } |
|---|
| 123 | #endif |
|---|
| 124 | |
|---|
| 125 | } // namespace WTF |
|---|
| 126 | |
|---|
| 127 | extern "C" { |
|---|
| 128 | |
|---|
| 129 | static void logToStderr(const char* buffer) |
|---|
| 130 | { |
|---|
| 131 | #if PLATFORM(COCOA) |
|---|
| 132 | os_log(OS_LOG_DEFAULT, "%s", buffer); |
|---|
| 133 | #endif |
|---|
| 134 | fputs(buffer, stderr); |
|---|
| 135 | } |
|---|
| 136 | |
|---|
| 137 | WTF_ATTRIBUTE_PRINTF(1, 0) |
|---|
| 138 | static void vprintf_stderr_common(const char* format, va_list args) |
|---|
| 139 | { |
|---|
| 140 | #if USE(CF) && !OS(WINDOWS) |
|---|
| 141 | if (strstr(format, "%@")) { |
|---|
| 142 | auto cfFormat = adoptCF(CFStringCreateWithCString(nullptr, format, kCFStringEncodingUTF8)); |
|---|
| 143 | |
|---|
| 144 | ALLOW_NONLITERAL_FORMAT_BEGIN |
|---|
| 145 | auto str = adoptCF(CFStringCreateWithFormatAndArguments(nullptr, nullptr, cfFormat.get(), args)); |
|---|
| 146 | ALLOW_NONLITERAL_FORMAT_END |
|---|
| 147 | CFIndex length = CFStringGetMaximumSizeForEncoding(CFStringGetLength(str.get()), kCFStringEncodingUTF8); |
|---|
| 148 | constexpr unsigned InitialBufferSize { 256 }; |
|---|
| 149 | Vector<char, InitialBufferSize> buffer(length + 1); |
|---|
| 150 | |
|---|
| 151 | CFStringGetCString(str.get(), buffer.data(), length, kCFStringEncodingUTF8); |
|---|
| 152 | |
|---|
| 153 | logToStderr(buffer.data()); |
|---|
| 154 | return; |
|---|
| 155 | } |
|---|
| 156 | |
|---|
| 157 | #if PLATFORM(COCOA) |
|---|
| 158 | if (!g_wtfConfig.disableForwardingVPrintfStdErrToOSLog) { |
|---|
| 159 | va_list copyOfArgs; |
|---|
| 160 | va_copy(copyOfArgs, args); |
|---|
| 161 | os_log_with_args(OS_LOG_DEFAULT, OS_LOG_TYPE_DEFAULT, format, copyOfArgs, __builtin_return_address(0)); |
|---|
| 162 | va_end(copyOfArgs); |
|---|
| 163 | } |
|---|
| 164 | #endif |
|---|
| 165 | |
|---|
| 166 | // Fall through to write to stderr in the same manner as other platforms. |
|---|
| 167 | |
|---|
| 168 | #elif HAVE(ISDEBUGGERPRESENT) |
|---|
| 169 | if (IsDebuggerPresent()) { |
|---|
| 170 | size_t size = 1024; |
|---|
| 171 | Vector<char> buffer(size); |
|---|
| 172 | do { |
|---|
| 173 | buffer.grow(size); |
|---|
| 174 | if (vsnprintf(buffer.data(), size, format, args) != -1) { |
|---|
| 175 | OutputDebugStringA(buffer.data()); |
|---|
| 176 | break; |
|---|
| 177 | } |
|---|
| 178 | size *= 2; |
|---|
| 179 | } while (size > 1024); |
|---|
| 180 | } |
|---|
| 181 | #endif |
|---|
| 182 | vfprintf(stderr, format, args); |
|---|
| 183 | } |
|---|
| 184 | |
|---|
| 185 | ALLOW_NONLITERAL_FORMAT_BEGIN |
|---|
| 186 | |
|---|
| 187 | WTF_ATTRIBUTE_PRINTF(2, 0) |
|---|
| 188 | static void vprintf_stderr_with_prefix(const char* prefix, const char* format, va_list args) |
|---|
| 189 | { |
|---|
| 190 | size_t prefixLength = strlen(prefix); |
|---|
| 191 | size_t formatLength = strlen(format); |
|---|
| 192 | Vector<char> formatWithPrefix(prefixLength + formatLength + 1); |
|---|
| 193 | memcpy(formatWithPrefix.data(), prefix, prefixLength); |
|---|
| 194 | memcpy(formatWithPrefix.data() + prefixLength, format, formatLength); |
|---|
| 195 | formatWithPrefix[prefixLength + formatLength] = 0; |
|---|
| 196 | |
|---|
| 197 | vprintf_stderr_common(formatWithPrefix.data(), args); |
|---|
| 198 | } |
|---|
| 199 | |
|---|
| 200 | WTF_ATTRIBUTE_PRINTF(1, 0) |
|---|
| 201 | static void vprintf_stderr_with_trailing_newline(const char* format, va_list args) |
|---|
| 202 | { |
|---|
| 203 | size_t formatLength = strlen(format); |
|---|
| 204 | if (formatLength && format[formatLength - 1] == '\n') { |
|---|
| 205 | vprintf_stderr_common(format, args); |
|---|
| 206 | return; |
|---|
| 207 | } |
|---|
| 208 | |
|---|
| 209 | Vector<char> formatWithNewline(formatLength + 2); |
|---|
| 210 | memcpy(formatWithNewline.data(), format, formatLength); |
|---|
| 211 | formatWithNewline[formatLength] = '\n'; |
|---|
| 212 | formatWithNewline[formatLength + 1] = 0; |
|---|
| 213 | |
|---|
| 214 | vprintf_stderr_common(formatWithNewline.data(), args); |
|---|
| 215 | } |
|---|
| 216 | |
|---|
| 217 | ALLOW_NONLITERAL_FORMAT_END |
|---|
| 218 | |
|---|
| 219 | WTF_ATTRIBUTE_PRINTF(1, 2) |
|---|
| 220 | static void printf_stderr_common(const char* format, ...) |
|---|
| 221 | { |
|---|
| 222 | va_list args; |
|---|
| 223 | va_start(args, format); |
|---|
| 224 | vprintf_stderr_common(format, args); |
|---|
| 225 | va_end(args); |
|---|
| 226 | } |
|---|
| 227 | |
|---|
| 228 | static void printCallSite(const char* file, int line, const char* function) |
|---|
| 229 | { |
|---|
| 230 | #if OS(WINDOWS) && defined(_DEBUG) |
|---|
| 231 | _CrtDbgReport(_CRT_WARN, file, line, NULL, "%s\n", function); |
|---|
| 232 | #else |
|---|
| 233 | // By using this format, which matches the format used by MSVC for compiler errors, developers |
|---|
| 234 | // using Visual Studio can double-click the file/line number in the Output Window to have the |
|---|
| 235 | // editor navigate to that line of code. It seems fine for other developers, too. |
|---|
| 236 | printf_stderr_common("%s(%d) : %s\n", file, line, function); |
|---|
| 237 | #endif |
|---|
| 238 | } |
|---|
| 239 | |
|---|
| 240 | void WTFReportNotImplementedYet(const char* file, int line, const char* function) |
|---|
| 241 | { |
|---|
| 242 | printf_stderr_common("NOT IMPLEMENTED YET\n"); |
|---|
| 243 | printCallSite(file, line, function); |
|---|
| 244 | } |
|---|
| 245 | |
|---|
| 246 | void WTFReportAssertionFailure(const char* file, int line, const char* function, const char* assertion) |
|---|
| 247 | { |
|---|
| 248 | if (assertion) |
|---|
| 249 | printf_stderr_common("ASSERTION FAILED: %s\n", assertion); |
|---|
| 250 | else |
|---|
| 251 | printf_stderr_common("SHOULD NEVER BE REACHED\n"); |
|---|
| 252 | printCallSite(file, line, function); |
|---|
| 253 | } |
|---|
| 254 | |
|---|
| 255 | void WTFReportAssertionFailureWithMessage(const char* file, int line, const char* function, const char* assertion, const char* format, ...) |
|---|
| 256 | { |
|---|
| 257 | va_list args; |
|---|
| 258 | va_start(args, format); |
|---|
| 259 | vprintf_stderr_with_prefix("ASSERTION FAILED: ", format, args); |
|---|
| 260 | va_end(args); |
|---|
| 261 | printf_stderr_common("\n%s\n", assertion); |
|---|
| 262 | printCallSite(file, line, function); |
|---|
| 263 | } |
|---|
| 264 | |
|---|
| 265 | void WTFReportArgumentAssertionFailure(const char* file, int line, const char* function, const char* argName, const char* assertion) |
|---|
| 266 | { |
|---|
| 267 | printf_stderr_common("ARGUMENT BAD: %s, %s\n", argName, assertion); |
|---|
| 268 | printCallSite(file, line, function); |
|---|
| 269 | } |
|---|
| 270 | |
|---|
| 271 | class CrashLogPrintStream final : public PrintStream { |
|---|
| 272 | public: |
|---|
| 273 | WTF_ATTRIBUTE_PRINTF(2, 0) |
|---|
| 274 | void vprintf(const char* format, va_list argList) final |
|---|
| 275 | { |
|---|
| 276 | vprintf_stderr_common(format, argList); |
|---|
| 277 | } |
|---|
| 278 | }; |
|---|
| 279 | |
|---|
| 280 | void WTFReportBacktraceWithPrefix(const char* prefix) |
|---|
| 281 | { |
|---|
| 282 | static constexpr int framesToShow = 31; |
|---|
| 283 | static constexpr int framesToSkip = 2; |
|---|
| 284 | void* samples[framesToShow + framesToSkip]; |
|---|
| 285 | int frames = framesToShow + framesToSkip; |
|---|
| 286 | |
|---|
| 287 | WTFGetBacktrace(samples, &frames); |
|---|
| 288 | WTFPrintBacktraceWithPrefix(samples + framesToSkip, frames - framesToSkip, prefix); |
|---|
| 289 | } |
|---|
| 290 | |
|---|
| 291 | void WTFReportBacktrace() |
|---|
| 292 | { |
|---|
| 293 | static constexpr int framesToShow = 31; |
|---|
| 294 | static constexpr int framesToSkip = 2; |
|---|
| 295 | void* samples[framesToShow + framesToSkip]; |
|---|
| 296 | int frames = framesToShow + framesToSkip; |
|---|
| 297 | |
|---|
| 298 | WTFGetBacktrace(samples, &frames); |
|---|
| 299 | WTFPrintBacktrace(samples + framesToSkip, frames - framesToSkip); |
|---|
| 300 | } |
|---|
| 301 | |
|---|
| 302 | void WTFPrintBacktraceWithPrefix(void** stack, int size, const char* prefix) |
|---|
| 303 | { |
|---|
| 304 | CrashLogPrintStream out; |
|---|
| 305 | StackTrace stackTrace(stack, size, prefix); |
|---|
| 306 | out.print(stackTrace); |
|---|
| 307 | } |
|---|
| 308 | |
|---|
| 309 | void WTFPrintBacktrace(void** stack, int size) |
|---|
| 310 | { |
|---|
| 311 | WTFPrintBacktraceWithPrefix(stack, size, ""); |
|---|
| 312 | } |
|---|
| 313 | |
|---|
| 314 | #if !defined(NDEBUG) || !(OS(DARWIN) || PLATFORM(PLAYSTATION)) |
|---|
| 315 | void WTFCrash() |
|---|
| 316 | { |
|---|
| 317 | WTFReportBacktrace(); |
|---|
| 318 | #if ASAN_ENABLED |
|---|
| 319 | __builtin_trap(); |
|---|
| 320 | #else |
|---|
| 321 | *(int *)(uintptr_t)0xbbadbeef = 0; |
|---|
| 322 | // More reliable, but doesn't say BBADBEEF. |
|---|
| 323 | #if COMPILER(GCC_COMPATIBLE) |
|---|
| 324 | __builtin_trap(); |
|---|
| 325 | #else |
|---|
| 326 | ((void(*)())nullptr)(); |
|---|
| 327 | #endif // COMPILER(GCC_COMPATIBLE) |
|---|
| 328 | #endif // ASAN_ENABLED |
|---|
| 329 | } |
|---|
| 330 | #else |
|---|
| 331 | // We need to keep WTFCrash() around (even on non-debug OS(DARWIN) builds) as a workaround |
|---|
| 332 | // for presently shipping (circa early 2016) SafariForWebKitDevelopment binaries which still |
|---|
| 333 | // expects to link to it. |
|---|
| 334 | void WTFCrash() |
|---|
| 335 | { |
|---|
| 336 | CRASH(); |
|---|
| 337 | } |
|---|
| 338 | #endif // !defined(NDEBUG) || !(OS(DARWIN) || PLATFORM(PLAYSTATION)) |
|---|
| 339 | |
|---|
| 340 | void WTFCrashWithSecurityImplication() |
|---|
| 341 | { |
|---|
| 342 | CRASH(); |
|---|
| 343 | } |
|---|
| 344 | |
|---|
| 345 | bool WTFIsDebuggerAttached() |
|---|
| 346 | { |
|---|
| 347 | #if OS(DARWIN) |
|---|
| 348 | struct kinfo_proc info; |
|---|
| 349 | int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, getpid() }; |
|---|
| 350 | size_t size = sizeof(info); |
|---|
| 351 | if (sysctl(mib, sizeof(mib) / sizeof(mib[0]), &info, &size, nullptr, 0) == -1) |
|---|
| 352 | return false; |
|---|
| 353 | return info.kp_proc.p_flag & P_TRACED; |
|---|
| 354 | #else |
|---|
| 355 | return false; |
|---|
| 356 | #endif |
|---|
| 357 | } |
|---|
| 358 | |
|---|
| 359 | void WTFReportFatalError(const char* file, int line, const char* function, const char* format, ...) |
|---|
| 360 | { |
|---|
| 361 | va_list args; |
|---|
| 362 | va_start(args, format); |
|---|
| 363 | vprintf_stderr_with_prefix("FATAL ERROR: ", format, args); |
|---|
| 364 | va_end(args); |
|---|
| 365 | printf_stderr_common("\n"); |
|---|
| 366 | printCallSite(file, line, function); |
|---|
| 367 | } |
|---|
| 368 | |
|---|
| 369 | void WTFReportError(const char* file, int line, const char* function, const char* format, ...) |
|---|
| 370 | { |
|---|
| 371 | va_list args; |
|---|
| 372 | va_start(args, format); |
|---|
| 373 | vprintf_stderr_with_prefix("ERROR: ", format, args); |
|---|
| 374 | va_end(args); |
|---|
| 375 | printf_stderr_common("\n"); |
|---|
| 376 | printCallSite(file, line, function); |
|---|
| 377 | } |
|---|
| 378 | |
|---|
| 379 | class WTFLoggingAccumulator { |
|---|
| 380 | WTF_MAKE_FAST_ALLOCATED; |
|---|
| 381 | public: |
|---|
| 382 | void accumulate(const String&); |
|---|
| 383 | void resetAccumulatedLogs(); |
|---|
| 384 | String getAndResetAccumulatedLogs(); |
|---|
| 385 | |
|---|
| 386 | private: |
|---|
| 387 | Lock accumulatorLock; |
|---|
| 388 | StringBuilder loggingAccumulator WTF_GUARDED_BY_LOCK(accumulatorLock); |
|---|
| 389 | }; |
|---|
| 390 | |
|---|
| 391 | void WTFLoggingAccumulator::accumulate(const String& log) |
|---|
| 392 | { |
|---|
| 393 | Locker locker { accumulatorLock }; |
|---|
| 394 | loggingAccumulator.append(log); |
|---|
| 395 | } |
|---|
| 396 | |
|---|
| 397 | void WTFLoggingAccumulator::resetAccumulatedLogs() |
|---|
| 398 | { |
|---|
| 399 | Locker locker { accumulatorLock }; |
|---|
| 400 | loggingAccumulator.clear(); |
|---|
| 401 | } |
|---|
| 402 | |
|---|
| 403 | String WTFLoggingAccumulator::getAndResetAccumulatedLogs() |
|---|
| 404 | { |
|---|
| 405 | Locker locker { accumulatorLock }; |
|---|
| 406 | String result = loggingAccumulator.toString(); |
|---|
| 407 | loggingAccumulator.clear(); |
|---|
| 408 | return result; |
|---|
| 409 | } |
|---|
| 410 | |
|---|
| 411 | static WTFLoggingAccumulator& loggingAccumulator() |
|---|
| 412 | { |
|---|
| 413 | static WTFLoggingAccumulator* accumulator; |
|---|
| 414 | static std::once_flag initializeAccumulatorOnce; |
|---|
| 415 | std::call_once(initializeAccumulatorOnce, [] { |
|---|
| 416 | accumulator = new WTFLoggingAccumulator; |
|---|
| 417 | }); |
|---|
| 418 | |
|---|
| 419 | return *accumulator; |
|---|
| 420 | } |
|---|
| 421 | |
|---|
| 422 | void WTFSetLogChannelLevel(WTFLogChannel* channel, WTFLogLevel level) |
|---|
| 423 | { |
|---|
| 424 | channel->level = level; |
|---|
| 425 | } |
|---|
| 426 | |
|---|
| 427 | bool WTFWillLogWithLevel(WTFLogChannel* channel, WTFLogLevel level) |
|---|
| 428 | { |
|---|
| 429 | return channel->level >= level && channel->state != WTFLogChannelState::Off; |
|---|
| 430 | } |
|---|
| 431 | |
|---|
| 432 | void WTFLogWithLevel(WTFLogChannel* channel, WTFLogLevel level, const char* format, ...) |
|---|
| 433 | { |
|---|
| 434 | if (level != WTFLogLevel::Always && level > channel->level) |
|---|
| 435 | return; |
|---|
| 436 | |
|---|
| 437 | if (channel->level != WTFLogLevel::Always && channel->state == WTFLogChannelState::Off) |
|---|
| 438 | return; |
|---|
| 439 | |
|---|
| 440 | va_list args; |
|---|
| 441 | va_start(args, format); |
|---|
| 442 | |
|---|
| 443 | ALLOW_NONLITERAL_FORMAT_BEGIN |
|---|
| 444 | WTFLog(channel, format, args); |
|---|
| 445 | ALLOW_NONLITERAL_FORMAT_END |
|---|
| 446 | |
|---|
| 447 | va_end(args); |
|---|
| 448 | } |
|---|
| 449 | |
|---|
| 450 | WTF_ATTRIBUTE_PRINTF(2, 0) |
|---|
| 451 | static void WTFLogVaList(WTFLogChannel* channel, const char* format, va_list args) |
|---|
| 452 | { |
|---|
| 453 | if (channel->state == WTFLogChannelState::Off) |
|---|
| 454 | return; |
|---|
| 455 | |
|---|
| 456 | if (channel->state == WTFLogChannelState::On) { |
|---|
| 457 | vprintf_stderr_with_trailing_newline(format, args); |
|---|
| 458 | return; |
|---|
| 459 | } |
|---|
| 460 | |
|---|
| 461 | ASSERT(channel->state == WTFLogChannelState::OnWithAccumulation); |
|---|
| 462 | |
|---|
| 463 | ALLOW_NONLITERAL_FORMAT_BEGIN |
|---|
| 464 | String loggingString = WTF::createWithFormatAndArguments(format, args); |
|---|
| 465 | ALLOW_NONLITERAL_FORMAT_END |
|---|
| 466 | |
|---|
| 467 | if (!loggingString.endsWith('\n')) |
|---|
| 468 | loggingString.append('\n'); |
|---|
| 469 | |
|---|
| 470 | loggingAccumulator().accumulate(loggingString); |
|---|
| 471 | |
|---|
| 472 | logToStderr(loggingString.utf8().data()); |
|---|
| 473 | } |
|---|
| 474 | |
|---|
| 475 | void WTFLog(WTFLogChannel* channel, const char* format, ...) |
|---|
| 476 | { |
|---|
| 477 | va_list args; |
|---|
| 478 | va_start(args, format); |
|---|
| 479 | |
|---|
| 480 | WTFLogVaList(channel, format, args); |
|---|
| 481 | |
|---|
| 482 | va_end(args); |
|---|
| 483 | } |
|---|
| 484 | |
|---|
| 485 | void WTFLogVerbose(const char* file, int line, const char* function, WTFLogChannel* channel, const char* format, ...) |
|---|
| 486 | { |
|---|
| 487 | if (channel->state != WTFLogChannelState::On) |
|---|
| 488 | return; |
|---|
| 489 | |
|---|
| 490 | va_list args; |
|---|
| 491 | va_start(args, format); |
|---|
| 492 | |
|---|
| 493 | ALLOW_NONLITERAL_FORMAT_BEGIN |
|---|
| 494 | WTFLogVaList(channel, format, args); |
|---|
| 495 | ALLOW_NONLITERAL_FORMAT_END |
|---|
| 496 | |
|---|
| 497 | va_end(args); |
|---|
| 498 | |
|---|
| 499 | printCallSite(file, line, function); |
|---|
| 500 | } |
|---|
| 501 | |
|---|
| 502 | void WTFLogAlwaysV(const char* format, va_list args) |
|---|
| 503 | { |
|---|
| 504 | vprintf_stderr_with_trailing_newline(format, args); |
|---|
| 505 | } |
|---|
| 506 | |
|---|
| 507 | void WTFLogAlways(const char* format, ...) |
|---|
| 508 | { |
|---|
| 509 | va_list args; |
|---|
| 510 | va_start(args, format); |
|---|
| 511 | WTFLogAlwaysV(format, args); |
|---|
| 512 | va_end(args); |
|---|
| 513 | } |
|---|
| 514 | |
|---|
| 515 | void WTFLogAlwaysAndCrash(const char* format, ...) |
|---|
| 516 | { |
|---|
| 517 | va_list args; |
|---|
| 518 | va_start(args, format); |
|---|
| 519 | WTFLogAlwaysV(format, args); |
|---|
| 520 | va_end(args); |
|---|
| 521 | CRASH(); |
|---|
| 522 | } |
|---|
| 523 | |
|---|
| 524 | WTFLogChannel* WTFLogChannelByName(WTFLogChannel* channels[], size_t count, const char* name) |
|---|
| 525 | { |
|---|
| 526 | for (size_t i = 0; i < count; ++i) { |
|---|
| 527 | WTFLogChannel* channel = channels[i]; |
|---|
| 528 | if (equalIgnoringASCIICase(name, channel->name)) |
|---|
| 529 | return channel; |
|---|
| 530 | } |
|---|
| 531 | |
|---|
| 532 | return nullptr; |
|---|
| 533 | } |
|---|
| 534 | |
|---|
| 535 | static void setStateOfAllChannels(WTFLogChannel* channels[], size_t channelCount, WTFLogChannelState state) |
|---|
| 536 | { |
|---|
| 537 | for (size_t i = 0; i < channelCount; ++i) |
|---|
| 538 | channels[i]->state = state; |
|---|
| 539 | } |
|---|
| 540 | |
|---|
| 541 | void WTFInitializeLogChannelStatesFromString(WTFLogChannel* channels[], size_t count, const char* logLevel) |
|---|
| 542 | { |
|---|
| 543 | #if USE(OS_LOG) && !RELEASE_LOG_DISABLED |
|---|
| 544 | for (size_t i = 0; i < count; ++i) { |
|---|
| 545 | WTFLogChannel* channel = channels[i]; |
|---|
| 546 | channel->osLogChannel = os_log_create(channel->subsystem, channel->name); |
|---|
| 547 | } |
|---|
| 548 | #endif |
|---|
| 549 | |
|---|
| 550 | for (auto& logLevelComponent : String(logLevel).split(',')) { |
|---|
| 551 | Vector<String> componentInfo = logLevelComponent.split('='); |
|---|
| 552 | String component = componentInfo[0].stripWhiteSpace(); |
|---|
| 553 | |
|---|
| 554 | WTFLogChannelState logChannelState = WTFLogChannelState::On; |
|---|
| 555 | if (component.startsWith('-')) { |
|---|
| 556 | logChannelState = WTFLogChannelState::Off; |
|---|
| 557 | component = component.substring(1); |
|---|
| 558 | } |
|---|
| 559 | |
|---|
| 560 | if (equalLettersIgnoringASCIICase(component, "all")) { |
|---|
| 561 | setStateOfAllChannels(channels, count, logChannelState); |
|---|
| 562 | continue; |
|---|
| 563 | } |
|---|
| 564 | |
|---|
| 565 | WTFLogLevel logChannelLevel = WTFLogLevel::Error; |
|---|
| 566 | if (componentInfo.size() > 1) { |
|---|
| 567 | String level = componentInfo[1].stripWhiteSpace(); |
|---|
| 568 | if (equalLettersIgnoringASCIICase(level, "error")) |
|---|
| 569 | logChannelLevel = WTFLogLevel::Error; |
|---|
| 570 | else if (equalLettersIgnoringASCIICase(level, "warning")) |
|---|
| 571 | logChannelLevel = WTFLogLevel::Warning; |
|---|
| 572 | else if (equalLettersIgnoringASCIICase(level, "info")) |
|---|
| 573 | logChannelLevel = WTFLogLevel::Info; |
|---|
| 574 | else if (equalLettersIgnoringASCIICase(level, "debug")) |
|---|
| 575 | logChannelLevel = WTFLogLevel::Debug; |
|---|
| 576 | else |
|---|
| 577 | WTFLogAlways("Unknown logging level: %s", level.utf8().data()); |
|---|
| 578 | } |
|---|
| 579 | |
|---|
| 580 | if (WTFLogChannel* channel = WTFLogChannelByName(channels, count, component.utf8().data())) { |
|---|
| 581 | channel->state = logChannelState; |
|---|
| 582 | channel->level = logChannelLevel; |
|---|
| 583 | } else |
|---|
| 584 | WTFLogAlways("Unknown logging channel: %s", component.utf8().data()); |
|---|
| 585 | } |
|---|
| 586 | } |
|---|
| 587 | |
|---|
| 588 | #if !RELEASE_LOG_DISABLED |
|---|
| 589 | void WTFReleaseLogStackTrace(WTFLogChannel* channel) |
|---|
| 590 | { |
|---|
| 591 | auto stackTrace = WTF::StackTrace::captureStackTrace(30, 0); |
|---|
| 592 | if (stackTrace && stackTrace->stack()) { |
|---|
| 593 | auto stack = stackTrace->stack(); |
|---|
| 594 | for (int frameNumber = 1; frameNumber < stackTrace->size(); ++frameNumber) { |
|---|
| 595 | auto stackFrame = stack[frameNumber]; |
|---|
| 596 | auto demangled = WTF::StackTrace::demangle(stackFrame); |
|---|
| 597 | #if USE(OS_LOG) |
|---|
| 598 | if (demangled && demangled->demangledName()) |
|---|
| 599 | os_log(channel->osLogChannel, "%-3d %p %{public}s", frameNumber, stackFrame, demangled->demangledName()); |
|---|
| 600 | else if (demangled && demangled->mangledName()) |
|---|
| 601 | os_log(channel->osLogChannel, "%-3d %p %{public}s", frameNumber, stackFrame, demangled->mangledName()); |
|---|
| 602 | else |
|---|
| 603 | os_log(channel->osLogChannel, "%-3d %p", frameNumber, stackFrame); |
|---|
| 604 | #elif USE(JOURNALD) |
|---|
| 605 | StringPrintStream out; |
|---|
| 606 | if (demangled && demangled->demangledName()) |
|---|
| 607 | out.printf("%-3d %p %s", frameNumber, stackFrame, demangled->demangledName()); |
|---|
| 608 | else if (demangled && demangled->mangledName()) |
|---|
| 609 | out.printf("%-3d %p %s", frameNumber, stackFrame, demangled->mangledName()); |
|---|
| 610 | else |
|---|
| 611 | out.printf("%-3d %p", frameNumber, stackFrame); |
|---|
| 612 | sd_journal_send("WEBKIT_SUBSYSTEM=%s", channel->subsystem, "WEBKIT_CHANNEL=%s", channel->name, "MESSAGE=%s", out.toCString().data(), nullptr); |
|---|
| 613 | #endif |
|---|
| 614 | } |
|---|
| 615 | } |
|---|
| 616 | } |
|---|
| 617 | #endif |
|---|
| 618 | |
|---|
| 619 | } // extern "C" |
|---|
| 620 | |
|---|
| 621 | #if (OS(DARWIN) || PLATFORM(PLAYSTATION)) && (CPU(X86_64) || CPU(ARM64)) |
|---|
| 622 | #if CPU(X86_64) |
|---|
| 623 | |
|---|
| 624 | #define CRASH_INST "int3" |
|---|
| 625 | |
|---|
| 626 | // This ordering was chosen to be consistent with JSC's JIT asserts. We probably shouldn't change this ordering |
|---|
| 627 | // since it would make tooling crash reports much harder. If, for whatever reason, we decide to change the ordering |
|---|
| 628 | // here we should update the abortWithuint64_t functions. |
|---|
| 629 | #define CRASH_GPR0 "r11" |
|---|
| 630 | #define CRASH_GPR1 "r10" |
|---|
| 631 | #define CRASH_GPR2 "r9" |
|---|
| 632 | #define CRASH_GPR3 "r8" |
|---|
| 633 | #define CRASH_GPR4 "r15" |
|---|
| 634 | #define CRASH_GPR5 "r14" |
|---|
| 635 | #define CRASH_GPR6 "r13" |
|---|
| 636 | |
|---|
| 637 | #elif CPU(ARM64) // CPU(X86_64) |
|---|
| 638 | |
|---|
| 639 | #define CRASH_INST "brk #0xc471" |
|---|
| 640 | |
|---|
| 641 | // See comment above on the ordering. |
|---|
| 642 | #define CRASH_GPR0 "x16" |
|---|
| 643 | #define CRASH_GPR1 "x17" |
|---|
| 644 | #define CRASH_GPR2 "x18" |
|---|
| 645 | #define CRASH_GPR3 "x19" |
|---|
| 646 | #define CRASH_GPR4 "x20" |
|---|
| 647 | #define CRASH_GPR5 "x21" |
|---|
| 648 | #define CRASH_GPR6 "x22" |
|---|
| 649 | |
|---|
| 650 | #endif // CPU(ARM64) |
|---|
| 651 | |
|---|
| 652 | void WTFCrashWithInfoImpl(int, const char*, const char*, int, uint64_t reason, uint64_t misc1, uint64_t misc2, uint64_t misc3, uint64_t misc4, uint64_t misc5, uint64_t misc6) |
|---|
| 653 | { |
|---|
| 654 | register uint64_t reasonGPR asm(CRASH_GPR0) = reason; |
|---|
| 655 | register uint64_t misc1GPR asm(CRASH_GPR1) = misc1; |
|---|
| 656 | register uint64_t misc2GPR asm(CRASH_GPR2) = misc2; |
|---|
| 657 | register uint64_t misc3GPR asm(CRASH_GPR3) = misc3; |
|---|
| 658 | register uint64_t misc4GPR asm(CRASH_GPR4) = misc4; |
|---|
| 659 | register uint64_t misc5GPR asm(CRASH_GPR5) = misc5; |
|---|
| 660 | register uint64_t misc6GPR asm(CRASH_GPR6) = misc6; |
|---|
| 661 | __asm__ volatile (CRASH_INST : : "r"(reasonGPR), "r"(misc1GPR), "r"(misc2GPR), "r"(misc3GPR), "r"(misc4GPR), "r"(misc5GPR), "r"(misc6GPR)); |
|---|
| 662 | __builtin_unreachable(); |
|---|
| 663 | } |
|---|
| 664 | |
|---|
| 665 | void WTFCrashWithInfoImpl(int, const char*, const char*, int, uint64_t reason, uint64_t misc1, uint64_t misc2, uint64_t misc3, uint64_t misc4, uint64_t misc5) |
|---|
| 666 | { |
|---|
| 667 | register uint64_t reasonGPR asm(CRASH_GPR0) = reason; |
|---|
| 668 | register uint64_t misc1GPR asm(CRASH_GPR1) = misc1; |
|---|
| 669 | register uint64_t misc2GPR asm(CRASH_GPR2) = misc2; |
|---|
| 670 | register uint64_t misc3GPR asm(CRASH_GPR3) = misc3; |
|---|
| 671 | register uint64_t misc4GPR asm(CRASH_GPR4) = misc4; |
|---|
| 672 | register uint64_t misc5GPR asm(CRASH_GPR5) = misc5; |
|---|
| 673 | __asm__ volatile (CRASH_INST : : "r"(reasonGPR), "r"(misc1GPR), "r"(misc2GPR), "r"(misc3GPR), "r"(misc4GPR), "r"(misc5GPR)); |
|---|
| 674 | __builtin_unreachable(); |
|---|
| 675 | } |
|---|
| 676 | |
|---|
| 677 | void WTFCrashWithInfoImpl(int, const char*, const char*, int, uint64_t reason, uint64_t misc1, uint64_t misc2, uint64_t misc3, uint64_t misc4) |
|---|
| 678 | { |
|---|
| 679 | register uint64_t reasonGPR asm(CRASH_GPR0) = reason; |
|---|
| 680 | register uint64_t misc1GPR asm(CRASH_GPR1) = misc1; |
|---|
| 681 | register uint64_t misc2GPR asm(CRASH_GPR2) = misc2; |
|---|
| 682 | register uint64_t misc3GPR asm(CRASH_GPR3) = misc3; |
|---|
| 683 | register uint64_t misc4GPR asm(CRASH_GPR4) = misc4; |
|---|
| 684 | __asm__ volatile (CRASH_INST : : "r"(reasonGPR), "r"(misc1GPR), "r"(misc2GPR), "r"(misc3GPR), "r"(misc4GPR)); |
|---|
| 685 | __builtin_unreachable(); |
|---|
| 686 | } |
|---|
| 687 | |
|---|
| 688 | void WTFCrashWithInfoImpl(int, const char*, const char*, int, uint64_t reason, uint64_t misc1, uint64_t misc2, uint64_t misc3) |
|---|
| 689 | { |
|---|
| 690 | register uint64_t reasonGPR asm(CRASH_GPR0) = reason; |
|---|
| 691 | register uint64_t misc1GPR asm(CRASH_GPR1) = misc1; |
|---|
| 692 | register uint64_t misc2GPR asm(CRASH_GPR2) = misc2; |
|---|
| 693 | register uint64_t misc3GPR asm(CRASH_GPR3) = misc3; |
|---|
| 694 | __asm__ volatile (CRASH_INST : : "r"(reasonGPR), "r"(misc1GPR), "r"(misc2GPR), "r"(misc3GPR)); |
|---|
| 695 | __builtin_unreachable(); |
|---|
| 696 | } |
|---|
| 697 | |
|---|
| 698 | void WTFCrashWithInfoImpl(int, const char*, const char*, int, uint64_t reason, uint64_t misc1, uint64_t misc2) |
|---|
| 699 | { |
|---|
| 700 | register uint64_t reasonGPR asm(CRASH_GPR0) = reason; |
|---|
| 701 | register uint64_t misc1GPR asm(CRASH_GPR1) = misc1; |
|---|
| 702 | register uint64_t misc2GPR asm(CRASH_GPR2) = misc2; |
|---|
| 703 | __asm__ volatile (CRASH_INST : : "r"(reasonGPR), "r"(misc1GPR), "r"(misc2GPR)); |
|---|
| 704 | __builtin_unreachable(); |
|---|
| 705 | } |
|---|
| 706 | |
|---|
| 707 | void WTFCrashWithInfoImpl(int, const char*, const char*, int, uint64_t reason, uint64_t misc1) |
|---|
| 708 | { |
|---|
| 709 | register uint64_t reasonGPR asm(CRASH_GPR0) = reason; |
|---|
| 710 | register uint64_t misc1GPR asm(CRASH_GPR1) = misc1; |
|---|
| 711 | __asm__ volatile (CRASH_INST : : "r"(reasonGPR), "r"(misc1GPR)); |
|---|
| 712 | __builtin_unreachable(); |
|---|
| 713 | } |
|---|
| 714 | |
|---|
| 715 | void WTFCrashWithInfoImpl(int, const char*, const char*, int, uint64_t reason) |
|---|
| 716 | { |
|---|
| 717 | register uint64_t reasonGPR asm(CRASH_GPR0) = reason; |
|---|
| 718 | __asm__ volatile (CRASH_INST : : "r"(reasonGPR)); |
|---|
| 719 | __builtin_unreachable(); |
|---|
| 720 | } |
|---|
| 721 | |
|---|
| 722 | #else |
|---|
| 723 | |
|---|
| 724 | void WTFCrashWithInfoImpl(int, const char*, const char*, int, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t) { CRASH(); } |
|---|
| 725 | void WTFCrashWithInfoImpl(int, const char*, const char*, int, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t) { CRASH(); } |
|---|
| 726 | void WTFCrashWithInfoImpl(int, const char*, const char*, int, uint64_t, uint64_t, uint64_t, uint64_t, uint64_t) { CRASH(); } |
|---|
| 727 | void WTFCrashWithInfoImpl(int, const char*, const char*, int, uint64_t, uint64_t, uint64_t, uint64_t) { CRASH(); } |
|---|
| 728 | void WTFCrashWithInfoImpl(int, const char*, const char*, int, uint64_t, uint64_t, uint64_t) { CRASH(); } |
|---|
| 729 | void WTFCrashWithInfoImpl(int, const char*, const char*, int, uint64_t, uint64_t) { CRASH(); } |
|---|
| 730 | void WTFCrashWithInfoImpl(int, const char*, const char*, int, uint64_t) { CRASH(); } |
|---|
| 731 | |
|---|
| 732 | #endif // (OS(DARWIN) || PLATFORM(PLAYSTATION)) && (CPU(X64_64) || CPU(ARM64)) |
|---|
| 733 | |
|---|
| 734 | namespace WTF { |
|---|
| 735 | |
|---|
| 736 | void resetAccumulatedLogs() |
|---|
| 737 | { |
|---|
| 738 | loggingAccumulator().resetAccumulatedLogs(); |
|---|
| 739 | } |
|---|
| 740 | |
|---|
| 741 | String getAndResetAccumulatedLogs() |
|---|
| 742 | { |
|---|
| 743 | return loggingAccumulator().getAndResetAccumulatedLogs(); |
|---|
| 744 | } |
|---|
| 745 | |
|---|
| 746 | } // namespace WTF |
|---|