platform_system_core/base/logging.cpp
Tom Cherry d6699b6b81 liblog: correct new APIs per the API review
1) Rename __android_logger_data to __android_log_message and rename
   __android_log_write_logger_data to
   __android_log_write_log_message. Move the const char* message
   argument into __android_log_message.
2) Add @param, @return, and "Available since API level 30." to the
   documentation of new functions.
3) Document that the user defined aborter should but may not abort.
4) Document the line separation is the responsibility of the log
   function provided to __android_log_set_logger().

Bug: 150898477
Test: build, liblog and libbase unit tests
Change-Id: I07c41011ef25b3e7cc4943f3f1e240a2f6aa2802
2020-03-16 15:31:52 -07:00

639 lines
19 KiB
C++

/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#if defined(_WIN32)
#include <windows.h>
#endif
#include "android-base/logging.h"
#include <fcntl.h>
#include <inttypes.h>
#include <libgen.h>
#include <time.h>
// For getprogname(3) or program_invocation_short_name.
#if defined(__ANDROID__) || defined(__APPLE__)
#include <stdlib.h>
#elif defined(__GLIBC__)
#include <errno.h>
#endif
#if defined(__linux__)
#include <sys/uio.h>
#endif
#include <atomic>
#include <iostream>
#include <limits>
#include <mutex>
#include <optional>
#include <sstream>
#include <string>
#include <utility>
#include <vector>
#include <android/log.h>
#ifdef __ANDROID__
#include <android/set_abort_message.h>
#else
#include <sys/types.h>
#include <unistd.h>
#endif
#include <android-base/file.h>
#include <android-base/macros.h>
#include <android-base/parseint.h>
#include <android-base/strings.h>
#include <android-base/threads.h>
#include "liblog_symbols.h"
namespace android {
namespace base {
// BSD-based systems like Android/macOS have getprogname(). Others need us to provide one.
#if defined(__GLIBC__) || defined(_WIN32)
static const char* getprogname() {
#if defined(__GLIBC__)
return program_invocation_short_name;
#elif defined(_WIN32)
static bool first = true;
static char progname[MAX_PATH] = {};
if (first) {
snprintf(progname, sizeof(progname), "%s",
android::base::Basename(android::base::GetExecutablePath()).c_str());
first = false;
}
return progname;
#endif
}
#endif
static const char* GetFileBasename(const char* file) {
// We can't use basename(3) even on Unix because the Mac doesn't
// have a non-modifying basename.
const char* last_slash = strrchr(file, '/');
if (last_slash != nullptr) {
return last_slash + 1;
}
#if defined(_WIN32)
const char* last_backslash = strrchr(file, '\\');
if (last_backslash != nullptr) {
return last_backslash + 1;
}
#endif
return file;
}
#if defined(__linux__)
static int OpenKmsg() {
#if defined(__ANDROID__)
// pick up 'file w /dev/kmsg' environment from daemon's init rc file
const auto val = getenv("ANDROID_FILE__dev_kmsg");
if (val != nullptr) {
int fd;
if (android::base::ParseInt(val, &fd, 0)) {
auto flags = fcntl(fd, F_GETFL);
if ((flags != -1) && ((flags & O_ACCMODE) == O_WRONLY)) return fd;
}
}
#endif
return TEMP_FAILURE_RETRY(open("/dev/kmsg", O_WRONLY | O_CLOEXEC));
}
#endif
static LogId log_id_tToLogId(int32_t buffer_id) {
switch (buffer_id) {
case LOG_ID_MAIN:
return MAIN;
case LOG_ID_SYSTEM:
return SYSTEM;
case LOG_ID_RADIO:
return RADIO;
case LOG_ID_CRASH:
return CRASH;
case LOG_ID_DEFAULT:
default:
return DEFAULT;
}
}
static int32_t LogIdTolog_id_t(LogId log_id) {
switch (log_id) {
case MAIN:
return LOG_ID_MAIN;
case SYSTEM:
return LOG_ID_SYSTEM;
case RADIO:
return LOG_ID_RADIO;
case CRASH:
return LOG_ID_CRASH;
case DEFAULT:
default:
return LOG_ID_DEFAULT;
}
}
static LogSeverity PriorityToLogSeverity(int priority) {
switch (priority) {
case ANDROID_LOG_DEFAULT:
return INFO;
case ANDROID_LOG_VERBOSE:
return VERBOSE;
case ANDROID_LOG_DEBUG:
return DEBUG;
case ANDROID_LOG_INFO:
return INFO;
case ANDROID_LOG_WARN:
return WARNING;
case ANDROID_LOG_ERROR:
return ERROR;
case ANDROID_LOG_FATAL:
return FATAL;
default:
return FATAL;
}
}
static int32_t LogSeverityToPriority(LogSeverity severity) {
switch (severity) {
case VERBOSE:
return ANDROID_LOG_VERBOSE;
case DEBUG:
return ANDROID_LOG_DEBUG;
case INFO:
return ANDROID_LOG_INFO;
case WARNING:
return ANDROID_LOG_WARN;
case ERROR:
return ANDROID_LOG_ERROR;
case FATAL_WITHOUT_ABORT:
case FATAL:
default:
return ANDROID_LOG_FATAL;
}
}
static std::mutex& LoggingLock() {
static auto& logging_lock = *new std::mutex();
return logging_lock;
}
// Only used for Q fallback.
static LogFunction& Logger() {
#ifdef __ANDROID__
static auto& logger = *new LogFunction(LogdLogger());
#else
static auto& logger = *new LogFunction(StderrLogger);
#endif
return logger;
}
// Only used for Q fallback.
static AbortFunction& Aborter() {
static auto& aborter = *new AbortFunction(DefaultAborter);
return aborter;
}
// Only used for Q fallback.
static std::recursive_mutex& TagLock() {
static auto& tag_lock = *new std::recursive_mutex();
return tag_lock;
}
// Only used for Q fallback.
static std::string* gDefaultTag;
void SetDefaultTag(const std::string& tag) {
static auto& liblog_functions = GetLibLogFunctions();
if (liblog_functions) {
liblog_functions->__android_log_set_default_tag(tag.c_str());
} else {
std::lock_guard<std::recursive_mutex> lock(TagLock());
if (gDefaultTag != nullptr) {
delete gDefaultTag;
gDefaultTag = nullptr;
}
if (!tag.empty()) {
gDefaultTag = new std::string(tag);
}
}
}
static bool gInitialized = false;
// Only used for Q fallback.
static LogSeverity gMinimumLogSeverity = INFO;
#if defined(__linux__)
void KernelLogger(android::base::LogId, android::base::LogSeverity severity,
const char* tag, const char*, unsigned int, const char* msg) {
// clang-format off
static constexpr int kLogSeverityToKernelLogLevel[] = {
[android::base::VERBOSE] = 7, // KERN_DEBUG (there is no verbose kernel log
// level)
[android::base::DEBUG] = 7, // KERN_DEBUG
[android::base::INFO] = 6, // KERN_INFO
[android::base::WARNING] = 4, // KERN_WARNING
[android::base::ERROR] = 3, // KERN_ERROR
[android::base::FATAL_WITHOUT_ABORT] = 2, // KERN_CRIT
[android::base::FATAL] = 2, // KERN_CRIT
};
// clang-format on
static_assert(arraysize(kLogSeverityToKernelLogLevel) == android::base::FATAL + 1,
"Mismatch in size of kLogSeverityToKernelLogLevel and values in LogSeverity");
static int klog_fd = OpenKmsg();
if (klog_fd == -1) return;
int level = kLogSeverityToKernelLogLevel[severity];
// The kernel's printk buffer is only 1024 bytes.
// TODO: should we automatically break up long lines into multiple lines?
// Or we could log but with something like "..." at the end?
char buf[1024];
size_t size = snprintf(buf, sizeof(buf), "<%d>%s: %s\n", level, tag, msg);
if (size > sizeof(buf)) {
size = snprintf(buf, sizeof(buf), "<%d>%s: %zu-byte message too long for printk\n",
level, tag, size);
}
iovec iov[1];
iov[0].iov_base = buf;
iov[0].iov_len = size;
TEMP_FAILURE_RETRY(writev(klog_fd, iov, 1));
}
#endif
void StderrLogger(LogId, LogSeverity severity, const char* tag, const char* file, unsigned int line,
const char* message) {
struct tm now;
time_t t = time(nullptr);
#if defined(_WIN32)
localtime_s(&now, &t);
#else
localtime_r(&t, &now);
#endif
char timestamp[32];
strftime(timestamp, sizeof(timestamp), "%m-%d %H:%M:%S", &now);
static const char log_characters[] = "VDIWEFF";
static_assert(arraysize(log_characters) - 1 == FATAL + 1,
"Mismatch in size of log_characters and values in LogSeverity");
char severity_char = log_characters[severity];
if (file != nullptr) {
fprintf(stderr, "%s %c %s %5d %5" PRIu64 " %s:%u] %s\n", tag ? tag : "nullptr", severity_char,
timestamp, getpid(), GetThreadId(), file, line, message);
} else {
fprintf(stderr, "%s %c %s %5d %5" PRIu64 " %s\n", tag ? tag : "nullptr", severity_char,
timestamp, getpid(), GetThreadId(), message);
}
}
void StdioLogger(LogId, LogSeverity severity, const char* /*tag*/, const char* /*file*/,
unsigned int /*line*/, const char* message) {
if (severity >= WARNING) {
fflush(stdout);
fprintf(stderr, "%s: %s\n", GetFileBasename(getprogname()), message);
} else {
fprintf(stdout, "%s\n", message);
}
}
void DefaultAborter(const char* abort_message) {
#ifdef __ANDROID__
android_set_abort_message(abort_message);
#else
UNUSED(abort_message);
#endif
abort();
}
LogdLogger::LogdLogger(LogId default_log_id) : default_log_id_(default_log_id) {
}
void LogdLogger::operator()(LogId id, LogSeverity severity, const char* tag,
const char* file, unsigned int line,
const char* message) {
int32_t priority = LogSeverityToPriority(severity);
if (id == DEFAULT) {
id = default_log_id_;
}
int32_t lg_id = LogIdTolog_id_t(id);
char log_message_with_file[4068]; // LOGGER_ENTRY_MAX_PAYLOAD, not available in the NDK.
if (priority == ANDROID_LOG_FATAL && file != nullptr) {
snprintf(log_message_with_file, sizeof(log_message_with_file), "%s:%u] %s", file, line,
message);
message = log_message_with_file;
}
static auto& liblog_functions = GetLibLogFunctions();
if (liblog_functions) {
__android_log_message log_message = {sizeof(__android_log_message), lg_id, priority, tag,
static_cast<const char*>(nullptr), 0, message};
liblog_functions->__android_log_logd_logger(&log_message);
} else {
__android_log_buf_print(lg_id, priority, tag, "%s", message);
}
}
void InitLogging(char* argv[], LogFunction&& logger, AbortFunction&& aborter) {
SetLogger(std::forward<LogFunction>(logger));
SetAborter(std::forward<AbortFunction>(aborter));
if (gInitialized) {
return;
}
gInitialized = true;
// Stash the command line for later use. We can use /proc/self/cmdline on
// Linux to recover this, but we don't have that luxury on the Mac/Windows,
// and there are a couple of argv[0] variants that are commonly used.
if (argv != nullptr) {
SetDefaultTag(basename(argv[0]));
}
const char* tags = getenv("ANDROID_LOG_TAGS");
if (tags == nullptr) {
return;
}
std::vector<std::string> specs = Split(tags, " ");
for (size_t i = 0; i < specs.size(); ++i) {
// "tag-pattern:[vdiwefs]"
std::string spec(specs[i]);
if (spec.size() == 3 && StartsWith(spec, "*:")) {
switch (spec[2]) {
case 'v':
SetMinimumLogSeverity(VERBOSE);
continue;
case 'd':
SetMinimumLogSeverity(DEBUG);
continue;
case 'i':
SetMinimumLogSeverity(INFO);
continue;
case 'w':
SetMinimumLogSeverity(WARNING);
continue;
case 'e':
SetMinimumLogSeverity(ERROR);
continue;
case 'f':
SetMinimumLogSeverity(FATAL_WITHOUT_ABORT);
continue;
// liblog will even suppress FATAL if you say 's' for silent, but that's
// crazy!
case 's':
SetMinimumLogSeverity(FATAL_WITHOUT_ABORT);
continue;
}
}
LOG(FATAL) << "unsupported '" << spec << "' in ANDROID_LOG_TAGS (" << tags
<< ")";
}
}
void SetLogger(LogFunction&& logger) {
static auto& liblog_functions = GetLibLogFunctions();
if (liblog_functions) {
// We need to atomically swap the old and new pointers since other threads may be logging.
// We know all threads will be using the new logger after __android_log_set_logger() returns,
// so we can delete it then.
// This leaks one std::function<> per instance of libbase if multiple copies of libbase within a
// single process call SetLogger(). That is the same cost as having a static
// std::function<>, which is the not-thread-safe alternative.
static std::atomic<LogFunction*> logger_function(nullptr);
auto* old_logger_function = logger_function.exchange(new LogFunction(logger));
liblog_functions->__android_log_set_logger([](const struct __android_log_message* log_message) {
auto log_id = log_id_tToLogId(log_message->buffer_id);
auto severity = PriorityToLogSeverity(log_message->priority);
auto& function = *logger_function.load(std::memory_order_acquire);
function(log_id, severity, log_message->tag, log_message->file, log_message->line,
log_message->message);
});
delete old_logger_function;
} else {
std::lock_guard<std::mutex> lock(LoggingLock());
Logger() = std::move(logger);
}
}
void SetAborter(AbortFunction&& aborter) {
static auto& liblog_functions = GetLibLogFunctions();
if (liblog_functions) {
// See the comment in SetLogger().
static std::atomic<AbortFunction*> abort_function(nullptr);
auto* old_abort_function = abort_function.exchange(new AbortFunction(aborter));
liblog_functions->__android_log_set_aborter([](const char* abort_message) {
auto& function = *abort_function.load(std::memory_order_acquire);
function(abort_message);
});
delete old_abort_function;
} else {
std::lock_guard<std::mutex> lock(LoggingLock());
Aborter() = std::move(aborter);
}
}
// This indirection greatly reduces the stack impact of having lots of
// checks/logging in a function.
class LogMessageData {
public:
LogMessageData(const char* file, unsigned int line, LogSeverity severity, const char* tag,
int error)
: file_(GetFileBasename(file)),
line_number_(line),
severity_(severity),
tag_(tag),
error_(error) {}
const char* GetFile() const {
return file_;
}
unsigned int GetLineNumber() const {
return line_number_;
}
LogSeverity GetSeverity() const {
return severity_;
}
const char* GetTag() const { return tag_; }
int GetError() const {
return error_;
}
std::ostream& GetBuffer() {
return buffer_;
}
std::string ToString() const {
return buffer_.str();
}
private:
std::ostringstream buffer_;
const char* const file_;
const unsigned int line_number_;
const LogSeverity severity_;
const char* const tag_;
const int error_;
DISALLOW_COPY_AND_ASSIGN(LogMessageData);
};
LogMessage::LogMessage(const char* file, unsigned int line, LogId, LogSeverity severity,
const char* tag, int error)
: LogMessage(file, line, severity, tag, error) {}
LogMessage::LogMessage(const char* file, unsigned int line, LogSeverity severity, const char* tag,
int error)
: data_(new LogMessageData(file, line, severity, tag, error)) {}
LogMessage::~LogMessage() {
// Check severity again. This is duplicate work wrt/ LOG macros, but not LOG_STREAM.
if (!WOULD_LOG(data_->GetSeverity())) {
return;
}
// Finish constructing the message.
if (data_->GetError() != -1) {
data_->GetBuffer() << ": " << strerror(data_->GetError());
}
std::string msg(data_->ToString());
if (data_->GetSeverity() == FATAL) {
#ifdef __ANDROID__
// Set the bionic abort message early to avoid liblog doing it
// with the individual lines, so that we get the whole message.
android_set_abort_message(msg.c_str());
#endif
}
{
// Do the actual logging with the lock held.
std::lock_guard<std::mutex> lock(LoggingLock());
if (msg.find('\n') == std::string::npos) {
LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), data_->GetTag(),
msg.c_str());
} else {
msg += '\n';
size_t i = 0;
while (i < msg.size()) {
size_t nl = msg.find('\n', i);
msg[nl] = '\0';
LogLine(data_->GetFile(), data_->GetLineNumber(), data_->GetSeverity(), data_->GetTag(),
&msg[i]);
// Undo the zero-termination so we can give the complete message to the aborter.
msg[nl] = '\n';
i = nl + 1;
}
}
}
// Abort if necessary.
if (data_->GetSeverity() == FATAL) {
static auto& liblog_functions = GetLibLogFunctions();
if (liblog_functions) {
liblog_functions->__android_log_call_aborter(msg.c_str());
} else {
Aborter()(msg.c_str());
}
}
}
std::ostream& LogMessage::stream() {
return data_->GetBuffer();
}
void LogMessage::LogLine(const char* file, unsigned int line, LogSeverity severity, const char* tag,
const char* message) {
static auto& liblog_functions = GetLibLogFunctions();
int32_t priority = LogSeverityToPriority(severity);
if (liblog_functions) {
__android_log_message log_message = {
sizeof(__android_log_message), LOG_ID_DEFAULT, priority, tag, file, line, message};
liblog_functions->__android_log_write_log_message(&log_message);
} else {
if (tag == nullptr) {
std::lock_guard<std::recursive_mutex> lock(TagLock());
if (gDefaultTag == nullptr) {
gDefaultTag = new std::string(getprogname());
}
Logger()(DEFAULT, severity, gDefaultTag->c_str(), file, line, message);
} else {
Logger()(DEFAULT, severity, tag, file, line, message);
}
}
}
LogSeverity GetMinimumLogSeverity() {
static auto& liblog_functions = GetLibLogFunctions();
if (liblog_functions) {
return PriorityToLogSeverity(liblog_functions->__android_log_get_minimum_priority());
} else {
return gMinimumLogSeverity;
}
}
bool ShouldLog(LogSeverity severity, const char* tag) {
static auto& liblog_functions = GetLibLogFunctions();
// Even though we're not using the R liblog functions in this function, if we're running on Q,
// we need to fall back to using gMinimumLogSeverity, since __android_log_is_loggable() will not
// take into consideration the value from SetMinimumLogSeverity().
if (liblog_functions) {
int32_t priority = LogSeverityToPriority(severity);
return __android_log_is_loggable(priority, tag, ANDROID_LOG_INFO);
} else {
return severity >= gMinimumLogSeverity;
}
}
LogSeverity SetMinimumLogSeverity(LogSeverity new_severity) {
static auto& liblog_functions = GetLibLogFunctions();
if (liblog_functions) {
int32_t priority = LogSeverityToPriority(new_severity);
return PriorityToLogSeverity(liblog_functions->__android_log_set_minimum_priority(priority));
} else {
LogSeverity old_severity = gMinimumLogSeverity;
gMinimumLogSeverity = new_severity;
return old_severity;
}
}
ScopedLogSeverity::ScopedLogSeverity(LogSeverity new_severity) {
old_ = SetMinimumLogSeverity(new_severity);
}
ScopedLogSeverity::~ScopedLogSeverity() {
SetMinimumLogSeverity(old_);
}
} // namespace base
} // namespace android