Android的日志系统涉及三个模块,服务端logd、客户端、logcat工具。
客户端,Android系统提供的是动态链接库liblog.so,在向上通过JNI封装了Java API,供应用程序(上层程序)调用。
logcat工具,用来输出系统日志到终端或重定向到文件。
buffer ID
/** @hide */ public static final int LOG_ID_MAIN = 0;
/** @hide */ public static final int LOG_ID_RADIO = 1;
/** @hide */ public static final int LOG_ID_EVENTS = 2;
/** @hide */ public static final int LOG_ID_SYSTEM = 3;
/** @hide */ public static final int LOG_ID_CRASH = 4;
优先级
/**
* Priority constant for the println method; use Log.v.
*/
public static final int VERBOSE = 2;
/**
* Priority constant for the println method; use Log.d.
*/
public static final int DEBUG = 3;
/**
* Priority constant for the println method; use Log.i.
*/
public static final int INFO = 4;
/**
* Priority constant for the println method; use Log.w.
*/
public static final int WARN = 5;
/**
* Priority constant for the println method; use Log.e.
*/
public static final int ERROR = 6;
/**
* Priority constant for the println method.
*/
public static final int ASSERT = 7;
frameworks/base/core/java/android/util/Slog.java
public static int i(String tag, String msg) {
return Log.println_native(Log.LOG_ID_SYSTEM, Log.INFO, tag, msg);
}
public static int i(String tag, String msg, Throwable tr) {
return Log.println_native(Log.LOG_ID_SYSTEM, Log.INFO, tag,
msg + '\n' + Log.getStackTraceString(tr));
}
frameworks/base/core/java/android/util/Log.java
/** @hide */ public static native int println_native(int bufID,
int priority, String tag, String msg);
已上编译之后生成在frameworks.jar里面,供应用程序调用,frameworks.jar就是Android的SDK。下面进入JNI。
JNI
frameworks/base/core/jni/android_util_Log.cpp
static const JNINativeMethod gMethods[] = {
/* name, signature, funcPtr */
{ "isLoggable", "(Ljava/lang/String;I)Z", (void*) android_util_Log_isLoggable },
{ "println_native", "(IILjava/lang/String;Ljava/lang/String;)I", (void*) android_util_Log_println_native },
{ "logger_entry_max_payload_native", "()I", (void*) android_util_Log_logger_entry_max_payload_native },
};
/*
* In class android.util.Log:
* public static native int println_native(int buffer, int priority, String tag, String msg)
*/
static jint android_util_Log_println_native(JNIEnv* env, jobject clazz,
jint bufID, jint priority, jstring tagObj, jstring msgObj)
{
const char* tag = NULL;
const char* msg = NULL;
if (msgObj == NULL) {
jniThrowNullPointerException(env, "println needs a message");
return -1;
}
if (bufID < 0 || bufID >= LOG_ID_MAX) {
jniThrowNullPointerException(env, "bad bufID");
return -1;
}
if (tagObj != NULL)
tag = env->GetStringUTFChars(tagObj, NULL);
msg = env->GetStringUTFChars(msgObj, NULL);
// 经过一些基本的检查后,调用下面的函数,这个函数是在
int res = __android_log_buf_write(bufID, (android_LogPriority)priority, tag, msg);
if (tag != NULL)
env->ReleaseStringUTFChars(tagObj, tag);
env->ReleaseStringUTFChars(msgObj, msg);
return res;
}
JNI部分编译生成动态链接库libandroid_runtime.so。虚拟机启动的时候会加载这个库,供虚拟机中的程序调用。
liblog.so
liblog库的代码路径system/core/liblog/
。
system/core/liblog/logger_write.c
LIBLOG_ABI_PUBLIC int __android_log_buf_write(int bufID, int prio,
const char* tag, const char* msg) {
struct iovec vec[3];
char tmp_tag[32];
if (!tag) tag = "";
/* XXX: This needs to go! */
if (bufID != LOG_ID_RADIO) {
switch (tag[0]) {
case 'H':
if (strcmp(tag + 1, "HTC_RIL" + 1)) break;
goto inform;
case 'R':
/* Any log tag with "RIL" as the prefix */
if (strncmp(tag + 1, "RIL" + 1, strlen("RIL") - 1)) break;
goto inform;
case 'Q':
/* Any log tag with "QC_RIL" as the prefix */
if (strncmp(tag + 1, "QC_RIL" + 1, strlen("QC_RIL") - 1)) break;
goto inform;
case 'I':
/* Any log tag with "IMS" as the prefix */
if (strncmp(tag + 1, "IMS" + 1, strlen("IMS") - 1)) break;
goto inform;
case 'A':
if (strcmp(tag + 1, "AT" + 1)) break;
goto inform;
case 'G':
if (strcmp(tag + 1, "GSM" + 1)) break;
goto inform;
case 'S':
if (strcmp(tag + 1, "STK" + 1) && strcmp(tag + 1, "SMS" + 1)) break;
goto inform;
case 'C':
if (strcmp(tag + 1, "CDMA" + 1)) break;
goto inform;
case 'P':
if (strcmp(tag + 1, "PHONE" + 1)) break;
/* FALLTHRU */
inform:
bufID = LOG_ID_RADIO;
snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
tag = tmp_tag;
/* FALLTHRU */
default:
break;
}
}
#if __BIONIC__
if (prio == ANDROID_LOG_FATAL) {
android_set_abort_message(msg);
}
#endif
vec[0].iov_base = (unsigned char*)&prio;
vec[0].iov_len = 1;
vec[1].iov_base = (void*)tag;
vec[1].iov_len = strlen(tag) + 1;
vec[2].iov_base = (void*)msg;
vec[2].iov_len = strlen(msg) + 1;
return write_to_log(bufID, vec, 3);
}
static int (*write_to_log)(log_id_t, struct iovec* vec,
size_t nr) = __write_to_log_init;
static int __write_to_log_init(log_id_t log_id, struct iovec* vec, size_t nr) {
int ret, save_errno = errno;
__android_log_lock();
if (write_to_log == __write_to_log_init) {
ret = __write_to_log_initialize();
if (ret < 0) {
__android_log_unlock();
if (!list_empty(&__android_log_persist_write)) {
__write_to_log_daemon(log_id, vec, nr);
}
errno = save_errno;
return ret;
}
write_to_log = __write_to_log_daemon;
}
__android_log_unlock();
ret = write_to_log(log_id, vec, nr);
errno = save_errno;
return ret;
}
static int __write_to_log_daemon(log_id_t log_id, struct iovec* vec, size_t nr) {
struct android_log_transport_write* node;
int ret, save_errno;
struct timespec ts;
size_t len, i;
for (len = i = 0; i < nr; ++i) {
len += vec[i].iov_len;
}
if (!len) {
return -EINVAL;
}
save_errno = errno;
#if defined(__ANDROID__)
clock_gettime(android_log_clockid(), &ts);
if (log_id == LOG_ID_SECURITY) {
if (vec[0].iov_len < 4) {
errno = save_errno;
return -EINVAL;
}
ret = check_log_uid_permissions();
if (ret < 0) {
errno = save_errno;
return ret;
}
if (!__android_log_security()) {
/* If only we could reset downstream logd counter */
errno = save_errno;
return -EPERM;
}
} else if (log_id == LOG_ID_EVENTS || log_id == LOG_ID_STATS) {
const char* tag;
size_t len;
EventTagMap *m, *f;
if (vec[0].iov_len < 4) {
errno = save_errno;
return -EINVAL;
}
tag = NULL;
len = 0;
f = NULL;
m = (EventTagMap*)atomic_load(&tagMap);
if (!m) {
ret = __android_log_trylock();
m = (EventTagMap*)atomic_load(&tagMap); /* trylock flush cache */
if (!m) {
m = android_openEventTagMap(NULL);
if (ret) { /* trylock failed, use local copy, mark for close */
f = m;
} else {
if (!m) { /* One chance to open map file */
m = (EventTagMap*)(uintptr_t)-1LL;
}
atomic_store(&tagMap, (uintptr_t)m);
}
}
if (!ret) { /* trylock succeeded, unlock */
__android_log_unlock();
}
}
if (m && (m != (EventTagMap*)(uintptr_t)-1LL)) {
tag = android_lookupEventTag_len(m, &len, get4LE(vec[0].iov_base));
}
ret = __android_log_is_loggable_len(ANDROID_LOG_INFO, tag, len,
ANDROID_LOG_VERBOSE);
if (f) { /* local copy marked for close */
android_closeEventTagMap(f);
}
if (!ret) {
errno = save_errno;
return -EPERM;
}
} else {
/* Validate the incoming tag, tag content can not split across iovec */
char prio = ANDROID_LOG_VERBOSE;
const char* tag = vec[0].iov_base;
size_t len = vec[0].iov_len;
if (!tag) {
len = 0;
}
if (len > 0) {
prio = *tag;
if (len > 1) {
--len;
++tag;
} else {
len = vec[1].iov_len;
tag = ((const char*)vec[1].iov_base);
if (!tag) {
len = 0;
}
}
}
/* tag must be nul terminated */
if (tag && strnlen(tag, len) >= len) {
tag = NULL;
}
if (!__android_log_is_loggable_len(prio, tag, len - 1, ANDROID_LOG_VERBOSE)) {
errno = save_errno;
return -EPERM;
}
}
#else
/* simulate clock_gettime(CLOCK_REALTIME, &ts); */
{
struct timeval tv;
gettimeofday(&tv, NULL);
ts.tv_sec = tv.tv_sec;
ts.tv_nsec = tv.tv_usec * 1000;
}
#endif
ret = 0;
i = 1 << log_id;
write_transport_for_each(node, &__android_log_transport_write) {
if (node->logMask & i) {
ssize_t retval;
retval = (*node->write)(log_id, &ts, vec, nr);
if (ret >= 0) {
ret = retval;
}
}
}
write_transport_for_each(node, &__android_log_persist_write) {
if (node->logMask & i) {
(void)(*node->write)(log_id, &ts, vec, nr);
}
}
errno = save_errno;
return ret;
}
static int logdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec,
size_t nr) {
ssize_t ret;
int sock;
static const unsigned headerLength = 1;
struct iovec newVec[nr + headerLength];
android_log_header_t header;
size_t i, payloadSize;
static atomic_int_fast32_t dropped;
static atomic_int_fast32_t droppedSecurity;
sock = atomic_load(&logdLoggerWrite.context.sock);
if (sock < 0) switch (sock) {
case -ENOTCONN:
case -ECONNREFUSED:
case -ENOENT:
break;
default:
return -EBADF;
}
/* logd, after initialization and priv drop */
if (__android_log_uid() == AID_LOGD) {
/*
* ignore log messages we send to ourself (logd).
* Such log messages are often generated by libraries we depend on
* which use standard Android logging.
*/
return 0;
}
/*
* struct {
* // what we provide to socket
* android_log_header_t header;
* // caller provides
* union {
* struct {
* char prio;
* char payload[];
* } string;
* struct {
* uint32_t tag
* char payload[];
* } binary;
* };
* };
*/
header.tid = gettid();
header.realtime.tv_sec = ts->tv_sec;
header.realtime.tv_nsec = ts->tv_nsec;
newVec[0].iov_base = (unsigned char*)&header;
newVec[0].iov_len = sizeof(header);
if (sock >= 0) {
int32_t snapshot =
atomic_exchange_explicit(&droppedSecurity, 0, memory_order_relaxed);
if (snapshot) {
android_log_event_int_t buffer;
header.id = LOG_ID_SECURITY;
buffer.header.tag = htole32(LIBLOG_LOG_TAG);
buffer.payload.type = EVENT_TYPE_INT;
buffer.payload.data = htole32(snapshot);
newVec[headerLength].iov_base = &buffer;
newVec[headerLength].iov_len = sizeof(buffer);
ret = TEMP_FAILURE_RETRY(writev(sock, newVec, 2));
if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
atomic_fetch_add_explicit(&droppedSecurity, snapshot,
memory_order_relaxed);
}
}
snapshot = atomic_exchange_explicit(&dropped, 0, memory_order_relaxed);
if (snapshot &&
__android_log_is_loggable_len(ANDROID_LOG_INFO, "liblog",
strlen("liblog"), ANDROID_LOG_VERBOSE)) {
android_log_event_int_t buffer;
header.id = LOG_ID_EVENTS;
buffer.header.tag = htole32(LIBLOG_LOG_TAG);
buffer.payload.type = EVENT_TYPE_INT;
buffer.payload.data = htole32(snapshot);
newVec[headerLength].iov_base = &buffer;
newVec[headerLength].iov_len = sizeof(buffer);
ret = TEMP_FAILURE_RETRY(writev(sock, newVec, 2));
if (ret != (ssize_t)(sizeof(header) + sizeof(buffer))) {
atomic_fetch_add_explicit(&dropped, snapshot, memory_order_relaxed);
}
}
}
header.id = logId;
for (payloadSize = 0, i = headerLength; i < nr + headerLength; i++) {
newVec[i].iov_base = vec[i - headerLength].iov_base;
payloadSize += newVec[i].iov_len = vec[i - headerLength].iov_len;
if (payloadSize > LOGGER_ENTRY_MAX_PAYLOAD) {
newVec[i].iov_len -= payloadSize - LOGGER_ENTRY_MAX_PAYLOAD;
if (newVec[i].iov_len) {
++i;
}
break;
}
}
/*
* The write below could be lost, but will never block.
*
* ENOTCONN occurs if logd has died.
* ENOENT occurs if logd is not running and socket is missing.
* ECONNREFUSED occurs if we can not reconnect to logd.
* EAGAIN occurs if logd is overloaded.
*/
if (sock < 0) {
ret = sock;
} else {
ret = TEMP_FAILURE_RETRY(writev(sock, newVec, i));
if (ret < 0) {
ret = -errno;
}
}
switch (ret) {
case -ENOTCONN:
case -ECONNREFUSED:
case -ENOENT:
if (__android_log_trylock()) {
return ret; /* in a signal handler? try again when less stressed */
}
__logdClose(ret);
ret = logdOpen();
__android_log_unlock();
if (ret < 0) {
return ret;
}
ret = TEMP_FAILURE_RETRY(
writev(atomic_load(&logdLoggerWrite.context.sock), newVec, i));
if (ret < 0) {
ret = -errno;
}
/* FALLTHRU */
default:
break;
}
if (ret > (ssize_t)sizeof(header)) {
ret -= sizeof(header);
} else if (ret == -EAGAIN) {
atomic_fetch_add_explicit(&dropped, 1, memory_order_relaxed);
if (logId == LOG_ID_SECURITY) {
atomic_fetch_add_explicit(&droppedSecurity, 1, memory_order_relaxed);
}
}
return ret;
}
static int __write_to_log_initialize() {
struct android_log_transport_write* transport;
struct listnode* n;
int i = 0, ret = 0;
// 初始化变量__android_log_transport_write和__android_log_persist_write
__android_log_config_write();
write_transport_for_each_safe(transport, n, &__android_log_transport_write) {
__android_log_cache_available(transport);
if (!transport->logMask) {
list_remove(&transport->node);
continue;
}
if (!transport->open || ((*transport->open)() < 0)) {
if (transport->close) {
(*transport->close)();
}
list_remove(&transport->node);
continue;
}
++ret;
}
write_transport_for_each_safe(transport, n, &__android_log_persist_write) {
__android_log_cache_available(transport);
if (!transport->logMask) {
list_remove(&transport->node);
continue;
}
if (!transport->open || ((*transport->open)() < 0)) {
if (transport->close) {
(*transport->close)();
}
list_remove(&transport->node);
continue;
}
++i;
}
if (!ret && !i) {
return -ENODEV;
}
return ret;
}
system\system\core\liblog\config_write.c
LIBLOG_HIDDEN void __android_log_config_write() {
// __android_log_transport默认值是LOGGER_DEFAULT
if (__android_log_transport & LOGGER_LOCAL) {
extern struct android_log_transport_write localLoggerWrite;
__android_log_add_transport(&__android_log_transport_write,
&localLoggerWrite);
}
if ((__android_log_transport == LOGGER_DEFAULT) ||
(__android_log_transport & LOGGER_LOGD)) {
#if (FAKE_LOG_DEVICE == 0)
extern struct android_log_transport_write logdLoggerWrite;
extern struct android_log_transport_write pmsgLoggerWrite;
__android_log_add_transport(&__android_log_transport_write,
&logdLoggerWrite);
__android_log_add_transport(&__android_log_persist_write, &pmsgLoggerWrite);
#else
extern struct android_log_transport_write fakeLoggerWrite;
__android_log_add_transport(&__android_log_transport_write,
&fakeLoggerWrite);
#endif
}
if (__android_log_transport & LOGGER_STDERR) {
extern struct android_log_transport_write stderrLoggerWrite;
/*
* stderr logger should be primary if we can be the only one, or if
* already in the primary list. Otherwise land in the persist list.
* Remember we can be called here if we are already initialized.
*/
if (list_empty(&__android_log_transport_write)) {
__android_log_add_transport(&__android_log_transport_write,
&stderrLoggerWrite);
} else {
struct android_log_transport_write* transp;
write_transport_for_each(transp, &__android_log_transport_write) {
if (transp == &stderrLoggerWrite) {
return;
}
}
__android_log_add_transport(&__android_log_persist_write,
&stderrLoggerWrite);
}
}
}
和server建立socket连接
system\core\liblog\logd_writer.c
static int logdOpen() {
int i, ret = 0;
i = atomic_load(&logdLoggerWrite.context.sock);
if (i < 0) {
int sock = TEMP_FAILURE_RETRY(
socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0));
if (sock < 0) {
ret = -errno;
} else {
struct sockaddr_un un;
memset(&un, 0, sizeof(struct sockaddr_un));
un.sun_family = AF_UNIX;
strcpy(un.sun_path, "/dev/socket/logdw");
// 通过logdw和logd建立socket连接,用于向logd发送客户端日志数据
if (TEMP_FAILURE_RETRY(connect(sock, (struct sockaddr*)&un,
sizeof(struct sockaddr_un))) < 0) {
ret = -errno;
switch (ret) {
case -ENOTCONN:
case -ECONNREFUSED:
case -ENOENT:
i = atomic_exchange(&logdLoggerWrite.context.sock, ret);
/* FALLTHRU */
default:
break;
}
close(sock);
} else {
ret = atomic_exchange(&logdLoggerWrite.context.sock, sock);
if ((ret >= 0) && (ret != sock)) {
close(ret);
}
ret = 0;
}
}
}
return ret;
}
变量logdLoggerWrite和pmsgLoggerWrite
system\system\core\liblog\logd_writer.c
LIBLOG_HIDDEN struct android_log_transport_write logdLoggerWrite = {
.node = { &logdLoggerWrite.node, &logdLoggerWrite.node },
.context.sock = -EBADF,
.name = "logd",
.available = logdAvailable,
.open = logdOpen,
.close = logdClose,
.write = logdWrite,
};
system\system\core\liblog\pmsg_writer.c
LIBLOG_HIDDEN struct android_log_transport_write pmsgLoggerWrite = {
.node = { &pmsgLoggerWrite.node, &pmsgLoggerWrite.node },
.context.fd = -1,
.name = "pmsg",
.available = pmsgAvailable,
.open = pmsgOpen,
.close = pmsgClose,
.write = pmsgWrite,
};
log daemon
log守护进程是通过init进程解析rc文件启动的,logd源码位于system/core/logd/
system/core/logd/logd.rc
service logd /system/bin/logd
socket logd stream 0666 logd logd
socket logdr seqpacket 0666 logd logd
socket logdw dgram+passcred 0222 logd logd
file /proc/kmsg r
file /dev/kmsg w
user logd
group logd system package_info readproc
writepid /dev/cpuset/system-background/tasks
system/core/logd/Android.mk
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_MODULE:= logd
LOCAL_INIT_RC := logd.rc
LOCAL_SRC_FILES := \
main.cpp \
... ...
LOCAL_SHARED_LIBRARIES := \
libsysutils \
liblog \
libcutils \
libbase \
libpackagelistparser
event_flag := -DAUDITD_LOG_TAG=1003 -DLOGD_LOG_TAG=1004
LOCAL_CFLAGS := -Werror $(event_flag)
include $(BUILD_EXECUTABLE)
include $(call first-makefiles-under,$(LOCAL_PATH))
主流程
system\system\core\logd\main.cpp
int main(int argc, char* argv[]) {
// 时区设置。设置为UTC时间
setenv("TZ", "UTC", 1);
// 执行logd的时候,如果传入参数--reinit就走这个if逻辑
if ((argc > 1) && argv[1] && !strcmp(argv[1], "--reinit")) {
return issueReinit();
}
static const char dev_kmsg[] = "/dev/kmsg";
// 从环境变量中取得kernel日志文件描述符,返回值小于0,获取失败,则直接打开文件/dev/kmsg,用于写
fdDmesg = android_get_control_file(dev_kmsg);
if (fdDmesg < 0) {
fdDmesg = TEMP_FAILURE_RETRY(open(dev_kmsg, O_WRONLY | O_CLOEXEC));
}
int fdPmesg = -1;
// 是否使能kernel日志,如果使能,尝试去得到kernel日志文件描述符,用于读
bool klogd = __android_logger_property_get_bool(
"ro.logd.kernel",
BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_ENG | BOOL_DEFAULT_FLAG_SVELTE);
if (klogd) {
static const char proc_kmsg[] = "/proc/kmsg";
fdPmesg = android_get_control_file(proc_kmsg);
if (fdPmesg < 0) {
fdPmesg = TEMP_FAILURE_RETRY(
open(proc_kmsg, O_RDONLY | O_NDELAY | O_CLOEXEC));
}
if (fdPmesg < 0) android::prdebug("Failed to open %s\n", proc_kmsg);
}
// 创建三个当前进程的线程间共享的信号量
sem_init(&reinit, 0, 0);
sem_init(&uidName, 0, 0);
sem_init(&sem_name, 0, 1);
pthread_attr_t attr;
if (!pthread_attr_init(&attr)) {
struct sched_param param;
memset(¶m, 0, sizeof(param));
pthread_attr_setschedparam(&attr, ¶m);
pthread_attr_setschedpolicy(&attr, SCHED_BATCH);
if (!pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED)) {
pthread_t thread;
reinit_running = true;
// 创建线程,线程创建成功后,会运行相应的线程函数,这里就是reinit_thread_start
if (pthread_create(&thread, &attr, reinit_thread_start, nullptr)) {
reinit_running = false;
}
}
pthread_attr_destroy(&attr);
}
bool auditd =
__android_logger_property_get_bool("ro.logd.auditd", BOOL_DEFAULT_TRUE);
if (drop_privs(klogd, auditd) != 0) {
return -1;
}
// Serves the purpose of managing the last logs times read on a
// socket connection, and as a reader lock on a range of log
// entries.
LastLogTimes* times = new LastLogTimes();
// LogBuffer is the object which is responsible for holding all
// log entries.
logBuf = new LogBuffer(times);
signal(SIGHUP, reinit_signal_handler);
if (__android_logger_property_get_bool(
"logd.statistics", BOOL_DEFAULT_TRUE | BOOL_DEFAULT_FLAG_PERSIST |
BOOL_DEFAULT_FLAG_ENG |
BOOL_DEFAULT_FLAG_SVELTE)) {
logBuf->enableStatistics();
}
// LogReader listens on /dev/socket/logdr. When a client
// connects, log entries in the LogBuffer are written to the client.
LogReader* reader = new LogReader(logBuf);
if (reader->startListener()) {
exit(1);
}
// LogListener listens on /dev/socket/logdw for client
// initiated log messages. New log entries are added to LogBuffer
// and LogReader is notified to send updates to connected clients.
LogListener* swl = new LogListener(logBuf, reader);
// Backlog and /proc/sys/net/unix/max_dgram_qlen set to large value
if (swl->startListener(600)) {
exit(1);
}
// Command listener listens on /dev/socket/logd for incoming logd
// administrative commands.
CommandListener* cl = new CommandListener(logBuf, reader, swl);
if (cl->startListener()) {
exit(1);
}
// LogAudit listens on NETLINK_AUDIT socket for selinux
// initiated log messages. New log entries are added to LogBuffer
// and LogReader is notified to send updates to connected clients.
LogAudit* al = nullptr;
if (auditd) {
al = new LogAudit(logBuf, reader,
__android_logger_property_get_bool(
"ro.logd.auditd.dmesg", BOOL_DEFAULT_TRUE)
? fdDmesg
: -1);
}
LogKlog* kl = nullptr;
if (klogd) {
kl = new LogKlog(logBuf, reader, fdDmesg, fdPmesg, al != nullptr);
}
readDmesg(al, kl);
// failure is an option ... messages are in dmesg (required by standard)
if (kl && kl->startListener()) {
delete kl;
}
if (al && al->startListener()) {
delete al;
}
TEMP_FAILURE_RETRY(pause());
exit(0);
}
static void readDmesg(LogAudit* al, LogKlog* kl) {
if (!al && !kl) {
return;
}
int rc = klogctl(KLOG_SIZE_BUFFER, nullptr, 0);
if (rc <= 0) {
return;
}
// Margin for additional input race or trailing nul
ssize_t len = rc + 1024;
std::unique_ptr<char[]> buf(new char[len]);
// 从内核空间获取日志到buf
rc = klogctl(KLOG_READ_ALL, buf.get(), len);
if (rc <= 0) {
return;
}
if (rc < len) {
len = rc + 1;
}
buf[--len] = '\0';
if (kl && kl->isMonotonic()) {
kl->synchronize(buf.get(), len);
}
ssize_t sublen;
for (char *ptr = nullptr, *tok = buf.get();
(rc >= 0) && !!(tok = android::log_strntok_r(tok, len, ptr, sublen));
tok = nullptr) {
if ((sublen <= 0) || !*tok) continue;
if (al) {
rc = al->log(tok, sublen);
}
if (kl) {
rc = kl->log(tok, sublen);
}
}
}
logcat
system/core/logcat/logcat_main.cpp
int main(int argc, char** argv, char** envp) {
// 创建logcat上下文
android_logcat_context ctx = create_android_logcat();
if (!ctx) return -1;
signal(SIGPIPE, exit);
// 执行logcat命令
int retval = android_logcat_run_command(ctx, -1, -1, argc, argv, envp);
int ret = android_logcat_destroy(&ctx);
if (!ret) ret = retval;
return ret;
}
主程序就这一段,依赖liblogcat.so
liblogcat.so
system/core/logcat/logcat.cpp
int android_logcat_run_command(android_logcat_context ctx,
int output, int error,
int argc, char* const* argv,
char* const* envp) {
android_logcat_context_internal* context = ctx;
context->output_fd = output;
context->error_fd = error;
context->argc = argc;
context->argv = argv;
context->envp = envp;
context->stop = false;
context->thread_stopped = false;
return __logcat(context);
}
static int __logcat(android_logcat_context_internal* context) {
... ...
// 标准输出和标准错误
context->output = stdout;
context->error = stderr;
// 日志输出到文件
if (filename) { // We supplied an output file redirected in command line
context->output = fopen(filename, "web");
}
// 默认output_fd=-1,这种情况就是输出到标准输出(屏幕)
if ((context->output_fd < 0) && context->output) {
context->output_fd = fileno(context->output);
}
// 如果是`logcat --help`,则打印帮助信息
if (argc == 2 && !strcmp(argv[1], "--help")) {
show_help(context);
context->retval = EXIT_SUCCESS;
goto exit;
}
while (!context->stop &&
(!context->maxCount || (context->printCount < context->maxCount))) {
struct log_msg log_msg;
int ret = android_logger_list_read(logger_list, &log_msg);
if (!ret) {
logcat_panic(context, HELP_FALSE, "read: unexpected EOF!\n");
break;
}
if (ret < 0) {
if (ret == -EAGAIN) break;
if (ret == -EIO) {
logcat_panic(context, HELP_FALSE, "read: unexpected EOF!\n");
break;
}
if (ret == -EINVAL) {
logcat_panic(context, HELP_FALSE, "read: unexpected length.\n");
break;
}
logcat_panic(context, HELP_FALSE, "logcat read failure\n");
break;
}
log_device_t* d;
for (d = context->devices; d; d = d->next) {
if (android_name_to_log_id(d->device) == log_msg.id()) break;
}
if (!d) {
context->devCount = 2; // set to Multiple
d = &unexpected;
d->binary = log_msg.id() == LOG_ID_EVENTS;
}
if (dev != d) {
dev = d;
maybePrintStart(context, dev, printDividers);
if (context->stop) break;
}
// 一般情况下不加参数B
if (context->printBinary) {
printBinary(context, &log_msg);
} else {
processBuffer(context, dev, &log_msg);
}
}
close:
// Short and sweet. Implemented generic version in android_logcat_destroy.
while (!!(dev = context->devices)) {
context->devices = dev->next;
delete dev;
}
android_logger_list_free(logger_list);
exit:
// close write end of pipe to help things along
if (context->output_fd == context->fds[1]) {
android::close_output(context);
}
if (context->error_fd == context->fds[1]) {
android::close_error(context);
}
if (context->fds[1] >= 0) {
// NB: should be closed by the above
int save_errno = errno;
close(context->fds[1]);
errno = save_errno;
context->fds[1] = -1;
}
context->thread_stopped = true;
return context->retval;
}
static void processBuffer(android_logcat_context_internal* context,
log_device_t* dev, struct log_msg* buf) {
int bytesWritten = 0;
int err;
AndroidLogEntry entry;
char binaryMsgBuf[1024];
if (dev->binary) {
if (!context->eventTagMap && !context->hasOpenedEventTagMap) {
context->eventTagMap = android_openEventTagMap(nullptr);
context->hasOpenedEventTagMap = true;
}
err = android_log_processBinaryLogBuffer(
&buf->entry_v1, &entry, context->eventTagMap, binaryMsgBuf,
sizeof(binaryMsgBuf));
// printf(">>> pri=%d len=%d msg='%s'\n",
// entry.priority, entry.messageLen, entry.message);
} else {
err = android_log_processLogBuffer(&buf->entry_v1, &entry);
}
if ((err < 0) && !context->debug) return;
if (android_log_shouldPrintLine(
context->logformat, std::string(entry.tag, entry.tagLen).c_str(),
entry.priority)) {
bool match = regexOk(context, entry);
context->printCount += match;
if (match || context->printItAnyways) {
bytesWritten = android_log_printLogLine(context->logformat,
context->output_fd, &entry);
if (bytesWritten < 0) {
logcat_panic(context, HELP_FALSE, "output error");
return;
}
}
}
context->outByteCount += bytesWritten;
if (context->logRotateSizeKBytes > 0 &&
(context->outByteCount / 1024) >= context->logRotateSizeKBytes) {
rotateLogs(context);
}
}
LIBLOG_ABI_PUBLIC int android_log_printLogLine(AndroidLogFormat* p_format,
int fd,
const AndroidLogEntry* entry) {
int ret;
char defaultBuffer[512];
char* outBuffer = NULL;
size_t totalLen;
outBuffer = android_log_formatLogLine(
p_format, defaultBuffer, sizeof(defaultBuffer), entry, &totalLen);
if (!outBuffer) return -1;
do {
// 输出日志,fd或者是标准输出或者是文件
ret = write(fd, outBuffer, totalLen);
} while (ret < 0 && errno == EINTR);
if (ret < 0) {
fprintf(stderr, "+++ LOG: write failed (errno=%d)\n", errno);
ret = 0;
goto done;
}
if (((size_t)ret) < totalLen) {
fprintf(stderr, "+++ LOG: write partial (%d of %d)\n", ret, (int)totalLen);
goto done;
}
done:
if (outBuffer != defaultBuffer) {
free(outBuffer);
}
return ret;
}
结构体
system/core/liblog/include/log/logprint.h
typedef struct AndroidLogEntry_t {
time_t tv_sec;
long tv_nsec;
android_LogPriority priority;
int32_t uid;
int32_t pid;
int32_t tid;
const char* tag;
size_t tagLen;
size_t messageLen;
const char* message;
} AndroidLogEntry;
一条日志数据包括时间、日志优先级、uid、pid、tid、标签、信息。
调试相关
APP日志
Log类里面的v/d/i/w/e/wtf等frameworks/base/core/java/android/util/Log.java
。输出的LOG_ID是MAIN,即日志是存放在main唤醒缓冲区。
import android.util.Log;
Log.v("TAG", "msg");
framework日志
framework里面的日志调用Slog类里面的v/d/i/w/e/wtf/println等frameworks/base/core/java/android/util/Slog.java
。输出的LOG_ID是SYSTEM,即日志是存放在system唤醒缓冲区
import android.util.Slog;
Slog.v("TAG", "msg");
native日志
system/core/liblog/include/log/log_main.h
这个头文件里面定义的宏,ALOGV/ALOGD/ALOGI/ALOGW/ALOGE/ALOG_ASSERT等供native代码调用。如果使用ALOGV需要在源文件的开头位置#define LOG_NDEBUG 0
。一般通过包含头文件#include <utils/Log.h>
。
system/core/libutils/include/utils/Log.h
#include <log/log.h>
system/core/liblog/include/log/log.h
#include <log/log_main.h>
其实这些最终也是调用到liblog.so里面的方法__android_log_buf_write
。
#define LOG_NDEBUG 0
#include <utils/Log.h>
ALOGV("ddddddddddddddddyy");
ALOGV("xxxxxx%d, buffersize:%d\n", timeoutMillis, bufferSize);
kernel日志
printk
日志缓冲区
system/core/logd/LogBuffer.cpp
unsigned long mMaxSize[LOG_ID_MAX];
#define log_buffer_size(id) mMaxSize[id]
#define LOG_BUFFER_MIN_SIZE (64 * 1024UL)
#define LOG_BUFFER_MAX_SIZE (256 * 1024 * 1024UL)
数组mMaxSize存放着每个环形缓冲区对应的buffer size。buffer有一个合理的区间值,最小为64KB,最大为256MB。其中环形缓冲区一共有8个。id对应下面结构体,0是MAIN,3是SYSTEM,4是CRASH。
typedef enum log_id {
LOG_ID_MIN = 0,
LOG_ID_MAIN = 0,
LOG_ID_RADIO = 1,
LOG_ID_EVENTS = 2,
LOG_ID_SYSTEM = 3,
LOG_ID_CRASH = 4,
LOG_ID_STATS = 5,
LOG_ID_SECURITY = 6,
LOG_ID_KERNEL = 7, /* place last, third-parties can not use it */
LOG_ID_MAX
} log_id_t;
log daemon启动的时候,会根据预设定的逻辑分配环形缓冲区的大小。也可以通过logcat -G XX
来修改环形缓冲区的大小。
查看设备日志缓冲区的大小
# logcat -g
main: ring buffer is 64Kb (60Kb consumed), max entry is 5120b, max payload is 4068b
system: ring buffer is 64Kb (63Kb consumed), max entry is 5120b, max payload is 4068b
crash: ring buffer is 64Kb (0b consumed), max entry is 5120b, max payload is 4068b
设置设备日志缓冲区的大小
# logcat -G 100K/20MB
查看某一缓冲区的日志内容
# logcat -b main/system
直接logcat命令,可以输出所有缓冲区的内容。
知识点总结
stdout
stdout是stdio.h头文件里面定义的一个宏
bionic/libc/include/stdio.h
#define stdout (&__sF[1])
bionic/libc/stdio/stdio.cpp
FILE __sF[3] = {
std(__SRD, STDIN_FILENO),
std(__SWR, STDOUT_FILENO),
std(__SWR|__SNBF, STDERR_FILENO),
};
bionic/libc/include/unistd.h
#define STDIN_FILENO 0
#define STDOUT_FILENO 1
#define STDERR_FILENO 2
实际上是文件/proc/self/fd/1