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