chromium/base/logging.cc

// Copyright 2012 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifdef UNSAFE_BUFFERS_BUILD
// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
#pragma allow_unsafe_buffers
#endif

#include "base/logging.h"

#ifdef BASE_CHECK_H_
#error "logging.h should not include check.h"
#endif

#include <limits.h>
#include <stdint.h>

#include <algorithm>
#include <atomic>
#include <cstring>
#include <ctime>
#include <iomanip>
#include <memory>
#include <ostream>
#include <string>
#include <string_view>
#include <tuple>
#include <utility>
#include <vector>

#include "base/base_export.h"
#include "base/base_switches.h"
#include "base/command_line.h"
#include "base/containers/stack.h"
#include "base/debug/alias.h"
#include "base/debug/crash_logging.h"
#include "base/debug/debugger.h"
#include "base/debug/stack_trace.h"
#include "base/debug/task_trace.h"
#include "base/functional/callback.h"
#include "base/immediate_crash.h"
#include "base/no_destructor.h"
#include "base/not_fatal_until.h"
#include "base/path_service.h"
#include "base/pending_task.h"
#include "base/posix/eintr_wrapper.h"
#include "base/process/process_handle.h"
#include "base/scoped_clear_last_error.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/strings/sys_string_conversions.h"
#include "base/strings/utf_string_conversions.h"
#include "base/synchronization/lock.h"
#include "base/task/common/task_annotator.h"
#include "base/test/scoped_logging_settings.h"
#include "base/threading/platform_thread.h"
#include "base/trace_event/base_tracing.h"
#include "base/vlog.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "third_party/abseil-cpp/absl/base/internal/raw_logging.h"
#include "third_party/abseil-cpp/absl/cleanup/cleanup.h"

#if !BUILDFLAG(IS_NACL)
#include "base/auto_reset.h"
#include "base/debug/crash_logging.h"
#endif  // !BUILDFLAG(IS_NACL)

#if defined(LEAK_SANITIZER) && !BUILDFLAG(IS_NACL)
#include "base/debug/leak_annotations.h"
#endif  // defined(LEAK_SANITIZER) && !BUILDFLAG(IS_NACL)

#if BUILDFLAG(IS_WIN)
#include <windows.h>

#include <io.h>

#include "base/win/win_util.h"

typedef HANDLE FileHandle;
// Windows warns on using write().  It prefers _write().
#define write
// Windows doesn't define STDERR_FILENO.  Define it here.
#define STDERR_FILENO
#endif  // BUILDFLAG(IS_WIN)

#if BUILDFLAG(IS_APPLE)
#include <CoreFoundation/CoreFoundation.h>
#include <mach-o/dyld.h>
#include <mach/mach.h>
#include <mach/mach_time.h>
#include <os/log.h>
#endif  // BUILDFLAG(IS_APPLE)

#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
#include <errno.h>
#include <paths.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
#include <time.h>

#include "base/posix/safe_strerror.h"

#if BUILDFLAG(IS_NACL)
#include <sys/time.h>  // timespec doesn't seem to be in <time.h>
#endif

#define MAX_PATH
FileHandle;
#endif  // BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)

#if BUILDFLAG(IS_ANDROID)
#include <android/log.h>
#include "base/android/jni_android.h"
#endif

#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "base/files/scoped_file.h"
#endif

#if BUILDFLAG(IS_FUCHSIA)
#include "base/fuchsia/scoped_fx_logger.h"
#endif

namespace logging {

namespace {

int g_min_log_level =;

// NOTE: Once |g_vlog_info| has been initialized, it might be in use
// by another thread. Never delete the old VLogInfo, just create a second
// one and overwrite. We need to use leak-san annotations on this intentional
// leak.
//
// This can be read/written on multiple threads. In tests we don't see that
// causing a problem as updates tend to happen early. Atomic ensures there are
// no problems. To avoid some of the overhead of Atomic, we use
// |load(std::memory_order_acquire)| and |store(...,
// std::memory_order_release)| when reading or writing. This guarantees that the
// referenced object is available at the time the |g_vlog_info| is read and that
// |g_vlog_info| is updated atomically.
//
// Do not access this directly. You must use |GetVlogInfo|, |InitializeVlogInfo|
// and/or |ExchangeVlogInfo|.
std::atomic<VlogInfo*> g_vlog_info =;

VlogInfo* GetVlogInfo() {}

// Sets g_vlog_info if it is not already set. Checking that it's not already set
// prevents logging initialization (which can come late in test setup) from
// overwriting values set via ScopedVmoduleSwitches.
bool InitializeVlogInfo(VlogInfo* vlog_info) {}

VlogInfo* ExchangeVlogInfo(VlogInfo* vlog_info) {}

// Creates a VlogInfo from the commandline if it has been initialized and if it
// contains relevant switches, otherwise this returns |nullptr|.
std::unique_ptr<VlogInfo> VlogInfoFromCommandLine() {}

// If the commandline is initialized for the current process this will
// initialize g_vlog_info. If there are no VLOG switches, it will initialize it
// to |nullptr|.
void MaybeInitializeVlogInfo() {}

const char* const log_severity_names[] =;
static_assert;

const char* log_severity_name(int severity) {}

// Specifies the process' logging sink(s), represented as a combination of
// LoggingDestination values joined by bitwise OR.
uint32_t g_logging_destination =;

#if BUILDFLAG(IS_CHROMEOS)
// Specifies the format of log header for chrome os.
LogFormat g_log_format = LogFormat::LOG_FORMAT_SYSLOG;
#endif

#if BUILDFLAG(IS_FUCHSIA)
// Retains system logging structures.
base::ScopedFxLogger& GetScopedFxLogger() {
  static base::NoDestructor<base::ScopedFxLogger> logger;
  return *logger;
}
#endif

// For LOGGING_ERROR and above, always print to stderr.
const int kAlwaysPrintErrorLevel =;

// Which log file to use? This is initialized by InitLogging or
// will be lazily initialized to the default value when it is
// first needed.
PathString;
PathString* g_log_file_name =;

// This file is lazily opened and the handle may be nullptr
FileHandle g_log_file =;

// What should be prepended to each message?
bool g_log_process_id =;
bool g_log_thread_id =;
bool g_log_timestamp =;
bool g_log_tickcount =;
const char* g_log_prefix =;

// Should we pop up fatal debug messages in a dialog?
bool show_error_dialogs =;

// An assert handler override specified by the client to be called instead of
// the debug message dialog and process termination. Assert handlers are stored
// in stack to allow overriding and restoring.
base::stack<LogAssertHandlerFunction>& GetLogAssertHandlerStack() {}

// A log message handler that gets notified of every log message we process.
LogMessageHandlerFunction g_log_message_handler =;

uint64_t TickCount() {}

void DeleteFilePath(const PathString& log_name) {}

PathString GetDefaultLogFile() {}

// We don't need locks on Windows for atomically appending to files. The OS
// provides this functionality.
#if BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)

// Provides a lock to synchronize appending to the log file across
// threads. This can be required to support NFS file systems even on OSes that
// provide atomic append operations in most cases. It should be noted that this
// lock is not not shared across processes. When using NFS filesystems
// protection against clobbering between different processes will be best-effort
// and provided by the OS. See
// https://man7.org/linux/man-pages/man2/open.2.html.
//
// The lock also protects initializing and closing the log file which can
// happen concurrently with logging on some platforms like ChromeOS that need to
// redirect logging by calling BaseInitLoggingImpl() twice.
base::Lock& GetLoggingLock() {}

#endif  // BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)

// Called by logging functions to ensure that |g_log_file| is initialized
// and can be used for writing. Returns false if the file could not be
// initialized. |g_log_file| will be nullptr in this case.
bool InitializeLogFileHandle() {}

void CloseFile(FileHandle log) {}

void CloseLogFileUnlocked() {}

#if BUILDFLAG(IS_FUCHSIA)
inline FuchsiaLogSeverity LogSeverityToFuchsiaLogSeverity(
    LogSeverity severity) {
  switch (severity) {
    case LOGGING_INFO:
      return FUCHSIA_LOG_INFO;
    case LOGGING_WARNING:
      return FUCHSIA_LOG_WARNING;
    case LOGGING_ERROR:
      return FUCHSIA_LOG_ERROR;
    case LOGGING_FATAL:
      // Don't use FX_LOG_FATAL, otherwise fx_logger_log() will abort().
      return FUCHSIA_LOG_ERROR;
  }
  if (severity > -3) {
    // LOGGING_VERBOSE levels 1 and 2.
    return FUCHSIA_LOG_DEBUG;
  }
  // LOGGING_VERBOSE levels 3 and higher, or incorrect levels.
  return FUCHSIA_LOG_TRACE;
}
#endif  // BUILDFLAG(IS_FUCHSIA)

void WriteToFd(int fd, const char* data, size_t length) {}

void SetLogFatalCrashKey(LogMessage* log_message) {}

std::string BuildCrashString(const char* file,
                             int line,
                             const char* message_without_prefix) {}

// Invokes macro to record trace event when a log message is emitted.
void TraceLogMessage(const char* file, int line, const std::string& message) {}

}  // namespace

#if BUILDFLAG(DCHECK_IS_CONFIGURABLE)
// In DCHECK-enabled Chrome builds, allow the meaning of LOGGING_DCHECK to be
// determined at run-time. We default it to ERROR, to avoid it triggering
// crashes before the run-time has explicitly chosen the behaviour.
BASE_EXPORT logging::LogSeverity LOGGING_DCHECK = LOGGING_ERROR;
#endif  // BUILDFLAG(DCHECK_IS_CONFIGURABLE)

// This is never instantiated, it's just used for EAT_STREAM_PARAMETERS to have
// an object of the correct type on the LHS of the unused part of the ternary
// operator.
std::ostream* g_swallow_stream;

bool BaseInitLoggingImpl(const LoggingSettings& settings) {}

void SetMinLogLevel(int level) {}

int GetMinLogLevel() {}

bool ShouldCreateLogMessage(int severity) {}

// Returns true when LOG_TO_STDERR flag is set, or |severity| is high.
// If |severity| is high then true will be returned when no log destinations are
// set, or only LOG_TO_FILE is set, since that is useful for local development
// and debugging.
bool ShouldLogToStderr(int severity) {}

int GetVlogVerbosity() {}

int GetVlogLevelHelper(const char* file, size_t N) {}

void SetLogItems(bool enable_process_id, bool enable_thread_id,
                 bool enable_timestamp, bool enable_tickcount) {}

void SetLogPrefix(const char* prefix) {}

void SetShowErrorDialogs(bool enable_dialogs) {}

namespace {

[[noreturn]] void AbslAbortHook(const char* file,
                                int line,
                                const char* buf_start,
                                const char* prefix_end,
                                const char* buf_end) {}

}  // namespace

void RegisterAbslAbortHook() {}

ScopedLogAssertHandler::ScopedLogAssertHandler(
    LogAssertHandlerFunction handler) {}

ScopedLogAssertHandler::~ScopedLogAssertHandler() {}

void SetLogMessageHandler(LogMessageHandlerFunction handler) {}

LogMessageHandlerFunction GetLogMessageHandler() {}

#if !defined(NDEBUG)
// Displays a message box to the user with the error message in it.
// Used for fatal messages, where we close the app simultaneously.
// This is for developers only; we don't use this in circumstances
// (like release builds) where users could see it, since users don't
// understand these messages anyway.
void DisplayDebugMessageInDialog(const std::string& str) {}
#endif  // !defined(NDEBUG)

LogMessage::LogMessage(const char* file, int line, LogSeverity severity)
    :{}

LogMessage::LogMessage(const char* file, int line, const char* condition)
    :{}

LogMessage::~LogMessage() {}

void LogMessage::Flush() {}

std::string LogMessage::BuildCrashString() const {}

// writes the common header info to the stream
void LogMessage::Init(const char* file, int line) {}

void LogMessage::HandleFatal(size_t stack_start,
                             const std::string& str_newline) const {}

LogMessageFatal::~LogMessageFatal() {}

#if BUILDFLAG(IS_WIN)
// This has already been defined in the header, but defining it again as DWORD
// ensures that the type used in the header is equivalent to DWORD. If not,
// the redefinition is a compile error.
typedef DWORD SystemErrorCode;
#endif

SystemErrorCode GetLastSystemErrorCode() {}

BASE_EXPORT std::string SystemErrorCodeToString(SystemErrorCode error_code) {}

#if BUILDFLAG(IS_WIN)
Win32ErrorLogMessage::Win32ErrorLogMessage(const char* file,
                                           int line,
                                           LogSeverity severity,
                                           SystemErrorCode err)
    : LogMessage(file, line, severity), err_(err) {}

Win32ErrorLogMessage::~Win32ErrorLogMessage() {
  AppendError();
}

void Win32ErrorLogMessage::AppendError() {
  // Don't let actions from this method affect the system error after returning.
  base::ScopedClearLastError scoped_clear_last_error;

  stream() << ": " << SystemErrorCodeToString(err_);
  // We're about to crash (CHECK). Put |err_| on the stack (by placing it in a
  // field) and use Alias in hopes that it makes it into crash dumps.
  DWORD last_error = err_;
  base::debug::Alias(&last_error);
}

Win32ErrorLogMessageFatal::~Win32ErrorLogMessageFatal() {
  AppendError();
  Flush();
  base::ImmediateCrash();
}

#elif BUILDFLAG(IS_POSIX) || BUILDFLAG(IS_FUCHSIA)
ErrnoLogMessage::ErrnoLogMessage(const char* file,
                                 int line,
                                 LogSeverity severity,
                                 SystemErrorCode err)
    :{}

ErrnoLogMessage::~ErrnoLogMessage() {}

void ErrnoLogMessage::AppendError() {}

ErrnoLogMessageFatal::~ErrnoLogMessageFatal() {}

#endif  // BUILDFLAG(IS_WIN)

void CloseLogFile() {}

#if BUILDFLAG(IS_CHROMEOS_ASH)
FILE* DuplicateLogFILE() {
  if ((g_logging_destination & LOG_TO_FILE) == 0 || !InitializeLogFileHandle())
    return nullptr;

  int log_fd = fileno(g_log_file);
  if (log_fd == -1)
    return nullptr;
  base::ScopedFD dup_fd(dup(log_fd));
  if (dup_fd == -1)
    return nullptr;
  FILE* duplicate = fdopen(dup_fd.get(), "a");
  if (!duplicate)
    return nullptr;
  std::ignore = dup_fd.release();
  return duplicate;
}
#endif

#if BUILDFLAG(IS_WIN)
HANDLE DuplicateLogFileHandle() {
  // `g_log_file` should only be valid, or nullptr, but be very careful that we
  // do not duplicate INVALID_HANDLE_VALUE as it aliases the process handle.
  if (!(g_logging_destination & LOG_TO_FILE) || !g_log_file ||
      g_log_file == INVALID_HANDLE_VALUE) {
    return nullptr;
  }
  HANDLE duplicate = nullptr;
  if (!::DuplicateHandle(::GetCurrentProcess(), g_log_file,
                         ::GetCurrentProcess(), &duplicate, 0,
                         /*bInheritHandle=*/TRUE, DUPLICATE_SAME_ACCESS)) {
    return nullptr;
  }
  return duplicate;
}
#endif

// Used for testing. Declared in test/scoped_logging_settings.h.
ScopedLoggingSettings::ScopedLoggingSettings()
    :{}

ScopedLoggingSettings::~ScopedLoggingSettings() {}

#if BUILDFLAG(IS_CHROMEOS)
void ScopedLoggingSettings::SetLogFormat(LogFormat log_format) const {
  g_log_format = log_format;
}
#endif  // BUILDFLAG(IS_CHROMEOS_ASH)

void RawLog(int level, const char* message) {}

// This was defined at the beginning of this file.
#undef write

#if BUILDFLAG(IS_WIN)
bool IsLoggingToFileEnabled() {
  return g_logging_destination & LOG_TO_FILE;
}

std::wstring GetLogFileFullPath() {
  if (g_log_file_name)
    return *g_log_file_name;
  return std::wstring();
}
#endif

// Used for testing. Declared in test/scoped_logging_settings.h.
ScopedVmoduleSwitches::ScopedVmoduleSwitches() = default;

VlogInfo* ScopedVmoduleSwitches::CreateVlogInfoWithSwitches(
    const std::string& vmodule_switch) {}

void ScopedVmoduleSwitches::InitWithSwitches(
    const std::string& vmodule_switch) {}

ScopedVmoduleSwitches::~ScopedVmoduleSwitches() {}

}  // namespace logging