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的最大缓存值。

步骤:


  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中有如下代码,处理设置缓存大小:

            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函数,只是写属性后是永久生效的。






1 0
原创粉丝点击