본문 바로가기
IT 기술/Android

Android Log 분석

by 땅뚱 2012. 1. 19.
<자바클래스 android.util.Log>
안드로이드에서 로그를 사용하고 싶은 경우에 자바 레이어에서는 android.util.Log 클래스를 사용한다.
android.util.Log 클래스는 frameworks/base/core/java/android/util/Log.java 에 구현되어 있다.

Log 클래스안에 구현된 메소드는 v(), d(), i(), w(), e() 등이 있다. 각각은 VERVOSE, DEBUG, INFO, WARN, ERROR 를 나타낸다. 이는 vervosity(자세함? 장황함)의 순서이다. Verbose 는 개발하는 동안에만 컴파일되고, Debug 는 컴파일되지만, runtime 시에 strip 된다. Error / warning / info 는 항상 동작한다.

보통은 본인이 작성하는 클래스에 TAG 라는 상수를 선언하는 것이 좋다. 왜냐면 Log 클래스의 메소드들의 첫번째 파라미터가 tag 를 나타내는 String 타입이기 때문에, 보통 Log 함수를 호출할 경우에 TAG 를 선언한 후에 Log.v(TAG, "statements") 와 같이 사용한다.

Log.v() 함수의 구현부를 살펴보자.

public static int v(String tag, String msg) {
            return println_native(LOG_ID_MAIN, VERBOSE, tag, msg);
}

public static native int println_native(int bufID, int priority, String tag, String msg);

위 코드에서 보다시피 Log.v() 함수는 native 함수인 println_native 함수를 호출하도록 되어있다. println_native() 함수는 frameworks/base/core/jni/android_util_Log.cpp 에 구현되어 있다.

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) {
        jclass npeClazz;

        npeClazz = env->FindClass("java/lang/NullPointerException");
        assert(npeClazz != NULL);

        env->ThrowNew(npeClazz, "println needs a message");
        return -1;
    }

    if (bufID < 0 || bufID >= LOG_ID_MAX) {
        jclass npeClazz;

        npeClazz = env->FindClass("java/lang/NullPointerException");
        assert(npeClazz != NULL);

        env->ThrowNew(npeClazz, "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 registration.
 */
static 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 },
};

관련부분을 뜯어보면 위 코드와 같이 구현되어 있다. 즉 println_native() 함수는 jni 의 매핑을 통해서 실제로는 android_util_Log_println_native() 함수를 호출하도록 되어있다. JNI 로 두 함수가 연결되는 내용은 다음을 참조하면 된다. (JNI 네이티브 함수에 직접 등록방법) 이에 대해서는 따로 정리할 예정이다.

android_util_Log_println_native() 함수는 최종적으로 __android_log_buf_write() 함수를 호출한다. 이 함수는 system/core/liblog/logd_write.c 에 구현되어 있고, write_to_log 라는 함수 포인터를 호출하는 데, 처음에 __write_to_log_init 으로 세팅되어 있고, 결과적으로 __write_to_log_kernel 함수를 호출하게 된다.

__write_to_log_kernel 은 각각 관련된 character device 에 해당 로그를 write 하게 된다. 안드로이드의 log 는 총 3가지 카테고리로 나뉘는데, GSM/CDMA/RIL/SMS 등 radio 관련된 내용(/dev/radio)과 시스템/하드웨어 관련된 내용(/dev/system), 이벤트 관련 내용(/dev/events) 등으로 나뉜다.

device read/write 관련 자세한 것은 kernel/drivers/staging/android/logger.c 파일에 구현되어 있다.
파일을 열어보면 Robert Love 가 구현한 것으로 되어있다.(Linux Kernel Development 의 저자인 Robert Love 가 구글에 입사했군요.)

각각 character device 는 초기화 시점(init_logger())에 misc device 로 등록(register_misc())하고, 각각의 file operation 은 logger_fops 로 등록한다.(DEFINE_LOGGER_DEVICE)
각 디바이스는 log_main(64K) / log_events(128K) / log_radio(64K) / log_system(64k) 의 버퍼 크기를 갖는다.


logger_fops 는 다음과 같이 정의되었기 때문에, 해당 character device 에 write 를 하게 되면, logger_aio_write() 함수가 호출된다.

static const struct file_operations logger_fops = {
    .owner = THIS_MODULE,
    .read = logger_read,
    .aio_write = logger_aio_write,
    .poll = logger_poll,
    .unlocked_ioctl = logger_ioctl,
    .compat_ioctl = logger_ioctl,
    .open = logger_open,
    .release = logger_release,
};

logger_aio_write() 는 최종적으로 do_write_log_from_user() 를 호출하고, 해당 함수는 디바이스에 해당하는 버퍼에 파라미터로 넘어온 user 영역 데이터를 write offset 부분에 기록하고, write offset 을 증가시킨다.

반대로 read 는 logger_read() 함수를 호출하고, 최종적으로 do_read_log_to_user() 를 호출한다. 해당 함수는 읽을 데이터가 없는 경우 TASK_INTERRUPTIBLE 상태로 sleep 한다. 데이터가 있는 경우 해당 버퍼에서 user 영역 메모리로 데이터를 복사해주고 read offset 을 수정해준다.

참고 : http://blog.csdn.net/fuyajun01/article/details/6787729