android 6.0 logcat机制(一)java层写log,logd接受log
来源:互联网 发布:aso榜单优化 编辑:程序博客网 时间:2024/06/05 09:32
第一篇博客,讲的主要是c++,java中打印log,然后通过socket传给logd,然后logd是如何处理接受log的。
一、logcat常用命令
logcat -c
清除已有log信息
logcat -b main 显示主缓冲区的log
logcat -b radio 显示无线缓冲区的log
logcat -b events 显示事件缓冲区的log
logcat -f [filename] 将log保存到指定的文件中,例如logcat -b radio -f /data/radio.log
比较常用的是显示时间:logcat -v time &
logcat -g 查看缓冲区的大小
logcat -g main
logcat -g radio
logcat -g events
logcat打印/dev/log设备下的三个文件radio, events, main数据
logcat默认是输出main、system缓冲区的log
二、java的Log打印
在android的java层的log有几种,比如Log Slog Rlog
我们先来看看其实现:
Slog
public final class Slog { private Slog() { } public static int v(String tag, String msg) { return Log.println_native(Log.LOG_ID_SYSTEM, Log.VERBOSE, tag, msg); } public static int v(String tag, String msg, Throwable tr) { return Log.println_native(Log.LOG_ID_SYSTEM, Log.VERBOSE, tag, msg + '\n' + Log.getStackTraceString(tr)); } public static int d(String tag, String msg) { return Log.println_native(Log.LOG_ID_SYSTEM, Log.DEBUG, tag, msg); }
Rlog
public final class Rlog { private Rlog() { } public static int v(String tag, String msg) { return Log.println_native(Log.LOG_ID_RADIO, Log.VERBOSE, tag, msg); } public static int v(String tag, String msg, Throwable tr) { return Log.println_native(Log.LOG_ID_RADIO, Log.VERBOSE, tag, msg + '\n' + Log.getStackTraceString(tr)); } public static int d(String tag, String msg) { return Log.println_native(Log.LOG_ID_RADIO, Log.DEBUG, tag, msg); }
Log
private Log() { } /** * Send a {@link #VERBOSE} log message. * @param tag Used to identify the source of a log message. It usually identifies * the class or activity where the log call occurs. * @param msg The message you would like logged. */ public static int v(String tag, String msg) { return println_native(LOG_ID_MAIN, VERBOSE, tag, msg); } /** * Send a {@link #VERBOSE} log message and log the exception. * @param tag Used to identify the source of a log message. It usually identifies * the class or activity where the log call occurs. * @param msg The message you would like logged. * @param tr An exception to log */ public static int v(String tag, String msg, Throwable tr) { return println_native(LOG_ID_MAIN, VERBOSE, tag, msg + '\n' + getStackTraceString(tr)); }
最终都是调用了printIn_native只是id不同main,system,radio。
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;}然后又调用了__android_log_buf_write函数,是在system/core/liblog/logd_write.c文件中
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) && (!strcmp(tag, "HTC_RIL") || !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */ !strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" as the prefix */ !strcmp(tag, "AT") || !strcmp(tag, "GSM") || !strcmp(tag, "STK") || !strcmp(tag, "CDMA") || !strcmp(tag, "PHONE") || !strcmp(tag, "SMS"))) { bufID = LOG_ID_RADIO;//这些tag也归类到radio中 /* Inform third party apps/ril/radio.. to use Rlog or RLOG */ snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag); tag = tmp_tag; }#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);}而write_to_log就是__write_to_log_init函数
static int __write_to_log_init(log_id_t, struct iovec *vec, size_t nr);static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init;
因为这里log库是公用的代码,host target都是也就是有的是pc代码,有的是手机代码共有了
static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr){#if !defined(_WIN32) pthread_mutex_lock(&log_init_lock);#endif if (write_to_log == __write_to_log_init) { int ret; ret = __write_to_log_initialize();//先调用了__write_to_log_initialize if (ret < 0) {#if !defined(_WIN32) pthread_mutex_unlock(&log_init_lock);#endif#if (FAKE_LOG_DEVICE == 0) if (pstore_fd >= 0) { __write_to_log_daemon(log_id, vec, nr);//然后调用__write_to_log_daemon方法 }#endif return ret; } write_to_log = __write_to_log_daemon; }#if !defined(_WIN32) pthread_mutex_unlock(&log_init_lock);#endif return write_to_log(log_id, vec, nr);}
上面的函数我们先调用了__write_to_log_initialize函数,然后再调用了__write_to_log_daemon函数
static int __write_to_log_initialize(){ int i, ret = 0;#if FAKE_LOG_DEVICE//这个是host才有的 for (i = 0; i < LOG_ID_MAX; i++) { char buf[sizeof("/dev/log_system")]; snprintf(buf, sizeof(buf), "/dev/log_%s", android_log_id_to_name(i)); log_fds[i] = fakeLogOpen(buf, O_WRONLY); }#else if (pstore_fd < 0) { pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY)); } if (logd_fd < 0) { i = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0)); if (i < 0) { ret = -errno; } else if (TEMP_FAILURE_RETRY(fcntl(i, F_SETFL, O_NONBLOCK)) < 0) { ret = -errno; close(i); } else { struct sockaddr_un un; memset(&un, 0, sizeof(struct sockaddr_un)); un.sun_family = AF_UNIX; strcpy(un.sun_path, "/dev/socket/logdw");// 我们的socket if (TEMP_FAILURE_RETRY(connect(i, (struct sockaddr *)&un, sizeof(struct sockaddr_un))) < 0) { ret = -errno; close(i); } else { logd_fd = i;//连接socket成功后,保存在log_fd这个全局变量中 } } }#endif return ret;}
上面初始化,我们的socket是dev/socket/logdw,__write_to_log_daemon函数我们就不看了就是往socket写log,而用的就是log_fd这个fd。
三、logd接受socket传过来的log
往socket写之后,又会在哪里接受呢?
答案是logd
我们先看下logd的main函数
在logd的main函数中如下代码:
LogListener *swl = new LogListener(logBuf, reader); // Backlog and /proc/sys/net/unix/max_dgram_qlen set to large value if (swl->startListener(300)) { exit(1); }我们看下其构造函数,
LogListener::LogListener(LogBuffer *buf, LogReader *reader) : SocketListener(getLogSocket(), false), logbuf(buf), reader(reader) {}
int LogListener::getLogSocket() { static const char socketName[] = "logdw"; int sock = android_get_control_socket(socketName); if (sock < 0) { sock = socket_local_server(socketName, ANDROID_SOCKET_NAMESPACE_RESERVED, SOCK_DGRAM); } int on = 1; if (setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) { return -1; } return sock;}
监听的socket为logdw,当有socket数据来的会调用onDataAvailable函数,这个函数我们就不看了,在这个函数中调用了LogBuffer::log函数。
int LogBuffer::log(log_id_t log_id, log_time realtime, uid_t uid, pid_t pid, pid_t tid, const char *msg, unsigned short len) { if ((log_id >= LOG_ID_MAX) || (log_id < 0)) { return -EINVAL; } LogBufferElement *elem = new LogBufferElement(log_id, realtime,//新建一个LogBufferElement对象 uid, pid, tid, msg, len); int prio = ANDROID_LOG_INFO; const char *tag = NULL; if (log_id == LOG_ID_EVENTS) { tag = android::tagToName(elem->getTag()); } else { prio = *msg; tag = msg + 1; } if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) { // Log traffic received to total pthread_mutex_lock(&mLogElementsLock); stats.add(elem);//统计信息 stats.subtract(elem); pthread_mutex_unlock(&mLogElementsLock); delete elem; return -EACCES; } pthread_mutex_lock(&mLogElementsLock); // Insert elements in time sorted order if possible // NB: if end is region locked, place element at end of list LogBufferElementCollection::iterator it = mLogElements.end(); LogBufferElementCollection::iterator last = it; while (last != mLogElements.begin()) { --it; if ((*it)->getRealTime() <= realtime) { break; } last = it; } if (last == mLogElements.end()) { mLogElements.push_back(elem); } else { uint64_t end = 1; bool end_set = false; bool end_always = false; LogTimeEntry::lock(); LastLogTimes::iterator t = mTimes.begin(); while(t != mTimes.end()) { LogTimeEntry *entry = (*t); if (entry->owned_Locked()) { if (!entry->mNonBlock) { end_always = true; break; } if (!end_set || (end <= entry->mEnd)) { end = entry->mEnd; end_set = true; } } t++; } if (end_always || (end_set && (end >= (*last)->getSequence()))) { mLogElements.push_back(elem);//将对象插入mLogElements } else { mLogElements.insert(last,elem); } LogTimeEntry::unlock(); } stats.add(elem); maybePrune(log_id); pthread_mutex_unlock(&mLogElementsLock); return len;}
这个函数主要讲log的内容信息封装在LogBufferElement,然后放到mLogElements中,最后调用maybePrune函数。
// Prune at most 10% of the log entries or 256, whichever is less.//// mLogElementsLock must be held when this function is called.void LogBuffer::maybePrune(log_id_t id) { size_t sizes = stats.sizes(id);//某个id的log个数 unsigned long maxSize = log_buffer_size(id); if (sizes > maxSize) { size_t sizeOver = sizes - ((maxSize * 9) / 10); size_t elements = stats.elements(id); size_t minElements = elements / 10; unsigned long pruneRows = elements * sizeOver / sizes; if (pruneRows <= minElements) { pruneRows = minElements; } if (pruneRows > 256) { pruneRows = 256; } prune(id, pruneRows); }}
我们首先看下这个英文注释,如果某个id的log超过了最大值,要删除256或者log总数的10%。
我们再来看看log_buffer_size这个函数,这个函数是某个id的log最大数。
int LogBuffer::setSize(log_id_t id, unsigned long size) { // Reasonable limits ... if (!valid_size(size)) { return -1; } pthread_mutex_lock(&mLogElementsLock); log_buffer_size(id) = size; pthread_mutex_unlock(&mLogElementsLock); return 0;}在setSize函数中初始化,那我们再看看是谁调用了setSize函数
void LogBuffer::init() { static const char global_tuneable[] = "persist.logd.size"; // Settings App static const char global_default[] = "ro.logd.size"; // BoardConfig.mk unsigned long default_size = property_get_size(global_tuneable);//从系统属性中获取默认大小 if (!default_size) { default_size = property_get_size(global_default); } log_id_for_each(i) { char key[PROP_NAME_MAX]; snprintf(key, sizeof(key), "%s.%s", global_tuneable, android_log_id_to_name(i)); unsigned long property_size = property_get_size(key);//从系统属性中获取某个log id的大小 if (!property_size) { snprintf(key, sizeof(key), "%s.%s", global_default, android_log_id_to_name(i)); property_size = property_get_size(key); } if (!property_size) { property_size = default_size; } if (!property_size) { property_size = LOG_BUFFER_SIZE;//没有设置属性就是这个值,是256k } if (setSize(i, property_size)) { setSize(i, LOG_BUFFER_MIN_SIZE); } }}获取系统属性的话,是下面这个函数计算出来。系统属性persist.logd.size,或者persist.logd.size.radio等
static unsigned long property_get_size(const char *key) { char property[PROPERTY_VALUE_MAX]; property_get(key, property, ""); char *cp; unsigned long value = strtoul(property, &cp, 10); switch(*cp) { case 'm': case 'M': value *= 1024; /* FALLTHRU */ case 'k': case 'K': value *= 1024; /* FALLTHRU */ case '\0': break; default: value = 0; } if (!valid_size(value)) { value = 0; } return value;}
最后我们每个log id的最大值都是256k,超过的话就要调用prune删除对应id的log了。
四、总结
最后我们可以通过设置系统属性persist.logd.size来设置每个log id的最大缓存值,或者persist.logd.size.radio设置每个id的最大缓存值。
步骤:
将手机连上电脑并且进入root
setproppersist.logd.size.radio 1024k
reboot 重启
另外可以用getprop | grep logd查看设置的属性是否生效
logcat -g 可以查看每个id 的缓存大小
当然这是通过属性的方法设置,我们还可以通过logcat的命令,logcat -G 10m是设置所有的id的大小,logcat -b radio -G 10m是设置radio的log的缓存大小
在logcat中有如下代码,处理设置缓存大小:
case 'G': { char *cp; if (strtoll(optarg, &cp, 0) > 0) { setLogSize = strtoll(optarg, &cp, 0); } else { setLogSize = 0; } switch(*cp) { case 'g': case 'G': setLogSize *= 1024; /* FALLTHRU */ case 'm': case 'M': setLogSize *= 1024; /* FALLTHRU */ case 'k': case 'K': setLogSize *= 1024; /* FALLTHRU */ case '\0': break; default: setLogSize = 0; } if (!setLogSize) { fprintf(stderr, "ERROR: -G <num><multiplier>\n"); return EXIT_FAILURE; } } break;
最终会设置到logd中去,在logcat中调用的是android_logger_set_log_size函数
if (setLogSize && android_logger_set_log_size(dev->logger, setLogSize)) { logcat_panic(false, "failed to set the log size"); }
最终会调用到到logd中的runCommand中:
int CommandListener::SetBufSizeCmd::runCommand(SocketClient *cli, int argc, char **argv) { setname(); if (!clientHasLogCredentials(cli)) { cli->sendMsg("Permission Denied"); return 0; } if (argc < 3) { cli->sendMsg("Missing Argument"); return 0; } int id = atoi(argv[1]); if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) { cli->sendMsg("Range Error"); return 0; } unsigned long size = atol(argv[2]); if (mBuf.setSize((log_id_t) id, size)) { cli->sendMsg("Range Error"); return 0; } cli->sendMsg("success"); return 0;}
最终也会调用到LogBuffer::setSize函数,只是写属性后是永久生效的。
- android 6.0 logcat机制(一)java层写log,logd接受log
- android 6.0 logcat机制(一)java层写log,logd接受log
- android 6.0 logcat机制(三)logd处理请求log
- android 6.0 logcat机制(三)logd处理请求log
- android 6.0 logcat机制(二)logcat从logd中获取log保存到文件中
- android 6.0 logcat机制(二)logcat从logd中获取log保存到文件中
- android log丢失(一)使用logd丢失log原理
- android log丢失(一)使用logd丢失log原理
- android log 和logcat 分析(一)
- android log丢失(三)动态切换logd机制和kernel机制
- android log丢失(三)动态切换logd机制和kernel机制
- 在Android C/C++层添加LOG调试(LOGI\LOGD\LOGE...)输出支持
- 在Android C/C++层添加LOG调试(LOGI\LOGD\LOGE...)输出支持 开花结果
- 在Android C/C++层添加LOG调试(LOGI\LOGD\LOGE...)输出支持 开花结果
- 在Android C/C++层添加LOG调试(LOGI\LOGD\LOGE...)输出支持
- 在Android C/C++层添加LOG调试(LOGI\LOGD\LOGE...)输出支持
- 在Android C/C++层添加LOG调试(LOGI\LOGD\LOGE...)输出支持
- 在Android C/C++层添加LOG调试(LOGI\LOGD\LOGE...)输出支持
- ajax跨域的解决办法
- 两个div并列显示
- 使用Lucene.Net实现全文检索
- 随笔知识1
- Java类的初始化
- android 6.0 logcat机制(一)java层写log,logd接受log
- 【BZOJ1624】[Usaco2008 Open] Clear And Present Danger 寻宝之路【Floyd】
- ListView/GridView 双列/多列显示
- 深入理解 java I/O
- WordPress插件机制实现原理
- 检索 COM 类工厂中 CLSID 为 {} 的组件时失败,原因是出现以下错误: 80070005
- 寻找旋转排序数组中的最小值 II
- MySQL命令行导出数据库
- 通过clonezilla将windows2008虚拟机从vCenter迁移至kvm