1 // Copyright (c) 2006-2009 The Chromium Authors. All rights reserved. 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 "base/logging.h" 6 7 #if defined(OS_WIN) 8 #include <io.h> 9 #include <windows.h> 10 typedef HANDLE FileHandle; 11 typedef HANDLE MutexHandle; 12 // Windows warns on using write(). It prefers _write(). 13 #define write(fd, buf, count) _write(fd, buf, static_cast<unsigned int>(count)) 14 // Windows doesn't define STDERR_FILENO. Define it here. 15 #define STDERR_FILENO 2 16 #elif defined(OS_MACOSX) 17 #include <CoreFoundation/CoreFoundation.h> 18 #include <mach/mach.h> 19 #include <mach/mach_time.h> 20 #include <mach-o/dyld.h> 21 #elif defined(OS_POSIX) 22 #include <sys/syscall.h> 23 #include <time.h> 24 #endif 25 26 #if defined(OS_POSIX) 27 #include <errno.h> 28 #include <stdlib.h> 29 #include <stdio.h> 30 #include <string.h> 31 #include <unistd.h> 32 #define MAX_PATH PATH_MAX 33 typedef FILE* FileHandle; 34 typedef pthread_mutex_t* MutexHandle; 35 #endif 36 37 #include <ctime> 38 #include <iomanip> 39 #include <cstring> 40 #include <algorithm> 41 42 #include "base/base_switches.h" 43 #include "base/command_line.h" 44 #include "base/debug_util.h" 45 #include "base/eintr_wrapper.h" 46 #include "base/lock_impl.h" 47 #if defined(OS_POSIX) 48 #include "base/safe_strerror_posix.h" 49 #endif 50 #include "base/string_piece.h" 51 #include "base/string_util.h" 52 #include "base/utf_string_conversions.h" 53 54 namespace logging { 55 56 bool g_enable_dcheck = false; 57 58 const char* const log_severity_names[LOG_NUM_SEVERITIES] = { 59 "INFO", "WARNING", "ERROR", "ERROR_REPORT", "FATAL" }; 60 61 int min_log_level = 0; 62 LogLockingState lock_log_file = LOCK_LOG_FILE; 63 64 // The default set here for logging_destination will only be used if 65 // InitLogging is not called. On Windows, use a file next to the exe; 66 // on POSIX platforms, where it may not even be possible to locate the 67 // executable on disk, use stderr. 68 #if defined(OS_WIN) 69 LoggingDestination logging_destination = LOG_ONLY_TO_FILE; 70 #elif defined(OS_POSIX) 71 LoggingDestination logging_destination = LOG_ONLY_TO_SYSTEM_DEBUG_LOG; 72 #endif 73 74 const int kMaxFilteredLogLevel = LOG_WARNING; 75 std::string* log_filter_prefix; 76 77 // For LOG_ERROR and above, always print to stderr. 78 const int kAlwaysPrintErrorLevel = LOG_ERROR; 79 80 // Which log file to use? This is initialized by InitLogging or 81 // will be lazily initialized to the default value when it is 82 // first needed. 83 #if defined(OS_WIN) 84 typedef wchar_t PathChar; 85 typedef std::wstring PathString; 86 #else 87 typedef char PathChar; 88 typedef std::string PathString; 89 #endif 90 PathString* log_file_name = NULL; 91 92 // this file is lazily opened and the handle may be NULL 93 FileHandle log_file = NULL; 94 95 // what should be prepended to each message? 96 bool log_process_id = false; 97 bool log_thread_id = false; 98 bool log_timestamp = true; 99 bool log_tickcount = false; 100 101 // An assert handler override specified by the client to be called instead of 102 // the debug message dialog and process termination. 103 LogAssertHandlerFunction log_assert_handler = NULL; 104 // An report handler override specified by the client to be called instead of 105 // the debug message dialog. 106 LogReportHandlerFunction log_report_handler = NULL; 107 // A log message handler that gets notified of every log message we process. 108 LogMessageHandlerFunction log_message_handler = NULL; 109 110 // The lock is used if log file locking is false. It helps us avoid problems 111 // with multiple threads writing to the log file at the same time. Use 112 // LockImpl directly instead of using Lock, because Lock makes logging calls. 113 static LockImpl* log_lock = NULL; 114 115 // When we don't use a lock, we are using a global mutex. We need to do this 116 // because LockFileEx is not thread safe. 117 #if defined(OS_WIN) 118 MutexHandle log_mutex = NULL; 119 #elif defined(OS_POSIX) 120 pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER; 121 #endif 122 123 // Helper functions to wrap platform differences. 124 125 int32 CurrentProcessId() { 126 #if defined(OS_WIN) 127 return GetCurrentProcessId(); 128 #elif defined(OS_POSIX) 129 return getpid(); 130 #endif 131 } 132 133 int32 CurrentThreadId() { 134 #if defined(OS_WIN) 135 return GetCurrentThreadId(); 136 #elif defined(OS_MACOSX) 137 return mach_thread_self(); 138 #elif defined(OS_LINUX) 139 return syscall(__NR_gettid); 140 #elif defined(OS_FREEBSD) 141 // TODO(BSD): find a better thread ID 142 return reinterpret_cast<int64>(pthread_self()); 143 #endif 144 } 145 146 uint64 TickCount() { 147 #if defined(OS_WIN) 148 return GetTickCount(); 149 #elif defined(OS_MACOSX) 150 return mach_absolute_time(); 151 #elif defined(OS_POSIX) 152 struct timespec ts; 153 clock_gettime(CLOCK_MONOTONIC, &ts); 154 155 uint64 absolute_micro = 156 static_cast<int64>(ts.tv_sec) * 1000000 + 157 static_cast<int64>(ts.tv_nsec) / 1000; 158 159 return absolute_micro; 160 #endif 161 } 162 163 void CloseFile(FileHandle log) { 164 #if defined(OS_WIN) 165 CloseHandle(log); 166 #else 167 fclose(log); 168 #endif 169 } 170 171 void DeleteFilePath(const PathString& log_name) { 172 #if defined(OS_WIN) 173 DeleteFile(log_name.c_str()); 174 #else 175 unlink(log_name.c_str()); 176 #endif 177 } 178 179 // Called by logging functions to ensure that debug_file is initialized 180 // and can be used for writing. Returns false if the file could not be 181 // initialized. debug_file will be NULL in this case. 182 bool InitializeLogFileHandle() { 183 if (log_file) 184 return true; 185 186 if (!log_file_name) { 187 // Nobody has called InitLogging to specify a debug log file, so here we 188 // initialize the log file name to a default. 189 #if defined(OS_WIN) 190 // On Windows we use the same path as the exe. 191 wchar_t module_name[MAX_PATH]; 192 GetModuleFileName(NULL, module_name, MAX_PATH); 193 log_file_name = new std::wstring(module_name); 194 std::wstring::size_type last_backslash = 195 log_file_name->rfind('\\', log_file_name->size()); 196 if (last_backslash != std::wstring::npos) 197 log_file_name->erase(last_backslash + 1); 198 *log_file_name += L"debug.log"; 199 #elif defined(OS_POSIX) 200 // On other platforms we just use the current directory. 201 log_file_name = new std::string("debug.log"); 202 #endif 203 } 204 205 if (logging_destination == LOG_ONLY_TO_FILE || 206 logging_destination == LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG) { 207 #if defined(OS_WIN) 208 log_file = CreateFile(log_file_name->c_str(), GENERIC_WRITE, 209 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, 210 OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 211 if (log_file == INVALID_HANDLE_VALUE || log_file == NULL) { 212 // try the current directory 213 log_file = CreateFile(L".\\debug.log", GENERIC_WRITE, 214 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, 215 OPEN_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); 216 if (log_file == INVALID_HANDLE_VALUE || log_file == NULL) { 217 log_file = NULL; 218 return false; 219 } 220 } 221 SetFilePointer(log_file, 0, 0, FILE_END); 222 #elif defined(OS_POSIX) 223 log_file = fopen(log_file_name->c_str(), "a"); 224 if (log_file == NULL) 225 return false; 226 #endif 227 } 228 229 return true; 230 } 231 232 #if defined(OS_POSIX) && !defined(OS_MACOSX) 233 int GetLoggingFileDescriptor() { 234 // No locking needed, since this is only called by the zygote server, 235 // which is single-threaded. 236 if (log_file) 237 return fileno(log_file); 238 return -1; 239 } 240 #endif 241 242 void InitLogMutex() { 243 #if defined(OS_WIN) 244 if (!log_mutex) { 245 // \ is not a legal character in mutex names so we replace \ with / 246 std::wstring safe_name(*log_file_name); 247 std::replace(safe_name.begin(), safe_name.end(), '\\', '/'); 248 std::wstring t(L"Global\\"); 249 t.append(safe_name); 250 log_mutex = ::CreateMutex(NULL, FALSE, t.c_str()); 251 } 252 #elif defined(OS_POSIX) 253 // statically initialized 254 #endif 255 } 256 257 void InitLogging(const PathChar* new_log_file, LoggingDestination logging_dest, 258 LogLockingState lock_log, OldFileDeletionState delete_old) { 259 g_enable_dcheck = 260 CommandLine::ForCurrentProcess()->HasSwitch(switches::kEnableDCHECK); 261 262 if (log_file) { 263 // calling InitLogging twice or after some log call has already opened the 264 // default log file will re-initialize to the new options 265 CloseFile(log_file); 266 log_file = NULL; 267 } 268 269 lock_log_file = lock_log; 270 logging_destination = logging_dest; 271 272 // ignore file options if logging is disabled or only to system 273 if (logging_destination == LOG_NONE || 274 logging_destination == LOG_ONLY_TO_SYSTEM_DEBUG_LOG) 275 return; 276 277 if (!log_file_name) 278 log_file_name = new PathString(); 279 *log_file_name = new_log_file; 280 if (delete_old == DELETE_OLD_LOG_FILE) 281 DeleteFilePath(*log_file_name); 282 283 if (lock_log_file == LOCK_LOG_FILE) { 284 InitLogMutex(); 285 } else if (!log_lock) { 286 log_lock = new LockImpl(); 287 } 288 289 InitializeLogFileHandle(); 290 } 291 292 void SetMinLogLevel(int level) { 293 min_log_level = level; 294 } 295 296 int GetMinLogLevel() { 297 return min_log_level; 298 } 299 300 void SetLogFilterPrefix(const char* filter) { 301 if (log_filter_prefix) { 302 delete log_filter_prefix; 303 log_filter_prefix = NULL; 304 } 305 306 if (filter) 307 log_filter_prefix = new std::string(filter); 308 } 309 310 void SetLogItems(bool enable_process_id, bool enable_thread_id, 311 bool enable_timestamp, bool enable_tickcount) { 312 log_process_id = enable_process_id; 313 log_thread_id = enable_thread_id; 314 log_timestamp = enable_timestamp; 315 log_tickcount = enable_tickcount; 316 } 317 318 void SetLogAssertHandler(LogAssertHandlerFunction handler) { 319 log_assert_handler = handler; 320 } 321 322 void SetLogReportHandler(LogReportHandlerFunction handler) { 323 log_report_handler = handler; 324 } 325 326 void SetLogMessageHandler(LogMessageHandlerFunction handler) { 327 log_message_handler = handler; 328 } 329 330 331 // Displays a message box to the user with the error message in it. For 332 // Windows programs, it's possible that the message loop is messed up on 333 // a fatal error, and creating a MessageBox will cause that message loop 334 // to be run. Instead, we try to spawn another process that displays its 335 // command line. We look for "Debug Message.exe" in the same directory as 336 // the application. If it exists, we use it, otherwise, we use a regular 337 // message box. 338 void DisplayDebugMessage(const std::string& str) { 339 if (str.empty()) 340 return; 341 342 #if defined(OS_WIN) 343 // look for the debug dialog program next to our application 344 wchar_t prog_name[MAX_PATH]; 345 GetModuleFileNameW(NULL, prog_name, MAX_PATH); 346 wchar_t* backslash = wcsrchr(prog_name, '\\'); 347 if (backslash) 348 backslash[1] = 0; 349 wcscat_s(prog_name, MAX_PATH, L"debug_message.exe"); 350 351 std::wstring cmdline = UTF8ToWide(str); 352 if (cmdline.empty()) 353 return; 354 355 STARTUPINFO startup_info; 356 memset(&startup_info, 0, sizeof(startup_info)); 357 startup_info.cb = sizeof(startup_info); 358 359 PROCESS_INFORMATION process_info; 360 if (CreateProcessW(prog_name, &cmdline[0], NULL, NULL, false, 0, NULL, 361 NULL, &startup_info, &process_info)) { 362 WaitForSingleObject(process_info.hProcess, INFINITE); 363 CloseHandle(process_info.hThread); 364 CloseHandle(process_info.hProcess); 365 } else { 366 // debug process broken, let's just do a message box 367 MessageBoxW(NULL, &cmdline[0], L"Fatal error", 368 MB_OK | MB_ICONHAND | MB_TOPMOST); 369 } 370 #else 371 fprintf(stderr, "%s\n", str.c_str()); 372 fflush(stderr); 373 #endif 374 } 375 376 #if defined(OS_WIN) 377 LogMessage::SaveLastError::SaveLastError() : last_error_(::GetLastError()) { 378 } 379 380 LogMessage::SaveLastError::~SaveLastError() { 381 ::SetLastError(last_error_); 382 } 383 #endif // defined(OS_WIN) 384 385 LogMessage::LogMessage(const char* file, int line, LogSeverity severity, 386 int ctr) 387 : severity_(severity) { 388 Init(file, line); 389 } 390 391 LogMessage::LogMessage(const char* file, int line, const CheckOpString& result) 392 : severity_(LOG_FATAL) { 393 Init(file, line); 394 stream_ << "Check failed: " << (*result.str_); 395 } 396 397 LogMessage::LogMessage(const char* file, int line, LogSeverity severity, 398 const CheckOpString& result) 399 : severity_(severity) { 400 Init(file, line); 401 stream_ << "Check failed: " << (*result.str_); 402 } 403 404 LogMessage::LogMessage(const char* file, int line) 405 : severity_(LOG_INFO) { 406 Init(file, line); 407 } 408 409 LogMessage::LogMessage(const char* file, int line, LogSeverity severity) 410 : severity_(severity) { 411 Init(file, line); 412 } 413 414 // writes the common header info to the stream 415 void LogMessage::Init(const char* file, int line) { 416 // log only the filename 417 const char* last_slash = strrchr(file, '\\'); 418 if (last_slash) 419 file = last_slash + 1; 420 421 // TODO(darin): It might be nice if the columns were fixed width. 422 423 stream_ << '['; 424 if (log_process_id) 425 stream_ << CurrentProcessId() << ':'; 426 if (log_thread_id) 427 stream_ << CurrentThreadId() << ':'; 428 if (log_timestamp) { 429 time_t t = time(NULL); 430 struct tm local_time = {0}; 431 #if _MSC_VER >= 1400 432 localtime_s(&local_time, &t); 433 #else 434 localtime_r(&t, &local_time); 435 #endif 436 struct tm* tm_time = &local_time; 437 stream_ << std::setfill('0') 438 << std::setw(2) << 1 + tm_time->tm_mon 439 << std::setw(2) << tm_time->tm_mday 440 << '/' 441 << std::setw(2) << tm_time->tm_hour 442 << std::setw(2) << tm_time->tm_min 443 << std::setw(2) << tm_time->tm_sec 444 << ':'; 445 } 446 if (log_tickcount) 447 stream_ << TickCount() << ':'; 448 stream_ << log_severity_names[severity_] << ":" << file << 449 "(" << line << ")] "; 450 451 message_start_ = stream_.tellp(); 452 } 453 454 LogMessage::~LogMessage() { 455 // TODO(brettw) modify the macros so that nothing is executed when the log 456 // level is too high. 457 if (severity_ < min_log_level) 458 return; 459 460 std::string str_newline(stream_.str()); 461 #if defined(OS_WIN) 462 str_newline.append("\r\n"); 463 #else 464 str_newline.append("\n"); 465 #endif 466 // Give any log message handler first dibs on the message. 467 if (log_message_handler && log_message_handler(severity_, str_newline)) 468 return; 469 470 if (log_filter_prefix && severity_ <= kMaxFilteredLogLevel && 471 str_newline.compare(message_start_, log_filter_prefix->size(), 472 log_filter_prefix->data()) != 0) { 473 return; 474 } 475 476 if (logging_destination == LOG_ONLY_TO_SYSTEM_DEBUG_LOG || 477 logging_destination == LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG) { 478 #if defined(OS_WIN) 479 OutputDebugStringA(str_newline.c_str()); 480 if (severity_ >= kAlwaysPrintErrorLevel) { 481 #else 482 { 483 #endif 484 // TODO(erikkay): this interferes with the layout tests since it grabs 485 // stderr and stdout and diffs them against known data. Our info and warn 486 // logs add noise to that. Ideally, the layout tests would set the log 487 // level to ignore anything below error. When that happens, we should 488 // take this fprintf out of the #else so that Windows users can benefit 489 // from the output when running tests from the command-line. In the 490 // meantime, we leave this in for Mac and Linux, but until this is fixed 491 // they won't be able to pass any layout tests that have info or warn 492 // logs. See http://b/1343647 493 fprintf(stderr, "%s", str_newline.c_str()); 494 fflush(stderr); 495 } 496 } else if (severity_ >= kAlwaysPrintErrorLevel) { 497 // When we're only outputting to a log file, above a certain log level, we 498 // should still output to stderr so that we can better detect and diagnose 499 // problems with unit tests, especially on the buildbots. 500 fprintf(stderr, "%s", str_newline.c_str()); 501 fflush(stderr); 502 } 503 504 // write to log file 505 if (logging_destination != LOG_NONE && 506 logging_destination != LOG_ONLY_TO_SYSTEM_DEBUG_LOG && 507 InitializeLogFileHandle()) { 508 // We can have multiple threads and/or processes, so try to prevent them 509 // from clobbering each other's writes. 510 if (lock_log_file == LOCK_LOG_FILE) { 511 // Ensure that the mutex is initialized in case the client app did not 512 // call InitLogging. This is not thread safe. See below. 513 InitLogMutex(); 514 515 #if defined(OS_WIN) 516 ::WaitForSingleObject(log_mutex, INFINITE); 517 // WaitForSingleObject could have returned WAIT_ABANDONED. We don't 518 // abort the process here. UI tests might be crashy sometimes, 519 // and aborting the test binary only makes the problem worse. 520 // We also don't use LOG macros because that might lead to an infinite 521 // loop. For more info see http://crbug.com/18028. 522 #elif defined(OS_POSIX) 523 pthread_mutex_lock(&log_mutex); 524 #endif 525 } else { 526 // use the lock 527 if (!log_lock) { 528 // The client app did not call InitLogging, and so the lock has not 529 // been created. We do this on demand, but if two threads try to do 530 // this at the same time, there will be a race condition to create 531 // the lock. This is why InitLogging should be called from the main 532 // thread at the beginning of execution. 533 log_lock = new LockImpl(); 534 } 535 log_lock->Lock(); 536 } 537 538 #if defined(OS_WIN) 539 SetFilePointer(log_file, 0, 0, SEEK_END); 540 DWORD num_written; 541 WriteFile(log_file, 542 static_cast<const void*>(str_newline.c_str()), 543 static_cast<DWORD>(str_newline.length()), 544 &num_written, 545 NULL); 546 #else 547 fprintf(log_file, "%s", str_newline.c_str()); 548 fflush(log_file); 549 #endif 550 551 if (lock_log_file == LOCK_LOG_FILE) { 552 #if defined(OS_WIN) 553 ReleaseMutex(log_mutex); 554 #elif defined(OS_POSIX) 555 pthread_mutex_unlock(&log_mutex); 556 #endif 557 } else { 558 log_lock->Unlock(); 559 } 560 } 561 562 if (severity_ == LOG_FATAL) { 563 // display a message or break into the debugger on a fatal error 564 if (DebugUtil::BeingDebugged()) { 565 DebugUtil::BreakDebugger(); 566 } else { 567 #ifndef NDEBUG 568 // Dump a stack trace on a fatal. 569 StackTrace trace; 570 stream_ << "\n"; // Newline to separate from log message. 571 trace.OutputToStream(&stream_); 572 #endif 573 574 if (log_assert_handler) { 575 // make a copy of the string for the handler out of paranoia 576 log_assert_handler(std::string(stream_.str())); 577 } else { 578 // Don't use the string with the newline, get a fresh version to send to 579 // the debug message process. We also don't display assertions to the 580 // user in release mode. The enduser can't do anything with this 581 // information, and displaying message boxes when the application is 582 // hosed can cause additional problems. 583 #ifndef NDEBUG 584 DisplayDebugMessage(stream_.str()); 585 #endif 586 // Crash the process to generate a dump. 587 DebugUtil::BreakDebugger(); 588 } 589 } 590 } else if (severity_ == LOG_ERROR_REPORT) { 591 // We are here only if the user runs with --enable-dcheck in release mode. 592 if (log_report_handler) { 593 log_report_handler(std::string(stream_.str())); 594 } else { 595 DisplayDebugMessage(stream_.str()); 596 } 597 } 598 } 599 600 #if defined(OS_WIN) 601 // This has already been defined in the header, but defining it again as DWORD 602 // ensures that the type used in the header is equivalent to DWORD. If not, 603 // the redefinition is a compile error. 604 typedef DWORD SystemErrorCode; 605 #endif 606 607 SystemErrorCode GetLastSystemErrorCode() { 608 #if defined(OS_WIN) 609 return ::GetLastError(); 610 #elif defined(OS_POSIX) 611 return errno; 612 #else 613 #error Not implemented 614 #endif 615 } 616 617 #if defined(OS_WIN) 618 Win32ErrorLogMessage::Win32ErrorLogMessage(const char* file, 619 int line, 620 LogSeverity severity, 621 SystemErrorCode err, 622 const char* module) 623 : err_(err), 624 module_(module), 625 log_message_(file, line, severity) { 626 } 627 628 Win32ErrorLogMessage::Win32ErrorLogMessage(const char* file, 629 int line, 630 LogSeverity severity, 631 SystemErrorCode err) 632 : err_(err), 633 module_(NULL), 634 log_message_(file, line, severity) { 635 } 636 637 Win32ErrorLogMessage::~Win32ErrorLogMessage() { 638 const int error_message_buffer_size = 256; 639 char msgbuf[error_message_buffer_size]; 640 DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM; 641 HMODULE hmod; 642 if (module_) { 643 hmod = GetModuleHandleA(module_); 644 if (hmod) { 645 flags |= FORMAT_MESSAGE_FROM_HMODULE; 646 } else { 647 // This makes a nested Win32ErrorLogMessage. It will have module_ of NULL 648 // so it will not call GetModuleHandle, so recursive errors are 649 // impossible. 650 DPLOG(WARNING) << "Couldn't open module " << module_ 651 << " for error message query"; 652 } 653 } else { 654 hmod = NULL; 655 } 656 DWORD len = FormatMessageA(flags, 657 hmod, 658 err_, 659 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), 660 msgbuf, 661 sizeof(msgbuf) / sizeof(msgbuf[0]), 662 NULL); 663 if (len) { 664 while ((len > 0) && 665 isspace(static_cast<unsigned char>(msgbuf[len - 1]))) { 666 msgbuf[--len] = 0; 667 } 668 stream() << ": " << msgbuf; 669 } else { 670 stream() << ": Error " << GetLastError() << " while retrieving error " 671 << err_; 672 } 673 } 674 #elif defined(OS_POSIX) 675 ErrnoLogMessage::ErrnoLogMessage(const char* file, 676 int line, 677 LogSeverity severity, 678 SystemErrorCode err) 679 : err_(err), 680 log_message_(file, line, severity) { 681 } 682 683 ErrnoLogMessage::~ErrnoLogMessage() { 684 stream() << ": " << safe_strerror(err_); 685 } 686 #endif // OS_WIN 687 688 void CloseLogFile() { 689 if (!log_file) 690 return; 691 692 CloseFile(log_file); 693 log_file = NULL; 694 } 695 696 void RawLog(int level, const char* message) { 697 if (level >= min_log_level) { 698 size_t bytes_written = 0; 699 const size_t message_len = strlen(message); 700 int rv; 701 while (bytes_written < message_len) { 702 rv = HANDLE_EINTR( 703 write(STDERR_FILENO, message + bytes_written, 704 message_len - bytes_written)); 705 if (rv < 0) { 706 // Give up, nothing we can do now. 707 break; 708 } 709 bytes_written += rv; 710 } 711 712 if (message_len > 0 && message[message_len - 1] != '\n') { 713 do { 714 rv = HANDLE_EINTR(write(STDERR_FILENO, "\n", 1)); 715 if (rv < 0) { 716 // Give up, nothing we can do now. 717 break; 718 } 719 } while (rv != 1); 720 } 721 } 722 723 if (level == LOG_FATAL) 724 DebugUtil::BreakDebugger(); 725 } 726 727 } // namespace logging 728 729 std::ostream& operator<<(std::ostream& out, const wchar_t* wstr) { 730 return out << WideToUTF8(std::wstring(wstr)); 731 } 732