第一篇博客,讲的主要是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. private Slog() {
  3. }
  4. public static int v(String tag, String msg) {
  5. return Log.println_native(Log.LOG_ID_SYSTEM, Log.VERBOSE, tag, msg);
  6. }
  7. public static int v(String tag, String msg, Throwable tr) {
  8. return Log.println_native(Log.LOG_ID_SYSTEM, Log.VERBOSE, tag,
  9. msg + '\n' + Log.getStackTraceString(tr));
  10. }
  11. public static int d(String tag, String msg) {
  12. return Log.println_native(Log.LOG_ID_SYSTEM, Log.DEBUG, tag, msg);
  13. }

Rlog


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

Log

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

最终都是调用了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. if (msgObj == NULL) {
  7. jniThrowNullPointerException(env, "println needs a message");
  8. return -1;
  9. }
  10. if (bufID < 0 || bufID >= LOG_ID_MAX) {
  11. jniThrowNullPointerException(env, "bad bufID");
  12. return -1;
  13. }
  14. if (tagObj != NULL)
  15. tag = env->GetStringUTFChars(tagObj, NULL);
  16. msg = env->GetStringUTFChars(msgObj, NULL);
  17. int res = __android_log_buf_write(bufID, (android_LogPriority)priority, tag, msg);
  18. if (tag != NULL)
  19. env->ReleaseStringUTFChars(tagObj, tag);
  20. env->ReleaseStringUTFChars(msgObj, msg);
  21. return res;
  22. }

然后又调用了__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. if (!tag)
  6. tag = "";
  7. /* XXX: This needs to go! */
  8. if ((bufID != LOG_ID_RADIO) &&
  9. (!strcmp(tag, "HTC_RIL") ||
  10. !strncmp(tag, "RIL", 3) || /* Any log tag with "RIL" as the prefix */
  11. !strncmp(tag, "IMS", 3) || /* Any log tag with "IMS" as the prefix */
  12. !strcmp(tag, "AT") ||
  13. !strcmp(tag, "GSM") ||
  14. !strcmp(tag, "STK") ||
  15. !strcmp(tag, "CDMA") ||
  16. !strcmp(tag, "PHONE") ||
  17. !strcmp(tag, "SMS"))) {
  18. bufID = LOG_ID_RADIO;//这些tag也归类到radio中
  19. /* Inform third party apps/ril/radio.. to use Rlog or RLOG */
  20. snprintf(tmp_tag, sizeof(tmp_tag), "use-Rlog/RLOG-%s", tag);
  21. tag = tmp_tag;
  22. }
  23. #if __BIONIC__
  24. if (prio == ANDROID_LOG_FATAL) {
  25. android_set_abort_message(msg);
  26. }
  27. #endif
  28. vec[0].iov_base   = (unsigned char *) &prio;
  29. vec[0].iov_len    = 1;
  30. vec[1].iov_base   = (void *) tag;
  31. vec[1].iov_len    = strlen(tag) + 1;
  32. vec[2].iov_base   = (void *) msg;
  33. vec[2].iov_len    = strlen(msg) + 1;
  34. return write_to_log(bufID, vec, 3);
  35. }

而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. if (write_to_log == __write_to_log_init) {
  7. int ret;
  8. ret = __write_to_log_initialize();//先调用了__write_to_log_initialize
  9. if (ret < 0) {
  10. #if !defined(_WIN32)
  11. pthread_mutex_unlock(&log_init_lock);
  12. #endif
  13. #if (FAKE_LOG_DEVICE == 0)
  14. if (pstore_fd >= 0) {
  15. __write_to_log_daemon(log_id, vec, nr);//然后调用__write_to_log_daemon方法
  16. }
  17. #endif
  18. return ret;
  19. }
  20. write_to_log = __write_to_log_daemon;
  21. }
  22. #if !defined(_WIN32)
  23. pthread_mutex_unlock(&log_init_lock);
  24. #endif
  25. return write_to_log(log_id, vec, nr);
  26. }

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

上面初始化,我们的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. if (sock < 0) {
  5. sock = socket_local_server(socketName,
  6. ANDROID_SOCKET_NAMESPACE_RESERVED,
  7. SOCK_DGRAM);
  8. }
  9. int on = 1;
  10. if (setsockopt(sock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
  11. return -1;
  12. }
  13. return sock;
  14. }

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

这个函数主要讲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. unsigned long default_size = property_get_size(global_tuneable);//从系统属性中获取默认大小
  5. if (!default_size) {
  6. default_size = property_get_size(global_default);
  7. }
  8. log_id_for_each(i) {
  9. char key[PROP_NAME_MAX];
  10. snprintf(key, sizeof(key), "%s.%s",
  11. global_tuneable, android_log_id_to_name(i));
  12. unsigned long property_size = property_get_size(key);//从系统属性中获取某个log id的大小
  13. if (!property_size) {
  14. snprintf(key, sizeof(key), "%s.%s",
  15. global_default, android_log_id_to_name(i));
  16. property_size = property_get_size(key);
  17. }
  18. if (!property_size) {
  19. property_size = default_size;
  20. }
  21. if (!property_size) {
  22. property_size = LOG_BUFFER_SIZE;//没有设置属性就是这个值,是256k
  23. }
  24. if (setSize(i, property_size)) {
  25. setSize(i, LOG_BUFFER_MIN_SIZE);
  26. }
  27. }
  28. }

获取系统属性的话,是下面这个函数计算出来。系统属性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. char *cp;
  5. unsigned long value = strtoul(property, &cp, 10);
  6. switch(*cp) {
  7. case 'm':
  8. case 'M':
  9. value *= 1024;
  10. /* FALLTHRU */
  11. case 'k':
  12. case 'K':
  13. value *= 1024;
  14. /* FALLTHRU */
  15. case '\0':
  16. break;
  17. default:
  18. value = 0;
  19. }
  20. if (!valid_size(value)) {
  21. value = 0;
  22. }
  23. return value;
  24. }

最后我们每个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. switch(*cp) {
  9. case 'g':
  10. case 'G':
  11. setLogSize *= 1024;
  12. /* FALLTHRU */
  13. case 'm':
  14. case 'M':
  15. setLogSize *= 1024;
  16. /* FALLTHRU */
  17. case 'k':
  18. case 'K':
  19. setLogSize *= 1024;
  20. /* FALLTHRU */
  21. case '\0':
  22. break;
  23. default:
  24. setLogSize = 0;
  25. }
  26. if (!setLogSize) {
  27. fprintf(stderr, "ERROR: -G <num><multiplier>\n");
  28. return EXIT_FAILURE;
  29. }
  30. }
  31. 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. if (argc < 3) {
  9. cli->sendMsg("Missing Argument");
  10. return 0;
  11. }
  12. int id = atoi(argv[1]);
  13. if ((id < LOG_ID_MIN) || (LOG_ID_MAX <= id)) {
  14. cli->sendMsg("Range Error");
  15. return 0;
  16. }
  17. unsigned long size = atol(argv[2]);
  18. if (mBuf.setSize((log_id_t) id, size)) {
  19. cli->sendMsg("Range Error");
  20. return 0;
  21. }
  22. cli->sendMsg("success");
  23. return 0;
  24. }

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

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

android 6.0 logcat机制(一)java层写log,logd接受log相关推荐

  1. android 6.0 logcat机制(三)logd处理请求log

    这篇博客,分析的是logd接收到logcat传来的命令,logd如何把log传给logcat. 一.logd LogReader监听logdr socket 在logd的main函数中会有一个监听lo ...

  2. android 6.0 logcat机制(二)logcat从logd中获取log保存到文件中

    这篇博客分析的是logcat是如何获取logd中的log,然后写入文件. 一.设置保存log文件的路径 在手机刚开机的时候,会有类似如下命令执行 /system/bin/logcat -r 5120 ...

  3. android 6.0 log,android 6.0 logcat机制(二)logcat从logd中获取log保存到文件中

    一.设置保存log文件的路径 在手机刚开机的时候,会有类似如下命令执行 /system/bin/logcat -r 5120 -v threadtime -v usec -v printable -n ...

  4. android 6.0 log,android 6.0 logcat机制(三)logd处理请求log

    一.logd LogReader监听logdr socket 在logd的main函数中会有一个监听logdr socket的LogReader类 我们来看下main函数的源码 LogReader * ...

  5. Android 6.0 PM机制系列(四) APK安装需要空间分析

    前言 在Android 9.0 PM机制系列(四) APK安装需要空间分析文章中,我们重点分析了Android9.0需要的最小APK安装存储空间大小.结论就是:只要系统空间小于Math.min(get ...

  6. 深入理解 Android 9.0 Crash 机制(二)

    极力推荐Android 开发大总结文章:欢迎收藏 Android 开发技术文章大总结 本篇文章主要介绍 Android 开发中的部分知识点,通过阅读本篇文章,您将收获以下内容: 九. AppError ...

  7. Android 8.0 VDEX机制简介

    背景 Android 8.0在odex的基础上又引入了vdex机制,目的是为了降低dex2oat时间. 因为当系统ota后,用户自己安装的应用是不会发生任何变化的,但framework代码已经发生了变 ...

  8. Android 9.0 PM机制系列(四) APK安装需要空间分析

    前言 在PM机制系列前三篇,我们着重分析了安装的整个流程,没有具体到很多细节问题. 这一篇文章我们就会具体到很多细节问题.本篇主要就是围绕一个问题展开: 安装APK到底需要多少空间不会报错INSTAL ...

  9. Android实战开发Handler机制深度解析

    本文为自己多年来在Android实战开发过程中总结归纳的一些常见问题,现在分享出来希望对初学者有所帮助. 本文出自门心叼龙的博客,转载请注明出处: https://blog.csdn.net/gedu ...

最新文章

  1. 「小程序JAVA实战」小程序的页面重定向(60)
  2. 如何防止锚标签上的默认值?
  3. Mysql总结(二)
  4. 一小时过c语言,一小时学会C语言.docx
  5. MySQL 最全优化指南
  6. 小红书创始人瞿芳回应裁员风波:战略部署清晰 人员翻倍
  7. 2023年山东大学社会工作考研成功上岸经验分享
  8. 部署Ansible与常用模块
  9. 从图书馆进入网络刷题练习与考试平台
  10. html5 调用歌词播放器,如何用h5+js实现音乐歌词同步播放器
  11. 计算机技术 安防 工程师考试,2020年上半年信息安全工程师考试报考指南
  12. 基于FPGA的脉冲压缩设计(Matlab+vivado)
  13. oracle18c静默安装教程,centos7安装Oracle18c
  14. cad二开之不通过netload加载命令(bundle文件的使用)
  15. 12月15日(第12天)
  16. Aria2整合FileRun自建离线下载网
  17. MEC与C-V2X融合应用场景白皮书
  18. 三国杀代码12武将C++
  19. 如何提高云服务器性能,提高云服务器性能
  20. mysql端口号3306被占用_使用pandas将excel表格数据导入到mysql中

热门文章

  1. 利用ipv6技术,废旧笔记本变成server
  2. 1计算机世界中的时间概念
  3. Linux(Ubuntu16.04)自学笔记,资源整理
  4. Atom - 介绍和使用方法(好用的文本编辑器,代码提示高亮、Markdown)
  5. MySQL-SQL语句的优化
  6. MySQL、SqlServer、Oracle 三种数据库的优缺点总结
  7. elasticsearch查询报错411状态码 The requested URL could not be retrieved
  8. MySQL函数---条件判断函数
  9. html在浏览器显示图片,html - 在所有Web浏览器中显示TIFF图像
  10. 变速不变调播放mp3-QT-QAudioOutput-lame-sonic