text: // Copyright 2006-2008 The Chromium Authors. All rights reserved.
  // Use of this source code is governed by a BSD-style license that can be
  // found in the LICENSE file.
  
  #include "base/logging.h"
  
  #include <stdio.h>
  #include <stdlib.h>
  
  #include <iomanip>
  
  #if defined(OS_POSIX)
  #include <paths.h>
  #include <sys/stat.h>
  #include <sys/time.h>
  #include <unistd.h>
  #include "base/posix/safe_strerror.h"
  #endif  // OS_POSIX
  
  #if defined(OS_APPLE)
  // In macOS 10.12 and iOS 10.0 and later ASL (Apple System Log) was deprecated
  // in favor of OS_LOG (Unified Logging).
  #include <AvailabilityMacros.h>
  #if defined(OS_IOS)
  #if !defined(__IPHONE_10_0) || __IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_10_0
  #define USE_ASL
  #endif
  #else  // !defined(OS_IOS)
  #if !defined(MAC_OS_X_VERSION_10_12) || \
      MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_12
  #define USE_ASL
  #endif
  #endif  // defined(OS_IOS)
  
  #if defined(USE_ASL)
  #include <asl.h>
  #else
  #include <os/log.h>
  #endif  // USE_ASL
  
  #include <CoreFoundation/CoreFoundation.h>
  #include <pthread.h>
  
  #elif defined(OS_LINUX)
  #include <sys/syscall.h>
  #include <sys/types.h>
  #elif defined(OS_WIN)
  #include <intrin.h>
  #include <windows.h>
  #elif defined(OS_FUCHSIA)
  #include <zircon/process.h>
  #include <zircon/syscalls.h>
  #endif
  
  #include "base/stl_util.h"
  #include "base/strings/string_util.h"
  #include "base/strings/stringprintf.h"
  #include "base/strings/utf_string_conversions.h"
  
  namespace logging {
  
  namespace {
  
  const char* const log_severity_names[] = {
    "INFO",
    "WARNING",
    "ERROR",
    "ERROR_REPORT",
    "FATAL"
  };
  
  LogMessageHandlerFunction g_log_message_handler = nullptr;
  
  }  // namespace
  
  void SetLogMessageHandler(LogMessageHandlerFunction log_message_handler) {
    g_log_message_handler = log_message_handler;
  }
  
  LogMessageHandlerFunction GetLogMessageHandler() {
    return g_log_message_handler;
  }
  
  #if defined(OS_WIN)
  std::string SystemErrorCodeToString(unsigned long error_code) {
    wchar_t msgbuf[256];
    DWORD flags = FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS |
                  FORMAT_MESSAGE_MAX_WIDTH_MASK;
    DWORD len = FormatMessage(flags,
                              nullptr,
                              error_code,
                              0,
                              msgbuf,
                              static_cast<DWORD>(base::size(msgbuf)),
                              nullptr);
    if (len) {
      // Most system messages end in a period and a space. Remove the space if
      // it’s there, because the following StringPrintf() includes one.
      if (len >= 1 && msgbuf[len - 1] == ' ') {
        msgbuf[len - 1] = '\0';
      }
      return base::StringPrintf("%s (%u)",
                                base::UTF16ToUTF8(msgbuf).c_str(), error_code);
    }
    return base::StringPrintf("Error %u while retrieving error %u",
                              GetLastError(),
                              error_code);
  }
  #endif  // OS_WIN
  
  #if defined(OS_FUCHSIA)
  zx_koid_t GetKoidForHandle(zx_handle_t handle) {
    // Get the 64-bit koid (unique kernel object ID) of the given handle.
    zx_koid_t koid = 0;
    zx_info_handle_basic_t info;
    if (zx_object_get_info(handle,
                           ZX_INFO_HANDLE_BASIC,
                           &info,
                           sizeof(info),
                           nullptr,
                           nullptr) == ZX_OK) {
      // If this fails, there's not much that can be done. As this is used only
      // for logging, leave it as 0, which is not a valid koid.
      koid = info.koid;
    }
    return koid;
  }
  #endif  // OS_FUCHSIA
  
  LogMessage::LogMessage(const char* function,
                         const char* file_path,
                         int line,
                         LogSeverity severity)
      : stream_(),
        file_path_(file_path),
        message_start_(0),
        line_(line),
        severity_(severity) {
    Init(function);
  }
  
  LogMessage::LogMessage(const char* function,
                         const char* file_path,
                         int line,
                         std::string* result)
      : stream_(),
        file_path_(file_path),
        message_start_(0),
        line_(line),
        severity_(LOG_FATAL) {
    Init(function);
    stream_ << "Check failed: " << *result << ". ";
    delete result;
  }
  
  LogMessage::~LogMessage() {
    stream_ << std::endl;
    std::string str_newline(stream_.str());
  
    if (g_log_message_handler &&
        g_log_message_handler(
            severity_, file_path_, line_, message_start_, str_newline)) {
      return;
    }
  
    fprintf(stderr, "%s", str_newline.c_str());
    fflush(stderr);
  
  #if defined(OS_APPLE)
    const bool log_to_system = []() {
      struct stat stderr_stat;
      if (fstat(fileno(stderr), &stderr_stat) == -1) {
        return true;
      }
      if (!S_ISCHR(stderr_stat.st_mode)) {
        return false;
      }
  
      struct stat dev_null_stat;
      if (stat(_PATH_DEVNULL, &dev_null_stat) == -1) {
        return true;
      }
  
      return !S_ISCHR(dev_null_stat.st_mode) ||
             stderr_stat.st_rdev == dev_null_stat.st_rdev;
    }();
  
    if (log_to_system) {
      CFBundleRef main_bundle = CFBundleGetMainBundle();
      CFStringRef main_bundle_id_cf =
          main_bundle ? CFBundleGetIdentifier(main_bundle) : nullptr;
  
      std::string main_bundle_id_buf;
      const char* main_bundle_id = nullptr;
  
      if (main_bundle_id_cf) {
        main_bundle_id =
            CFStringGetCStringPtr(main_bundle_id_cf, kCFStringEncodingUTF8);
        if (!main_bundle_id) {
          // 1024 is from 10.10.5 CF-1153.18/CFBundle.c __CFBundleMainID__ (at
          // the point of use, not declaration).
          main_bundle_id_buf.resize(1024);
          if (!CFStringGetCString(main_bundle_id_cf,
                                  &main_bundle_id_buf[0],
                                  main_bundle_id_buf.size(),
                                  kCFStringEncodingUTF8)) {
            main_bundle_id_buf.clear();
          } else {
            main_bundle_id = &main_bundle_id_buf[0];
          }
        }
      }
  
  #if defined(USE_ASL)
      // Use ASL when this might run on pre-10.12 systems. Unified Logging
      // (os_log) was introduced in 10.12.
  
      const class ASLClient {
       public:
        explicit ASLClient(const char* asl_facility)
            : client_(asl_open(nullptr, asl_facility, ASL_OPT_NO_DELAY)) {}
        ~ASLClient() { asl_close(client_); }
  
        aslclient get() const { return client_; }
  
       private:
        aslclient client_;
        DISALLOW_COPY_AND_ASSIGN(ASLClient);
      } asl_client(main_bundle_id ? main_bundle_id : "com.apple.console");
  
      const class ASLMessage {
       public:
        ASLMessage() : message_(asl_new(ASL_TYPE_MSG)) {}
        ~ASLMessage() { asl_free(message_); }
  
        aslmsg get() const { return message_; }
  
       private:
        aslmsg message_;
        DISALLOW_COPY_AND_ASSIGN(ASLMessage);
      } asl_message;
  
      // By default, messages are only readable by the admin group. Explicitly
      // make them readable by the user generating the messages.
      char euid_string[12];
      snprintf(euid_string, base::size(euid_string), "%d", geteuid());
      asl_set(asl_message.get(), ASL_KEY_READ_UID, euid_string);
  
      // Map Chrome log severities to ASL log levels.
      const char* const asl_level_string = [](LogSeverity severity) {
  #define ASL_LEVEL_STR(level) ASL_LEVEL_STR_X(level)
  #define ASL_LEVEL_STR_X(level) #level
          switch (severity) {
            case LOG_INFO:
              return ASL_LEVEL_STR(ASL_LEVEL_INFO);
            case LOG_WARNING:
              return ASL_LEVEL_STR(ASL_LEVEL_WARNING);
            case LOG_ERROR:
              return ASL_LEVEL_STR(ASL_LEVEL_ERR);
            case LOG_FATAL:
              return ASL_LEVEL_STR(ASL_LEVEL_CRIT);
            default:
              return severity < 0 ? ASL_LEVEL_STR(ASL_LEVEL_DEBUG)
                                  : ASL_LEVEL_STR(ASL_LEVEL_NOTICE);
          }
  #undef ASL_LEVEL_STR
  #undef ASL_LEVEL_STR_X
      }(severity_);
      asl_set(asl_message.get(), ASL_KEY_LEVEL, asl_level_string);
  
      asl_set(asl_message.get(), ASL_KEY_MSG, str_newline.c_str());
  
      asl_send(asl_client.get(), asl_message.get());
  #else
      // Use Unified Logging (os_log) when this will only run on 10.12 and later.
      // ASL is deprecated in 10.12.
  
      const class OSLog {
       public:
        explicit OSLog(const char* subsystem)
            : os_log_(subsystem ? os_log_create(subsystem, "chromium_logging")
                                : OS_LOG_DEFAULT) {}
        ~OSLog() {
          if (os_log_ != OS_LOG_DEFAULT) {
            os_release(os_log_);
          }
        }
  
        os_log_t get() const { return os_log_; }
  
       private:
        os_log_t os_log_;
        DISALLOW_COPY_AND_ASSIGN(OSLog);
      } log(main_bundle_id);
  
      const os_log_type_t os_log_type = [](LogSeverity severity) {
        switch (severity) {
          case LOG_INFO:
            return OS_LOG_TYPE_INFO;
          case LOG_WARNING:
            return OS_LOG_TYPE_DEFAULT;
          case LOG_ERROR:
            return OS_LOG_TYPE_ERROR;
          case LOG_FATAL:
            return OS_LOG_TYPE_FAULT;
          default:
            return severity < 0 ? OS_LOG_TYPE_DEBUG : OS_LOG_TYPE_DEFAULT;
        }
      }(severity_);
  
      os_log_with_type(log.get(), os_log_type, "%{public}s", str_newline.c_str());
  #endif
    }
  #elif defined(OS_WIN)
    OutputDebugString(base::UTF8ToUTF16(str_newline).c_str());
  #endif  // OS_*
  
    if (severity_ == LOG_FATAL) {
  #if defined(COMPILER_MSVC)
      __debugbreak();
  #if defined(ARCH_CPU_X86_FAMILY)
      __ud2();
  #elif defined(ARCH_CPU_ARM64)
      __hlt(0);
  #else
  #error Unsupported Windows Arch
  #endif
  #elif defined(ARCH_CPU_X86_FAMILY)
      asm("int3; ud2;");
  #elif defined(ARCH_CPU_ARMEL)
      asm("bkpt #0; udf #0;");
  #elif defined(ARCH_CPU_ARM64)
      asm("brk #0; hlt #0;");
  #else
      __builtin_trap();
  #endif
    }
  }
  
  void LogMessage::Init(const char* function) {
    std::string file_name(file_path_);
  #if defined(OS_WIN)
    size_t last_slash = file_name.find_last_of("\\/");
  #else
    size_t last_slash = file_name.find_last_of('/');
  #endif
    if (last_slash != std::string::npos) {
      file_name.assign(file_name.substr(last_slash + 1));
    }
  
  #if defined(OS_FUCHSIA)
    zx_koid_t pid = GetKoidForHandle(zx_process_self());
  #elif defined(OS_POSIX)
    pid_t pid = getpid();
  #elif defined(OS_WIN)
    DWORD pid = GetCurrentProcessId();
  #endif
  
  #if defined(OS_APPLE)
    uint64_t thread;
    pthread_threadid_np(pthread_self(), &thread);
  #elif defined(OS_ANDROID)
    pid_t thread = gettid();
  #elif defined(OS_LINUX)
    pid_t thread = syscall(__NR_gettid);
  #elif defined(OS_WIN)
    DWORD thread = GetCurrentThreadId();
  #elif defined(OS_FUCHSIA)
    zx_koid_t thread = GetKoidForHandle(zx_thread_self());
  #endif
  
    stream_ << '['
            << pid
            << ':'
            << thread
            << ':'
            << std::setfill('0');
  
  #if defined(OS_POSIX)
    timeval tv;
    gettimeofday(&tv, nullptr);
    tm local_time;
    localtime_r(&tv.tv_sec, &local_time);
    stream_ << std::setw(4) << local_time.tm_year + 1900
            << std::setw(2) << local_time.tm_mon + 1
            << std::setw(2) << local_time.tm_mday
            << ','
            << std::setw(2) << local_time.tm_hour
            << std::setw(2) << local_time.tm_min
            << std::setw(2) << local_time.tm_sec
            << '.'
            << std::setw(6) << tv.tv_usec;
  #elif defined(OS_WIN)
    SYSTEMTIME local_time;
    GetLocalTime(&local_time);
    stream_ << std::setw(4) << local_time.wYear
            << std::setw(2) << local_time.wMonth
            << std::setw(2) << local_time.wDay
            << ','
            << std::setw(2) << local_time.wHour
            << std::setw(2) << local_time.wMinute
            << std::setw(2) << local_time.wSecond
            << '.'
            << std::setw(3) << local_time.wMilliseconds;
  #endif
  
    stream_ << ':';
  
    if (severity_ >= 0) {
      stream_ << log_severity_names[severity_];
    } else {
      stream_ << "VERBOSE" << -severity_;
    }
  
    stream_ << ' '
            << file_name
            << ':'
            << line_
            << "] ";
  
    message_start_ = stream_.str().size();
  }
  
  #if defined(OS_WIN)
  
  unsigned long GetLastSystemErrorCode() {
    return GetLastError();
  }
  
  Win32ErrorLogMessage::Win32ErrorLogMessage(const char* function,
                                             const char* file_path,
                                             int line,
                                             LogSeverity severity,
                                             unsigned long err)
      : LogMessage(function, file_path, line, severity), err_(err) {
  }
  
  Win32ErrorLogMessage::~Win32ErrorLogMessage() {
    stream() << ": " << SystemErrorCodeToString(err_);
  }
  
  #elif defined(OS_POSIX)
  
  ErrnoLogMessage::ErrnoLogMessage(const char* function,
                                   const char* file_path,
                                   int line,
                                   LogSeverity severity,
                                   int err)
      : LogMessage(function, file_path, line, severity),
        err_(err) {
  }
  
  ErrnoLogMessage::~ErrnoLogMessage() {
    stream() << ": "
             << base::safe_strerror(err_)
             << " ("
             << err_
             << ")";
  }
  
  #endif  // OS_POSIX
  
  }  // namespace logging
  