当前位置:   article > 正文

android 6.0 logcat机制(一)java层写log,logd接受log_android java log

android java log

第一篇博客,讲的主要是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

[java]  view plain  copy
  1. public final class Slog {  
  2.   
  3.     private Slog() {  
  4.     }  
  5.   
  6.     public static int v(String tag, String msg) {  
  7.         return Log.println_native(Log.LOG_ID_SYSTEM, Log.VERBOSE, tag, msg);  
  8.     }  
  9.   
  10.     public static int v(String tag, String msg, Throwable tr) {  
  11.         return Log.println_native(Log.LOG_ID_SYSTEM, Log.VERBOSE, tag,  
  12.                 msg + '\n' + Log.getStackTraceString(tr));  
  13.     }  
  14.   
  15.     public static int d(String tag, String msg) {  
  16.         return Log.println_native(Log.LOG_ID_SYSTEM, Log.DEBUG, tag, msg);  
  17.     }  

Rlog


[java]  view plain  copy
  1. public final class Rlog {  
  2.   
  3.     private Rlog() {  
  4.     }  
  5.   
  6.     public static int v(String tag, String msg) {  
  7.         return Log.println_native(Log.LOG_ID_RADIO, Log.VERBOSE, tag, msg);  
  8.     }  
  9.   
  10.     public static int v(String tag, String msg, Throwable tr) {  
  11.         return Log.println_native(Log.LOG_ID_RADIO, Log.VERBOSE, tag,  
  12.                 msg + '\n' + Log.getStackTraceString(tr));  
  13.     }  
  14.   
  15.     public static int d(String tag, String msg) {  
  16.         return Log.println_native(Log.LOG_ID_RADIO, Log.DEBUG, tag, msg);  
  17.     }  

Log

[java]  view plain  copy
  1. private Log() {  
  2. }  
  3.   
  4. /** 
  5.  * Send a {@link #VERBOSE} log message. 
  6.  * @param tag Used to identify the source of a log message.  It usually identifies 
  7.  *        the class or activity where the log call occurs. 
  8.  * @param msg The message you would like logged. 
  9.  */  
  10. public static int v(String tag, String msg) {  
  11.     return println_native(LOG_ID_MAIN, VERBOSE, tag, msg);  
  12. }  
  13.   
  14. /** 
  15.  * Send a {@link #VERBOSE} log message and log the exception. 
  16.  * @param tag Used to identify the source of a log message.  It usually identifies 
  17.  *        the class or activity where the log call occurs. 
  18.  * @param msg The message you would like logged. 
  19.  * @param tr An exception to log 
  20.  */  
  21. public static int v(String tag, String msg, Throwable tr) {  
  22.     return println_native(LOG_ID_MAIN, VERBOSE, tag, msg + '\n' + getStackTraceString(tr));  
  23. }  

最终都是调用了printIn_native只是id不同main,system,radio。

[java]  view plain  copy
  1. static jint android_util_Log_println_native(JNIEnv* env, jobject clazz,  
  2.         jint bufID, jint priority, jstring tagObj, jstring msgObj)  
  3. {  
  4.     const char* tag = NULL;  
  5.     const char* msg = NULL;  
  6.   
  7.     if (msgObj == NULL) {  
  8.         jniThrowNullPointerException(env, "println needs a message");  
  9.         return -1;  
  10.     }  
  11.   
  12.     if (bufID < 0 || bufID >= LOG_ID_MAX) {  
  13.         jniThrowNullPointerException(env, "bad bufID");  
  14.         return -1;  
  15.     }  
  16.   
  17.     if (tagObj != NULL)  
  18.         tag = env->GetStringUTFChars(tagObj, NULL);  
  19.     msg = env->GetStringUTFChars(msgObj, NULL);  
  20.   
  21.     int res = __android_log_buf_write(bufID, (android_LogPriority)priority, tag, msg);  
  22.   
  23.     if (tag != NULL)  
  24.         env->ReleaseStringUTFChars(tagObj, tag);  
  25.     env->ReleaseStringUTFChars(msgObj, msg);  
  26.   
  27.     return res;  
  28. }  
然后又调用了__android_log_buf_write函数,是在system/core/liblog/logd_write.c文件中

[java]  view plain  copy
  1. int __android_log_buf_write(int bufID, int prio, const char *tag, const char *msg)  
  2. {  
  3.     struct iovec vec[3];  
  4.     char tmp_tag[32];  
  5.   
  6.     if (!tag)  
  7.         tag = "";  
  8.   
  9.     /* XXX: This needs to go! */  
  10.     if ((bufID != LOG_ID_RADIO) &&  
  11.          (!strcmp(tag, "HTC_RIL") ||  
  12.         !strncmp(tag, "RIL"3) || /* Any log tag with "RIL" as the prefix */  
  13.         !strncmp(tag, "IMS"3) || /* Any log tag with "IMS" as the prefix */  
  14.         !strcmp(tag, "AT") ||  
  15.         !strcmp(tag, "GSM") ||  
  16.         !strcmp(tag, "STK") ||  
  17.         !strcmp(tag, "CDMA") ||  
  18.         !strcmp(tag, "PHONE") ||  
  19.         !strcmp(tag, "SMS"))) {  
  20.             bufID = LOG_ID_RADIO;//这些tag也归类到radio中  
  21.             /* Inform third party apps/ril/radio.. to use Rlog or RLOG */  
  22.             snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);  
  23.             tag = tmp_tag;  
  24.     }  
  25.   
  26. #if __BIONIC__  
  27.     if (prio == ANDROID_LOG_FATAL) {  
  28.         android_set_abort_message(msg);  
  29.     }  
  30. #endif  
  31.   
  32.     vec[0].iov_base   = (unsigned char *) &prio;  
  33.     vec[0].iov_len    = 1;  
  34.     vec[1].iov_base   = (void *) tag;  
  35.     vec[1].iov_len    = strlen(tag) + 1;  
  36.     vec[2].iov_base   = (void *) msg;  
  37.     vec[2].iov_len    = strlen(msg) + 1;  
  38.   
  39.     return write_to_log(bufID, vec, 3);  
  40. }  
而write_to_log就是__write_to_log_init函数

[java]  view plain  copy
  1. static int __write_to_log_init(log_id_t, struct iovec *vec, size_t nr);  
  2. static int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) = __write_to_log_init;  


因为这里log库是公用的代码,host target都是也就是有的是pc代码,有的是手机代码共有了

[java]  view plain  copy
  1. static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr)  
  2. {  
  3. #if !defined(_WIN32)  
  4.     pthread_mutex_lock(&log_init_lock);  
  5. #endif  
  6.   
  7.     if (write_to_log == __write_to_log_init) {  
  8.         int ret;  
  9.   
  10.         ret = __write_to_log_initialize();//先调用了__write_to_log_initialize  
  11.         if (ret < 0) {  
  12. #if !defined(_WIN32)  
  13.             pthread_mutex_unlock(&log_init_lock);  
  14. #endif  
  15. #if (FAKE_LOG_DEVICE == 0)  
  16.             if (pstore_fd >= 0) {  
  17.                 __write_to_log_daemon(log_id, vec, nr);//然后调用__write_to_log_daemon方法  
  18.             }  
  19. #endif  
  20.             return ret;  
  21.         }  
  22.   
  23.         write_to_log = __write_to_log_daemon;  
  24.     }  
  25.   
  26. #if !defined(_WIN32)  
  27.     pthread_mutex_unlock(&log_init_lock);  
  28. #endif  
  29.   
  30.     return write_to_log(log_id, vec, nr);  
  31. }  

上面的函数我们先调用了__write_to_log_initialize函数,然后再调用了__write_to_log_daemon函数

[java]  view plain  copy
  1. static int __write_to_log_initialize()  
  2. {  
  3.     int i, ret = 0;  
  4.   
  5. #if FAKE_LOG_DEVICE//这个是host才有的  
  6.     for (i = 0; i < LOG_ID_MAX; i++) {  
  7.         char buf[sizeof("/dev/log_system")];  
  8.         snprintf(buf, sizeof(buf), "/dev/log_%s", android_log_id_to_name(i));  
  9.         log_fds[i] = fakeLogOpen(buf, O_WRONLY);  
  10.     }  
  11. #else  
  12.     if (pstore_fd < 0) {  
  13.         pstore_fd = TEMP_FAILURE_RETRY(open("/dev/pmsg0", O_WRONLY));  
  14.     }  
  15.   
  16.     if (logd_fd < 0) {  
  17.         i = TEMP_FAILURE_RETRY(socket(PF_UNIX, SOCK_DGRAM | SOCK_CLOEXEC, 0));  
  18.         if (i < 0) {  
  19.             ret = -errno;  
  20.         } else if (TEMP_FAILURE_RETRY(fcntl(i, F_SETFL, O_NONBLOCK)) < 0) {  
  21.             ret = -errno;  
  22.             close(i);  
  23.         } else {  
  24.             struct sockaddr_un un;  
  25.             memset(&un, 0, sizeof(struct sockaddr_un));  
  26.             un.sun_family = AF_UNIX;  
  27.             strcpy(un.sun_path, "/dev/socket/logdw");// 我们的socket  
  28.   
  29.             if (TEMP_FAILURE_RETRY(connect(i, (struct sockaddr *)&un,  
  30.                                            sizeof(struct sockaddr_un))) < 0) {  
  31.                 ret = -errno;  
  32.                 close(i);  
  33.             } else {  
  34.                 logd_fd = i;//连接socket成功后,保存在log_fd这个全局变量中  
  35.             }  
  36.         }  
  37.     }  
  38. #endif  
  39.   
  40.     return ret;  
  41. }  

上面初始化,我们的socket是dev/socket/logdw,__write_to_log_daemon函数我们就不看了就是往socket写log,而用的就是log_fd这个fd。

三、logd接受socket传过来的log

往socket写之后,又会在哪里接受呢?

答案是logd

我们先看下logd的main函数


在logd的main函数中如下代码:

[java]  view plain  copy
  1. LogListener *swl = new LogListener(logBuf, reader);  
  2. // Backlog and /proc/sys/net/unix/max_dgram_qlen set to large value  
  3. if (swl->startListener(300)) {  
  4.     exit(1);  
  5. }  
我们看下其构造函数,

[java]  view plain  copy
  1. LogListener::LogListener(LogBuffer *buf, LogReader *reader) :  
  2.         SocketListener(getLogSocket(), false),  
  3.         logbuf(buf),  
  4.         reader(reader) {  
  5. }  
[java]  view plain  copy
  1. int LogListener::getLogSocket() {  
  2.     static const char socketName[] = "logdw";  
  3.     int sock = android_get_control_socket(socketName);  
  4.   
  5.     if (sock < 0) {  
  6.         sock = socket_local_server(socketName,  
  7.                                    ANDROID_SOCKET_NAMESPACE_RESERVED,  
  8.                                    SOCK_DGRAM);  
  9.     }  
  10.   
  11.     int on = 1;  
  12.     if (setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {  
  13.         return -1;  
  14.     }  
  15.     return sock;  
  16. }  

监听的socket为logdw,当有socket数据来的会调用onDataAvailable函数,这个函数我们就不看了,在这个函数中调用了LogBuffer::log函数。

[java]  view plain  copy
  1. int LogBuffer::log(log_id_t log_id, log_time realtime,  
  2.                    uid_t uid, pid_t pid, pid_t tid,  
  3.                    const char *msg, unsigned short len) {  
  4.     if ((log_id >= LOG_ID_MAX) || (log_id < 0)) {  
  5.         return -EINVAL;  
  6.     }  
  7.   
  8.     LogBufferElement *elem = new LogBufferElement(log_id, realtime,//新建一个LogBufferElement对象  
  9.                                                   uid, pid, tid, msg, len);  
  10.     int prio = ANDROID_LOG_INFO;  
  11.     const char *tag = NULL;  
  12.     if (log_id == LOG_ID_EVENTS) {  
  13.         tag = android::tagToName(elem->getTag());  
  14.     } else {  
  15.         prio = *msg;  
  16.         tag = msg + 1;  
  17.     }  
  18.     if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) {  
  19.         // Log traffic received to total  
  20.         pthread_mutex_lock(&mLogElementsLock);  
  21.         stats.add(elem);//统计信息  
  22.         stats.subtract(elem);  
  23.         pthread_mutex_unlock(&mLogElementsLock);  
  24.         delete elem;  
  25.         return -EACCES;  
  26.     }  
  27.   
  28.     pthread_mutex_lock(&mLogElementsLock);  
  29.   
  30.     // Insert elements in time sorted order if possible  
  31.     //  NB: if end is region locked, place element at end of list  
  32.     LogBufferElementCollection::iterator it = mLogElements.end();  
  33.     LogBufferElementCollection::iterator last = it;  
  34.     while (last != mLogElements.begin()) {  
  35.         --it;  
  36.         if ((*it)->getRealTime() <= realtime) {  
  37.             break;  
  38.         }  
  39.         last = it;  
  40.     }  
  41.   
  42.     if (last == mLogElements.end()) {  
  43.         mLogElements.push_back(elem);  
  44.     } else {  
  45.         uint64_t end = 1;  
  46.         bool end_set = false;  
  47.         bool end_always = false;  
  48.   
  49.         LogTimeEntry::lock();  
  50.   
  51.         LastLogTimes::iterator t = mTimes.begin();  
  52.         while(t != mTimes.end()) {  
  53.             LogTimeEntry *entry = (*t);  
  54.             if (entry->owned_Locked()) {  
  55.                 if (!entry->mNonBlock) {  
  56.                     end_always = true;  
  57.                     break;  
  58.                 }  
  59.                 if (!end_set || (end <= entry->mEnd)) {  
  60.                     end = entry->mEnd;  
  61.                     end_set = true;  
  62.                 }  
  63.             }  
  64.             t++;  
  65.         }  
  66.   
  67.         if (end_always  
  68.                 || (end_set && (end >= (*last)->getSequence()))) {  
  69.             mLogElements.push_back(elem);//将对象插入mLogElements  
  70.         } else {  
  71.             mLogElements.insert(last,elem);  
  72.         }  
  73.   
  74.         LogTimeEntry::unlock();  
  75.     }  
  76.   
  77.     stats.add(elem);  
  78.     maybePrune(log_id);  
  79.     pthread_mutex_unlock(&mLogElementsLock);  
  80.   
  81.     return len;  
  82. }  

这个函数主要讲log的内容信息封装在LogBufferElement,然后放到mLogElements中,最后调用maybePrune函数。

[java]  view plain  copy
  1. // Prune at most 10% of the log entries or 256, whichever is less.  
  2. //  
  3. // mLogElementsLock must be held when this function is called.  
  4. void LogBuffer::maybePrune(log_id_t id) {  
  5.     size_t sizes = stats.sizes(id);//某个id的log个数  
  6.     unsigned long maxSize = log_buffer_size(id);  
  7.     if (sizes > maxSize) {  
  8.         size_t sizeOver = sizes - ((maxSize * 9) / 10);  
  9.         size_t elements = stats.elements(id);  
  10.         size_t minElements = elements / 10;  
  11.         unsigned long pruneRows = elements * sizeOver / sizes;  
  12.         if (pruneRows <= minElements) {  
  13.             pruneRows = minElements;  
  14.         }  
  15.         if (pruneRows > 256) {  
  16.             pruneRows = 256;  
  17.         }  
  18.         prune(id, pruneRows);  
  19.     }  
  20. }  

我们首先看下这个英文注释,如果某个id的log超过了最大值,要删除256或者log总数的10%。

我们再来看看log_buffer_size这个函数,这个函数是某个id的log最大数。

[java]  view plain  copy
  1. int LogBuffer::setSize(log_id_t id, unsigned long size) {  
  2.     // Reasonable limits ...  
  3.     if (!valid_size(size)) {  
  4.         return -1;  
  5.     }  
  6.     pthread_mutex_lock(&mLogElementsLock);  
  7.     log_buffer_size(id) = size;  
  8.     pthread_mutex_unlock(&mLogElementsLock);  
  9.     return 0;  
  10. }  
在setSize函数中初始化,那我们再看看是谁调用了setSize函数
[java]  view plain  copy
  1. void LogBuffer::init() {  
  2.     static const char global_tuneable[] = "persist.logd.size"// Settings App  
  3.     static const char global_default[] = "ro.logd.size";       // BoardConfig.mk  
  4.   
  5.     unsigned long default_size = property_get_size(global_tuneable);//从系统属性中获取默认大小  
  6.     if (!default_size) {  
  7.         default_size = property_get_size(global_default);  
  8.     }  
  9.   
  10.     log_id_for_each(i) {  
  11.         char key[PROP_NAME_MAX];  
  12.   
  13.         snprintf(key, sizeof(key), "%s.%s",  
  14.                  global_tuneable, android_log_id_to_name(i));  
  15.         unsigned long property_size = property_get_size(key);//从系统属性中获取某个log id的大小  
  16.   
  17.         if (!property_size) {  
  18.             snprintf(key, sizeof(key), "%s.%s",  
  19.                      global_default, android_log_id_to_name(i));  
  20.             property_size = property_get_size(key);  
  21.         }  
  22.   
  23.         if (!property_size) {  
  24.             property_size = default_size;  
  25.         }  
  26.   
  27.         if (!property_size) {  
  28.             property_size = LOG_BUFFER_SIZE;//没有设置属性就是这个值,是256k  
  29.         }  
  30.   
  31.         if (setSize(i, property_size)) {  
  32.             setSize(i, LOG_BUFFER_MIN_SIZE);  
  33.         }  
  34.     }  
  35. }  
获取系统属性的话,是下面这个函数计算出来。系统属性persist.logd.size,或者persist.logd.size.radio等
[java]  view plain  copy
  1. static unsigned long property_get_size(const char *key) {  
  2.     char property[PROPERTY_VALUE_MAX];  
  3.     property_get(key, property, "");  
  4.   
  5.     char *cp;  
  6.     unsigned long value = strtoul(property, &cp, 10);  
  7.   
  8.     switch(*cp) {  
  9.     case 'm':  
  10.     case 'M':  
  11.         value *= 1024;  
  12.     /* FALLTHRU */  
  13.     case 'k':  
  14.     case 'K':  
  15.         value *= 1024;  
  16.     /* FALLTHRU */  
  17.     case '\0':  
  18.         break;  
  19.   
  20.     default:  
  21.         value = 0;  
  22.     }  
  23.   
  24.     if (!valid_size(value)) {  
  25.         value = 0;  
  26.     }  
  27.   
  28.     return value;  
  29. }  

最后我们每个log id的最大值都是256k,超过的话就要调用prune删除对应id的log了。



四、总结

最后我们可以通过设置系统属性persist.logd.size来设置每个log id的最大缓存值,或者persist.logd.size.radio设置每个id的最大缓存值。

步骤:


  1. 将手机连上电脑并且进入root

  2. setproppersist.logd.size.radio 1024k

  3. reboot 重启

另外可以用getprop | grep logd查看设置的属性是否生效

logcat -g 可以查看每个id 的缓存大小


当然这是通过属性的方法设置,我们还可以通过logcat的命令,logcat -G 10m是设置所有的id的大小,logcat -b radio -G 10m是设置radio的log的缓存大小

在logcat中有如下代码,处理设置缓存大小:

[java]  view plain  copy
  1. case 'G': {  
  2.     char *cp;  
  3.     if (strtoll(optarg, &cp, 0) > 0) {  
  4.         setLogSize = strtoll(optarg, &cp, 0);  
  5.     } else {  
  6.         setLogSize = 0;  
  7.     }  
  8.   
  9.     switch(*cp) {  
  10.     case 'g':  
  11.     case 'G':  
  12.         setLogSize *= 1024;  
  13.     /* FALLTHRU */  
  14.     case 'm':  
  15.     case 'M':  
  16.         setLogSize *= 1024;  
  17.     /* FALLTHRU */  
  18.     case 'k':  
  19.     case 'K':  
  20.         setLogSize *= 1024;  
  21.     /* FALLTHRU */  
  22.     case '\0':  
  23.     break;  
  24.   
  25.     default:  
  26.         setLogSize = 0;  
  27.     }  
  28.   
  29.     if (!setLogSize) {  
  30.         fprintf(stderr, "ERROR: -G <num><multiplier>\n");  
  31.         return EXIT_FAILURE;  
  32.     }  
  33. }  
  34. break;  

最终会设置到logd中去,在logcat中调用的是android_logger_set_log_size函数

[java]  view plain  copy
  1. if (setLogSize && android_logger_set_log_size(dev->logger, setLogSize)) {  
  2.     logcat_panic(false"failed to set the log size");  
  3. }  

最终会调用到到logd中的runCommand中:

[java]  view plain  copy
  1. int CommandListener::SetBufSizeCmd::runCommand(SocketClient *cli,  
  2.                                          int argc, char **argv) {  
  3.     setname();  
  4.     if (!clientHasLogCredentials(cli)) {  
  5.         cli->sendMsg("Permission Denied");  
  6.         return 0;  
  7.     }  
  8.   
  9.     if (argc < 3) {  
  10.         cli->sendMsg("Missing Argument");  
  11.         return 0;  
  12.     }  
  13.   
  14.     int id = atoi(argv[1]);  
  15.     if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) {  
  16.         cli->sendMsg("Range Error");  
  17.         return 0;  
  18.     }  
  19.   
  20.     unsigned long size = atol(argv[2]);  
  21.     if (mBuf.setSize((log_id_t) id, size)) {  
  22.         cli->sendMsg("Range Error");  
  23.         return 0;  
  24.     }  
  25.   
  26.     cli->sendMsg("success");  
  27.     return 0;  
  28. }  

最终也会调用到LogBuffer::setSize函数,只是写属性后是永久生效的。


原文地址: http://blog.csdn.net/kc58236582/article/details/51073489

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/745376
推荐阅读
相关标签
  

闽ICP备14008679号