android logとlogcat分析(一)

20148 ワード

プログラム開発において、LOGはプログラム実行プロセスを記録するために広く使用されているメカニズムであり、プログラムデバッグにも製品運営におけるイベント記録にも使用できる.Androidシステムでは、シンプルで便利なLOGメカニズムが提供されており、開発者が使いやすいようになっています.この記事では、AndroidカーネルスペースとユーザースペースでのLOGの使用と表示方法について簡単に紹介します.
次の図はlogの使用方法を簡単に示しています.
public class LogDemo extends Activity {
  
 private static final String ACTIVITY_TAG="LogDemo";
 private Button bt;
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        //  findViewById  Button  
        bt = (Button)findViewById(R.id.bt);
        //      
        bt.setOnClickListener(new Button.OnClickListener(){
    @Override
   public void onClick(View v) {
    Log.v(LogDemo.ACTIVITY_TAG, "This is Verbose.");
    Log.d(LogDemo.ACTIVITY_TAG, "This is Debug.");
    Log.i(LogDemo.ACTIVITY_TAG, "This is Information");
    Log.w(LogDemo.ACTIVITY_TAG, "This is Warnning.");
    Log.e(LogDemo.ACTIVITY_TAG, "This is Error.");
   }
         
        });
    }
        
}
logクラスはframeworks/base/core/java/android/util/LOgに定義する.JAvaファイルにあります.
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 + '
' + getStackTraceString(tr)); } /** * Send a {@link #DEBUG} 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 d(String tag, String msg) { return println_native(LOG_ID_MAIN, DEBUG, tag, msg); } /** * Send a {@link #DEBUG} 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 d(String tag, String msg, Throwable tr) { return println_native(LOG_ID_MAIN, DEBUG, tag, msg + '
' + getStackTraceString(tr)); } /** * Send an {@link #INFO} 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 i(String tag, String msg) { return println_native(LOG_ID_MAIN, INFO, tag, msg); } /** * Send a {@link #INFO} 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 i(String tag, String msg, Throwable tr) { return println_native(LOG_ID_MAIN, INFO, tag, msg + '
' + getStackTraceString(tr)); } /** * Send a {@link #WARN} 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 w(String tag, String msg) { return println_native(LOG_ID_MAIN, WARN, tag, msg); } /** * Send a {@link #WARN} 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 w(String tag, String msg, Throwable tr) { return println_native(LOG_ID_MAIN, WARN, tag, msg + '
' + getStackTraceString(tr)); } /** * Checks to see whether or not a log for the specified tag is loggable at the specified level. * * The default level of any tag is set to INFO. This means that any level above and including * INFO will be logged. Before you make any calls to a logging method you should check to see * if your tag should be logged. You can change the default level by setting a system property: * 'setprop log.tag.<YOUR_LOG_TAG> <LEVEL>' * Where level is either VERBOSE, DEBUG, INFO, WARN, ERROR, ASSERT, or SUPPRESS. SUPPRESS will * turn off all logging for your tag. You can also create a local.prop file that with the * following in it: * 'log.tag.<YOUR_LOG_TAG>=<LEVEL>' * and place that in /data/local.prop. * * @param tag The tag to check. * @param level The level to check. * @return Whether or not that this is allowed to be logged. * @throws IllegalArgumentException is thrown if the tag.length() > 23. */ public static native boolean isLoggable(String tag, int level); /* * Send a {@link #WARN} 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 tr An exception to log */ public static int w(String tag, Throwable tr) { return println_native(LOG_ID_MAIN, WARN, tag, getStackTraceString(tr)); } /** * Send an {@link #ERROR} 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 e(String tag, String msg) { return println_native(LOG_ID_MAIN, ERROR, tag, msg); }

e d wとi関数はまた呼び出される
println_native(LOG_ID_MAIN, ERROR, tag, msg)
この定義もLog.JAvaで
public static native int println_native(int bufID,             int priority, String tag, String msg); }
明らかにこの関数はローカル関数です!!ローカルライブラリを呼び出します.これは、frameworks/base/core/jni/android_でjni関数を呼び出すlinuxレイヤでlog実装が実装されていることを示しています.util_Log.cppファイルのandroid_util_Log_println_native関数
namespace android {

struct levels_t {
    jint verbose;
    jint debug;
    jint info;
    jint warn;
    jint error;
    jint assert;
};
static levels_t levels;

static int toLevel(const char* value)
{
    switch (value[0]) {
        case 'V': return levels.verbose;
        case 'D': return levels.debug;
        case 'I': return levels.info;
        case 'W': return levels.warn;
        case 'E': return levels.error;
        case 'A': return levels.assert;
        case 'S': return -1; // SUPPRESS
    }
    return levels.info;
}

static jboolean isLoggable(const char* tag, jint level) {
    String8 key;
    key.append(LOG_NAMESPACE);
    key.append(tag);

    char buf[PROPERTY_VALUE_MAX];
    if (property_get(key.string(), buf, "") <= 0) {
        buf[0] = '\0';
    }

    int logLevel = toLevel(buf);
    return logLevel >= 0 && level >= logLevel;
}

static jboolean android_util_Log_isLoggable(JNIEnv* env, jobject clazz, jstring tag, jint level)
{
    if (tag == NULL) {
        return false;
    }

    const char* chars = env->GetStringUTFChars(tag, NULL);
    if (!chars) {
        return false;
    }

    jboolean result = false;
    if ((strlen(chars)+sizeof(LOG_NAMESPACE)) > PROPERTY_KEY_MAX) {
        char buf2[200];
        snprintf(buf2, sizeof(buf2), "Log tag \"%s\" exceeds limit of %d characters
", chars, PROPERTY_KEY_MAX - sizeof(LOG_NAMESPACE)); jniThrowException(env, "java/lang/IllegalArgumentException", buf2); } else { result = isLoggable(chars, level); } env->ReleaseStringUTFChars(tag, chars); return result; } bool android_util_Log_isVerboseLogEnabled(const char* tag) { return isLoggable(tag, levels.verbose); } /* * In class android.util.Log: * public static native int println_native(int buffer, int priority, String tag, String msg) */ 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; } /* * 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 }, }; int register_android_util_Log(JNIEnv* env) { jclass clazz = env->FindClass("android/util/Log"); if (clazz == NULL) { ALOGE("Can't find android/util/Log"); return -1; } levels.verbose = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "VERBOSE", "I")); levels.debug = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "DEBUG", "I")); levels.info = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "INFO", "I")); levels.warn = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "WARN", "I")); levels.error = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "ERROR", "I")); levels.assert = env->GetStaticIntField(clazz, env->GetStaticFieldID(clazz, "ASSERT", "I")); return AndroidRuntime::registerNativeMethods(env, "android/util/Log", gMethods, NELEM(gMethods)); } }; // namespace android

 { "println_native",  "(IILjava/lang/String;Ljava/lang/String;)I", (void*) android_util_Log_println_native },
};
はjavaがC++を呼び出すローカル関数で、上位層がlogを使用するたびに使用されます.dまたはlog.eすべての関数を待つとprintln_を呼び出すことに気づきましたnative,最後にC++のandroidを呼び出すutil_Log_println_native関数.
android_util_Log_println_nativeの内容は次のとおりです.
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;
}

ローカルstatic関数です.メモリに1部しか存在しないので、まず伝達msgが空であるかどうかを判断し、bufidが5以上であるかどうかを判断し、
env->GetStringUTFChars

上層部から伝達されたtagを取得し、
 msg = env->GetStringUTFChars(msgObj, NULL);

上層伝達のmsgを得た.
__android_log_buf_write(bufID, (android_LogPriority)priority, tag, msg);
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];

printf("------
"); 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; // 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; } 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); }

ここでは構造体配列iovecを用いてlogのtag,msg,prio情報を格納し,
struct iovec {
    const void*  iov_base;
    size_t       iov_len;
};
強制変換によりprio,tag,msgをivo_に保存ベースで関数を呼び出します
write_to_log(bufID, vec, 3);}
この関数はポインタであり、ポインタによって最後に呼び出された関数がコピーされることを発見しました.
__write_to_log_init
int (*write_to_log)(log_id_t, struct iovec *vec, size_t nr) __attribute__((visibility ("hidden"))) = __write_to_log_init;
static int __write_to_log_init(log_id_t log_id, struct iovec *vec, size_t nr)
{
#ifdef HAVE_PTHREADS
    pthread_mutex_lock(&log_init_lock);
#endif

    if (write_to_log == __write_to_log_init) {
        log_fds[LOG_ID_MAIN] = log_open("/dev/"LOGGER_LOG_MAIN, O_WRONLY);
        log_fds[LOG_ID_RADIO] = log_open("/dev/"LOGGER_LOG_RADIO, O_WRONLY);
        log_fds[LOG_ID_EVENTS] = log_open("/dev/"LOGGER_LOG_EVENTS, O_WRONLY);
        log_fds[LOG_ID_SYSTEM] = log_open("/dev/"LOGGER_LOG_SYSTEM, O_WRONLY);

        write_to_log = __write_to_log_kernel;

        if (log_fds[LOG_ID_MAIN] < 0 || log_fds[LOG_ID_RADIO] < 0 ||
                log_fds[LOG_ID_EVENTS] < 0) {
            log_close(log_fds[LOG_ID_MAIN]);
            log_close(log_fds[LOG_ID_RADIO]);
            log_close(log_fds[LOG_ID_EVENTS]);
            log_fds[LOG_ID_MAIN] = -1;
            log_fds[LOG_ID_RADIO] = -1;
            log_fds[LOG_ID_EVENTS] = -1;
            write_to_log = __write_to_log_null;
        }

        if (log_fds[LOG_ID_SYSTEM] < 0) {
            log_fds[LOG_ID_SYSTEM] = log_fds[LOG_ID_MAIN];
        }
    }

#ifdef HAVE_PTHREADS
    pthread_mutex_unlock(&log_init_lock);
#endif

    return write_to_log(log_id, vec, nr);
}

この関数ではまずスレッドロック関数を使用します
 pthread_mutex_lock(&log_init_lock);
最初はwrite_を定義しましたto_log == __write_to_log_Initですので、次の文は必ず実行されます.
static int log_fds[(int)LOG_ID_MAX] = { -1, -1, -1, -1 };static int配列です
log_fds[LOG_ID_MAIN] = log_open("/dev/"LOGGER_LOG_MAIN, O_WRONLY);
        log_fds[LOG_ID_RADIO] = log_open("/dev/"LOGGER_LOG_RADIO, O_WRONLY);
        log_fds[LOG_ID_EVENTS] = log_open("/dev/"LOGGER_LOG_EVENTS, O_WRONLY);
        log_fds[LOG_ID_SYSTEM] = log_open("/dev/"LOGGER_LOG_SYSTEM, O_WRONLY);

それぞれ
log_fds配列要素はlog_を保存しますOpenが開いているファイルハンドルを検索するとlogが見つかります.Open関数定義は
#if FAKE_LOG_DEVICE
// This will be defined when building for the host.
#define log_open(pathname, flags) fakeLogOpen(pathname, flags)
#define log_writev(filedes, vector, count) fakeLogWritev(filedes, vector, count)
#define log_close(filedes) fakeLogClose(filedes)
#else
#define log_open(pathname, flags) open(pathname, (flags) | O_CLOEXEC)
#define log_writev(filedes, vector, count) writev(filedes, vector, count)
#define log_close(filedes) close(filedes)
#endif

上の関数を使っているのか下の関数を使っているのか、FAKE_のみLOG_DEVICEが定義されているかどうかはわかりませんが、
ファイルシステム/core/liblog/Androidを開きます.mkファイル、FAKE_を発見LOG_DEVICEはここで定義されていますが、
# Shared and static library for host
# ========================================================
LOCAL_MODULE := liblog
LOCAL_SRC_FILES := $(liblog_host_sources)
LOCAL_LDLIBS := -lpthread
LOCAL_CFLAGS := -DFAKE_LOG_DEVICE=1
LOCAL_WHOLE_STATIC_LIBRARIES := libxlog
include $(BUILD_HOST_STATIC_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := liblog
LOCAL_WHOLE_STATIC_LIBRARIES := liblog
include $(BUILD_HOST_SHARED_LIBRARY)

ここでadbがhost端子であればFAKE_を定義するLOG_DEVICEは1で、コンパイルするときに上の関数を選んでコンパイルし、
Android製品ではほとんどがクライアントとして使用されているため
# Shared and static library for target
# ========================================================
include $(CLEAR_VARS)
LOCAL_MODULE := liblog
LOCAL_SRC_FILES := $(liblog_sources)
LOCAL_WHOLE_STATIC_LIBRARIES := libxlog
include $(BUILD_STATIC_LIBRARY)

include $(CLEAR_VARS)
LOCAL_MODULE := liblog
LOCAL_WHOLE_STATIC_LIBRARIES := liblog
include $(BUILD_SHARED_LIBRARY)

FAKEがないからLOG_DEVICE定義は次の関数を使用します.
log_open(pathname, flags) open(pathname, (flags) | O_CLOEXEC)
通常のlinuxシステムの通常ファイル操作関数open(pathname,(flags)|O_CLOEXEC)
linuxはデバイスをファイルとして扱うので、ファイル関数openを使用して開き、adb shell ls/dev/log/
Loggerドライバモジュールには、4つのデバイスファイルノードがあることを知っています.それぞれdev/log/main,dev/log/radio,dev/log/systemおよび
dev/log/event、これも私たちのlogです.Openが開いている4つのファイルは、読み書き可能に開きます.
ついでに次の2つも列挙します
 log_writev(filedes, vector, count) writev(filedes, vector, count)
log_close(filedes) close(filedes)
ファイルの開く操作を実行した後、write_to_logポインタ付与=_write_to_log_kernel、この関数こそ本当にlog駆動モジュールにログを書きます.
static int __write_to_log_kernel(log_id_t log_id, struct iovec *vec, size_t nr)
{
    ssize_t ret;
    int log_fd;

    if (/*(int)log_id >= 0 &&*/ (int)log_id < (int)LOG_ID_MAX) {
        log_fd = log_fds[(int)log_id];
    } else {
        return EBADF;
    }

    do {
        ret = log_writev(log_fd, vec, nr);
    } while (ret < 0 && errno == EINTR);

    return ret;
}

判定ロゴを表示id値が0以下4以下であるか
typedef enum {
    LOG_ID_MAIN = 0,
    LOG_ID_RADIO = 1,
    LOG_ID_EVENTS = 2,
    LOG_ID_SYSTEM = 3,

    LOG_ID_MAX
} log_id_t;

書き込み操作関数ret=log_を実行します.writev(log_fd, vec, nr);
上の解析ではlog_が示されていますwritev(filedes, vector, count) writev(filedes, vector, count)
関数を使用するのはwritev(filedes,vector,count)です
int  writev( int  fd, const struct iovec*  vecs, int  count )
{
    int   total = 0;

    for ( ; count > 0; count--, vecs++ ) {
        const char*  buf = (const char*)vecs->iov_base;
        int          len = (int)vecs->iov_len;
        
        while (len > 0) {
            int  ret = write( fd, buf, len );
            if (ret < 0) {
                if (total == 0)
                    total = -1;
                goto Exit;
            }
            if (ret == 0)
                goto Exit;

            total += ret;
            buf   += ret;
            len   -= ret;
        }
    }
Exit:    
    return total;
}

ここでは簡単なファイル操作関数writeですが、ここでは分析しません.