logging.cc revision c7f5f8508d98d5952d42ed7648c2a8f30a4da156
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> 10typedef HANDLE FileHandle; 11typedef 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 33typedef FILE* FileHandle; 34typedef 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 54namespace logging { 55 56bool g_enable_dcheck = false; 57 58const char* const log_severity_names[LOG_NUM_SEVERITIES] = { 59 "INFO", "WARNING", "ERROR", "ERROR_REPORT", "FATAL" }; 60 61int min_log_level = 0; 62LogLockingState 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) 69LoggingDestination logging_destination = LOG_ONLY_TO_FILE; 70#elif defined(OS_POSIX) 71LoggingDestination logging_destination = LOG_ONLY_TO_SYSTEM_DEBUG_LOG; 72#endif 73 74const int kMaxFilteredLogLevel = LOG_WARNING; 75std::string* log_filter_prefix; 76 77// For LOG_ERROR and above, always print to stderr. 78const 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) 84typedef wchar_t PathChar; 85typedef std::wstring PathString; 86#else 87typedef char PathChar; 88typedef std::string PathString; 89#endif 90PathString* log_file_name = NULL; 91 92// this file is lazily opened and the handle may be NULL 93FileHandle log_file = NULL; 94 95// what should be prepended to each message? 96bool log_process_id = false; 97bool log_thread_id = false; 98bool log_timestamp = true; 99bool 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. 103LogAssertHandlerFunction log_assert_handler = NULL; 104// An report handler override specified by the client to be called instead of 105// the debug message dialog. 106LogReportHandlerFunction log_report_handler = NULL; 107// A log message handler that gets notified of every log message we process. 108LogMessageHandlerFunction 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. 113static 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) 118MutexHandle log_mutex = NULL; 119#elif defined(OS_POSIX) 120pthread_mutex_t log_mutex = PTHREAD_MUTEX_INITIALIZER; 121#endif 122 123// Helper functions to wrap platform differences. 124 125int32 CurrentProcessId() { 126#if defined(OS_WIN) 127 return GetCurrentProcessId(); 128#elif defined(OS_POSIX) 129 return getpid(); 130#endif 131} 132 133int32 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 146uint64 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 163void CloseFile(FileHandle log) { 164#if defined(OS_WIN) 165 CloseHandle(log); 166#else 167 fclose(log); 168#endif 169} 170 171void 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. 182bool 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) 233int 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 242void 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 257void 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 292void SetMinLogLevel(int level) { 293 min_log_level = level; 294} 295 296int GetMinLogLevel() { 297 return min_log_level; 298} 299 300void 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 310void 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 318void SetLogAssertHandler(LogAssertHandlerFunction handler) { 319 log_assert_handler = handler; 320} 321 322void SetLogReportHandler(LogReportHandlerFunction handler) { 323 log_report_handler = handler; 324} 325 326void 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. 338void 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) 377LogMessage::SaveLastError::SaveLastError() : last_error_(::GetLastError()) { 378} 379 380LogMessage::SaveLastError::~SaveLastError() { 381 ::SetLastError(last_error_); 382} 383#endif // defined(OS_WIN) 384 385LogMessage::LogMessage(const char* file, int line, LogSeverity severity, 386 int ctr) 387 : severity_(severity) { 388 Init(file, line); 389} 390 391LogMessage::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 397LogMessage::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 404LogMessage::LogMessage(const char* file, int line) 405 : severity_(LOG_INFO) { 406 Init(file, line); 407} 408 409LogMessage::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 415void 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 454LogMessage::~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. 604typedef DWORD SystemErrorCode; 605#endif 606 607SystemErrorCode 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) 618Win32ErrorLogMessage::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 628Win32ErrorLogMessage::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 637Win32ErrorLogMessage::~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) 675ErrnoLogMessage::ErrnoLogMessage(const char* file, 676 int line, 677 LogSeverity severity, 678 SystemErrorCode err) 679 : err_(err), 680 log_message_(file, line, severity) { 681} 682 683ErrnoLogMessage::~ErrnoLogMessage() { 684 stream() << ": " << safe_strerror(err_); 685} 686#endif // OS_WIN 687 688void CloseLogFile() { 689 if (!log_file) 690 return; 691 692 CloseFile(log_file); 693 log_file = NULL; 694} 695 696void 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 729std::ostream& operator<<(std::ostream& out, const wchar_t* wstr) { 730 return out << WideToUTF8(std::wstring(wstr)); 731} 732