<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                合規國際互聯網加速 OSASE為企業客戶提供高速穩定SD-WAN國際加速解決方案。 廣告
                Logcat工具的初始化過程是從文件logcat.cpp中的函數main開始的,它會打開日志設備和解析命令行參數。這個函數的實現代碼比較長,我們分段來閱讀。 **system/core/logcat/logcat.cpp** ~~~ static AndroidLogFormat * g_logformat; static bool g_nonblock = false; static int g_tail_lines = 0; static const char * g_outputFileName = NULL; static int g_logRotateSizeKBytes = 0; // 0 means "no log rotation" static int g_maxRotatedLogs = DEFAULT_MAX_ROTATED_LOGS; // 0 means "unbounded" static int g_outFD = -1; static off_t g_outByteCount = 0; static int g_printBinary = 0; static int g_devCount = 0; static EventTagMap* g_eventTagMap = NULL; int main(int argc, char **argv) { int err; int hasSetLogFormat = 0; ...... const char *forceFilters = NULL; log_device_t* devices = NULL; log_device_t* dev; bool needBinary = false; g_logformat = android_log_format_new(); ~~~ 第25行調用函數android_log_format_new來創建一個全局的日志記錄輸出格式和輸出過濾器對象g_logformat。 **system/core/liblog/logprint.c** ~~~ AndroidLogFormat *android_log_format_new() { AndroidLogFormat *p_ret; p_ret = calloc(1, sizeof(AndroidLogFormat)); p_ret->global_pri = ANDROID_LOG_VERBOSE; p_ret->format = FORMAT_BRIEF; return p_ret; } ~~~ 從函數android_log_format_new的實現就可以看出,全局變量g_logformat指定的日志記錄輸出格式為FORMAT_BRIEF,而指定的全局默認日志記錄過濾優先級為ANDROID_LOG_VERBOSE。 **system/core/logcat/logcat.cpp** ~~~ for (;;) { int ret; ret = getopt(argc, argv, "cdt:gsQf:r::n:v:b:B"); ...... switch(ret) { ...... case 'd': g_nonblock = true; break; case 't': g_nonblock = true; g_tail_lines = atoi(optarg); break; ...... case 'b': { char* buf = (char*) malloc(strlen(LOG_FILE_DIR) + strlen(optarg) + 1); strcpy(buf, LOG_FILE_DIR); strcat(buf, optarg); bool binary = strcmp(optarg, "events") == 0; if (binary) { needBinary = true; } if (devices) { dev = devices; while (dev->next) { dev = dev->next; } dev->next = new log_device_t(buf, binary, optarg[0]); } else { devices = new log_device_t(buf, binary, optarg[0]); } android::g_devCount++; } break; case 'B': android::g_printBinary = 1; break; case 'f': // redirect output to a file android::g_outputFileName = optarg; break; case 'r': if (optarg == NULL) { android::g_logRotateSizeKBytes = DEFAULT_LOG_ROTATE_SIZE_KBYTES; } else { long logRotateSize; char *lastDigit; if (!isdigit(optarg[0])) { ...... } android::g_logRotateSizeKBytes = atoi(optarg); } break; case 'n': if (!isdigit(optarg[0])) { ...... } android::g_maxRotatedLogs = atoi(optarg); break; case 'v': err = setLogFormat(optarg); ...... hasSetLogFormat = 1; break; ...... } } ~~~ 函數第26行到第102行的for循環依次對命令行參數進行解析。命令行參數的字符串解析是通過調用函數getopt來實現的,它的返回值ret表示命令行中的一個選項,而選項對應的值保存在變量optarg中。接下來,我們就分別對命令行中的選項d、t、b、B、f、r、n和v進行介紹。 如果使用選項d來啟動Logcat工具,那么函數第35行就會把全局變量g_nonblock的值設置為true,表示當Logger日志驅動程序中沒有日志記錄可讀時,Logcat工具就直接退出。 如果使用選項t來啟動Logcat工具,那么函數第39行就會將選項后面的數字保存在全局變量g_tail_lines中,同時第38行會將全局變量g_nonblock的值設置為true。其中,全局變量g_tail_lines表示Logcat工具每次在輸出日志記錄時,只輸出最新的日志記錄條數。 如果使用選項b來啟動Logcat工具,那么函數第52行到第60行代碼就會將選項后面的字符串取出來,并且為它創建一個log_device_t結構體,表示Logcat工具要打開的日志設備。選項后面的字符串的取值為“main”、“radio”或者 “events”,表示要打開的日志設備分別為/dev/log/main、/dev/log/radio或者/dev/log/events。當選項后面的字符串為“events”時,函數第49行會將變量needBinary的值設置為true,表示Logcat工具要解析目標設備上的/system/etc/event-log-tags文件的內容,以便可以將日志設備 /dev/log/events的內容從二進制格式轉換為文本格式。每次創建一個log_device_t結構體時,全局變量g_devCount的值就會加1,表示Logcat工具所打開的日志設備個數。 > 如果在啟動Logcat工具時,沒有使用選項b,那么Logcat工具默認打開的日志設備就為/dev/log/main。 如果使用選項B來啟動Logcat工具,那么函數第65行就會將全局變量g_printBinary的值設置為1,表示要以二進制格式來輸出日志記錄。這時候就不需要對日志記錄的內容進行解析了。 如果使用選項f來啟動Logcat工具,那么函數第70行就會將選項后面的字符串取出來,并且保存在全局變量g_outputFileName中,用作日志記錄的輸出文件名稱。當輸入到文件g_outputFileName的日志記錄的大小(單位是字節)達到設定的量時,Logcat工具就會將接下來的日志記錄輸出到另外一個文件中,直到這個文件的日志記錄的大小也達到設定的量為止。用作日志記錄輸出的文件的命名方式是遵循一定的規律的。例如,假設選項f后面的字符串為 “logfile”,那么第一個日志記錄輸出文件的名稱就為“logfile”,接下來的日志記錄輸出文件的名稱依次為“logfile.1”、“logfile.2”、“logfile.3”等。日志記錄輸出文件的大小由選項r來指定,而日志記錄輸出文件的個數由選項n來指定。當日志記錄輸出文件的個數達到設定的值時,并且每一個日志記錄輸出文件的大小也達到設定的值時,Logcat工具就會循環使用已有的日志記錄輸出文件,即將原來的日志記錄輸出文件的內容擦掉,然后將新的日志記錄寫入到這些文件中。 如果使用選項r來啟動Logcat工具,那么函數第84行就會將選項后面的數字保存在全局變量g_logRotateSizeKBytes中,用來表示每一個日志記錄輸出文件的最大容量。如果該選項后面沒有指定數字,那么全局變量g_logRotateSizeKBytes的值就默認設置為DEFAULT_LOG_ROTATE_SIZE_KBYTES。 **system/core/logcat/logcat.cpp** ~~~ #define DEFAULT_LOG_ROTATE_SIZE_KBYTES 16 ~~~ 全局變量g_logRotateSizeKBytes的值被初始化為0,表示所有的日志記錄輸出文件的容量沒有限制,這會導致所有的日志記錄都輸出到文件g_outputFileName中。 如果使用選項n來啟動Logcat工具,那么函數第92行就會將選項后面的數字保存在全局變量g_maxRotatedLogs中,表示日志記錄輸出文件的最大個數。全局變量g_maxRotateLogs的值被初始化為DEFAULT_MAX_ROTATED_LOGS。 **system/core/logcat/logcat.cpp** ~~~ #define DEFAULT_MAX_ROTATED_LOGS 16 ~~~ 如果使用選項v來啟動Logcat工具,那么函數第95行就會調用函數setLogFormat將選項后面的字符串轉換為相應的AndroidLogPrintFormat枚舉值。該選項后面的字符串取值為“brief”、“process”、“tag”、“thread”、“raw”、“time”、“threadtime”或者“long”,分別對應于枚舉AndroidLogPrintFormat中的每一個值。 函數setLogFormat的實現如下所示。 **system/core/logcat/logcat.cpp** ~~~ static int setLogFormat(const char * formatString) { static AndroidLogPrintFormat format; format = android_log_formatFromString(formatString); if (format == FORMAT_OFF) { // FORMAT_OFF means invalid string return -1; } android_log_setPrintFormat(g_logformat, format); return 0; } ~~~ 第5行調用函數android_log_formatFromString將字符串formatString轉換為一個AndroidLogPrintFormat枚舉值。 **system/core/liblog/logprint.c**、 ~~~ AndroidLogPrintFormat android_log_formatFromString(const char * formatString) { static AndroidLogPrintFormat format; if (strcmp(formatString, "brief") == 0) format = FORMAT_BRIEF; else if (strcmp(formatString, "process") == 0) format = FORMAT_PROCESS; else if (strcmp(formatString, "tag") == 0) format = FORMAT_TAG; else if (strcmp(formatString, "thread") == 0) format = FORMAT_THREAD; else if (strcmp(formatString, "raw") == 0) format = FORMAT_RAW; else if (strcmp(formatString, "time") == 0) format = FORMAT_TIME; else if (strcmp(formatString, "threadtime") == 0) format = FORMAT_THREADTIME; else if (strcmp(formatString, "long") == 0) format = FORMAT_LONG; else format = FORMAT_OFF; return format; } ~~~ 如果參數formatString是一個非法字符串,那么函數android_log_formatFromString就會將它轉換為一個FORMAT_OFF值;否則,就通過第5行到第12行代碼來得到正確的AndroidLogPrintFormat枚舉值。 回到函數setLogFormat中,將前面得到的日志記錄輸出格式保存在變量format中,接著第12行繼續調用函數android_log_setPrintFormat將變量format的值設置到全局變量g_logformat的成員變量format中,用來描述Logcat工具的日志記錄輸出格式。 **system/core/liblog/logprint.c** ~~~ void android_log_setPrintFormat(AndroidLogFormat *p_format, AndroidLogPrintFormat format) { p_format->format=format; } ~~~ 回到main函數中,如果指定了選項v,并且成功地對它進行了解析,那么函數第98行就會將變量hasSetLogFormat的值設置為1,表示設置了Logcat工具的日志記錄輸出格式。 函數main解析完成命令行參數后,繼續往下執行。 **system/core/logcat/logcat.cpp** ~~~ if (!devices) { devices = new log_device_t(strdup("/dev/"LOGGER_LOG_MAIN), false, 'm'); android::g_devCount = 1; int accessmode = (mode & O_RDONLY) ? R_OK : 0 | (mode & O_WRONLY) ? W_OK : 0; // only add this if it's available if (0 == access("/dev/"LOGGER_LOG_SYSTEM, accessmode)) { devices->next = new log_device_t(strdup("/dev/"LOGGER_LOG_SYSTEM), false, 's'); android::g_devCount++; } } ~~~ 如果啟動Logcat工具時,沒有指定選項b,那么變量devices的值就會等于NULL。這時候Logcat工具就會默認打開日志設備/dev/log/main來讀取它的日志記錄。如果日志設備/dev/log/system也存在,那么Logcat工具也會一起打開它來讀取日志記錄。 打開要讀取日志記錄的日志設備之后,函數main繼續往下執行。 **system/core/logcat/logcat.cpp** ~~~ android::setupOutput(); ~~~ 第115行調用函數setupOutput來設置日志記錄是輸出到文件中還是打印到標準輸出中,它的實現如下所示。 **system/core/logcat/logcat.cpp** ~~~ static void setupOutput() { if (g_outputFileName == NULL) { g_outFD = STDOUT_FILENO; } else { struct stat statbuf; g_outFD = openLogFile(g_outputFileName); if (g_outFD < 0) { perror ("couldn't open output file"); exit(-1); } fstat(g_outFD, &statbuf); g_outByteCount = statbuf.st_size; } } ~~~ 第4行判斷全局變量g_outputFileName的值是否等于NULL。如果是,則說明要把日志記錄打印到標準輸出中,因此就將全局變量g_outFD指向標準輸出文件描述符STDOUT_FILENO;否則,就要將日志記錄輸出到文件g_outputFileName中。如果是將日志記錄輸出到文件中,那么函數第10行調用openLogFile函數以APPEND方式打開日志記錄輸出文件g_outputFileName。 **system/core/logcat/logcat.cpp** ~~~ static int openLogFile (const char *pathname) { return open(g_outputFileName, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR); } ~~~ 回到函數setupOutput中,第17行獲得當前日志記錄輸出文件g_outputFileName的大小,并且將它保存在全局變量g_outByteCount中。接下來Logcat工具往文件g_outputFileName輸出日志記錄時,就會相應地增加全局變量g_outByteCount的值。當全局變量g_outByteCount的值達到g_logRotateSizeKBytes時,Logcat工具就會將后面的日志記錄輸出到另外一個文件中。 回到函數main中,繼續往下執行。 **system/core/logcat/logcat.cpp** ~~~ if (hasSetLogFormat == 0) { const char* logFormat = getenv("ANDROID_PRINTF_LOG"); if (logFormat != NULL) { err = setLogFormat(logFormat); ...... } } ~~~ 如果啟動Logcat工具時,沒有指定選項v,那么變量hasSetLogFormat的值就會等于0,表示沒有指定日志記錄的輸出格式。這時候Logcat工具就會檢查環境變量ANDROID_PRINTF_LOG的值。如果環境變量ANDROID_PRINTF_LOG存在,那么就會調用函數setLogFormat將它的值作為Logcat工具的日志記錄輸出格式。 設置好日志記錄的輸出格式之后,函數main繼續往下執行。 **system/core/logcat/logcat.cpp** ~~~ if (forceFilters) { err = android_log_addFilterString(g_logformat, forceFilters); ...... } else if (argc == optind) { // Add from environment variable char *env_tags_orig = getenv("ANDROID_LOG_TAGS"); if (env_tags_orig != NULL) { err = android_log_addFilterString(g_logformat, env_tags_orig); ...... } } else { // Add from commandline for (int i = optind ; i < argc ; i++) { err = android_log_addFilterString(g_logformat, argv[i]); ...... } } ~~~ 在啟動Logcat工具時,我們可以指定一個不公開的選項Q,這時候Logcat工具就會去讀取目標設備上的/proc/cmdline文件,檢查里面有沒有設置日志記錄輸出過濾器。如果設置了,那么變量forceFilters就指向這個日志記錄輸出過濾器,因此,函數第125行就會調用函數android_log_addFilterString將它增加到Logcat工具的日志記錄輸出過濾器列表中。 如果啟動Logcat工具時,沒有指定選項Q,那么Logcat工具就會繼續檢查命令行是否還帶有其他額外的參數。如果沒有,即第127行的if語句為true,那么Logcat工具就會將環境變量ANDROID_LOG_TAGS的值作為日志記錄輸出的一個過濾器來使用。 如果啟動Logcat工具時,沒有指定選項Q,但是命令行帶有額外的參數,那么函數第137行到第140行的for循環就會檢查這些額外的參數是否是一個日志記錄輸出過濾表達式。例如,當使用下面的命令來啟動Logcat工具時: `USER@MACHINE:~/Android$ adb logcat LOGTAG:I` 最后一個參數“LOGTAG:I”就稱為日志記錄輸出過濾表達式。日志記錄輸出過濾表達式的格式為“[:priority]”,其中,tag為任意字符串,表示一個日志記錄標簽;priority是一個字符,表示一個日志記錄優先級。合法的priority字符為數字‘0’到‘9’,或者字母‘v’、‘d’、‘i’、‘w’、‘f’、‘s’和‘*’,它們的具體含義在后面會進一步解釋。這時候函數第138行就會調用函數android_log_addFilterString來解析這些日志記錄輸出過濾表達式,并且將它增加到Logcat工具的日志記錄輸出過濾器列表中。 函數android_log_addFilterString的實現如下所示。 **system/core/liblog/logprint.c** ~~~ /** * filterString: a comma/whitespace-separated set of filter expressions * * eg "AT:d *:i" * * returns 0 on success and -1 on invalid expression * * Assumes single threaded execution * */ int android_log_addFilterString(AndroidLogFormat *p_format, const char *filterString) { char *filterStringCopy = strdup (filterString); char *p_cur = filterStringCopy; char *p_ret; int err; // Yes, I'm using strsep while (NULL != (p_ret = strsep(&p_cur, " \t,"))) { // ignore whitespace-only entries if(p_ret[0] != '\0') { err = android_log_addFilterRule(p_format, p_ret); if (err < 0) { goto error; } } } free (filterStringCopy); return 0; error: free (filterStringCopy); return -1; } ~~~ 第二個參數filterString可以同時包括若干個日志記錄輸出過濾表達式,它們以空格、制表符或者逗號分割。每一個日志記錄輸出過濾表達式都是使用函數android_log_addFilterRule來解析的,它的實現如下所示。 **system/core/liblog/logprint.c** ~~~ /** * filterExpression: a single filter expression * eg "AT:d" * * returns 0 on success and -1 on invalid expression * * Assumes single threaded execution */ int android_log_addFilterRule(AndroidLogFormat *p_format, const char *filterExpression) { size_t i=0; size_t tagNameLength; android_LogPriority pri = ANDROID_LOG_DEFAULT; tagNameLength = strcspn(filterExpression, ":"); if (tagNameLength == 0) { goto error; } if(filterExpression[tagNameLength] == ':') { pri = filterCharToPri(filterExpression[tagNameLength+1]); if (pri == ANDROID_LOG_UNKNOWN) { goto error; } } if(0 == strncmp("*", filterExpression, tagNameLength)) { // This filter expression refers to the global filter // The default level for this is DEBUG if the priority // is unspecified if (pri == ANDROID_LOG_DEFAULT) { pri = ANDROID_LOG_DEBUG; } p_format->global_pri = pri; } else { // for filter expressions that don't refer to the global // filter, the default is verbose if the priority is unspecified if (pri == ANDROID_LOG_DEFAULT) { pri = ANDROID_LOG_VERBOSE; } char *tagName; // Presently HAVE_STRNDUP is never defined, so the second case is always taken // Darwin doesn't have strnup, everything else does #ifdef HAVE_STRNDUP tagName = strndup(filterExpression, tagNameLength); #else //a few extra bytes copied... tagName = strdup(filterExpression); tagName[tagNameLength] = '\0'; #endif /*HAVE_STRNDUP*/ FilterInfo *p_fi = filterinfo_new(tagName, pri); free(tagName); p_fi->p_next = p_format->filters; p_format->filters = p_fi; } return 0; error: return -1; } ~~~ 第17行取得日志記錄輸出過濾表達式中冒號的位置,接著第24行就調用函數filterCharToPri將冒號后面的字符轉換為android_LogPriority枚舉值。 **system/core/liblog/logprint.c** ~~~ /* * Note: also accepts 0-9 priorities * returns ANDROID_LOG_UNKNOWN if the character is unrecognized */ static android_LogPriority filterCharToPri (char c) { android_LogPriority pri; c = tolower(c); if (c >= '0' && c <= '9') { if (c >= ('0'+ANDROID_LOG_SILENT)) { pri = ANDROID_LOG_VERBOSE; } else { pri = (android_LogPriority)(c - '0'); } } else if (c == 'v') { pri = ANDROID_LOG_VERBOSE; } else if (c == 'd') { pri = ANDROID_LOG_DEBUG; } else if (c == 'i') { pri = ANDROID_LOG_INFO; } else if (c == 'w') { pri = ANDROID_LOG_WARN; } else if (c == 'e') { pri = ANDROID_LOG_ERROR; } else if (c == 'f') { pri = ANDROID_LOG_FATAL; } else if (c == 's') { pri = ANDROID_LOG_SILENT; } else if (c == '*') { pri = ANDROID_LOG_DEFAULT; } else { pri = ANDROID_LOG_UNKNOWN; } return pri; } ~~~ 從函數filterCharToPri的實現就可以看出,字符“v”、“d”、“i”、“w”、“e”、“f”、“s”和“*”對應的日志記錄優先級分別為ANDROID_LOG_VERBOSE、ANDROID_LOG_DEBUG、ANDROID_LOG_INFO、ANDROID_LOG_WARN、ANDROID_LOG_ERROR、ANDROID_LOG_FATAL、ANDROID_LOG_SILENT和ANDROID_LOG_DEFAULT。此外,字符“0”到“7”對應的日志記錄優先級分別為ANDROID_LOG_UNKNOWN、ANDROID_LOG_DEFAULT、ANDROID_LOG_VERBOSE、ANDROID_LOG_DEBUG、ANDROID_LOG_INFO、ANDROID_LOG_WARN、ANDROID_LOG_ERROR和ANDROID_LOG_FATAL,而字符“8”和“9”對應的日志記錄優先級均為ANDROID_LOG_VERBOSE。 回到函數android_log_addFilterRule中,如果日志記錄輸出過濾表達式中的日志記錄標簽值為“*”,即第31行的if語句為true,就表示要將它的日志記錄優先級設置為全局的日志記錄過濾優先級;否則,就為該日志記錄輸出過濾表達式創建一個日志記錄輸出過濾器,并且增加到Logcat工具的日志記錄輸出過濾器列表中,如第59行到第63行代碼所示。 設置好日志記錄輸出過濾器列表之后,回到函數main中,繼續往下執行。 **system/core/logcat/logcat.cpp** ~~~ dev = devices; while (dev) { dev->fd = open(dev->device, mode); ...... dev = dev->next; } ~~~ 函數第143行到148行的while循環依次調用函數open來打開保存在devices列表中的各個日志設備,并且將得到的文件描述符保存在對應的日志設備的成員變量fd中。 日志設備打開之后,函數main繼續往下執行。 **system/core/logcat/logcat.cpp** ~~~ if (needBinary) android::g_eventTagMap = android_openEventTagMap(EVENT_TAG_MAP_FILE); ~~~ 函數第149行判斷打開的日志設備的日志記錄格式是否包含二進制格式,即是否打開了日志設備/dev/log/events。如果是,即第149行的if語句為true,那么第150行就會調用函數android_openEventTagMap來解析目標設備上的/system/etc/event-log-tags文件,因為Logcat工具需要根據它的內容來解析類型為events的日志記錄。函數android_openEventTagMap的返回值是一個EventTagMap結構體,保存在全局變量g_eventTagMap中。 EVENT_TAG_MAP_FILE是一個宏定義,它的值為“/system/etc/event-log-tags”,如下所示。 **system/core/include/cutils/event_tag_map.h** `#define EVENT_TAG_MAP_FILE "/system/etc/event-log-tags"` 函數android_openEventTagMap的實現如下所示。 **system/core/liblog/event_tag_map.c** ~~~ /* * Open the map file and allocate a structure to manage it. * * We create a private mapping because we want to terminate the log tag * strings with '\0'. */ EventTagMap* android_openEventTagMap(const char* fileName) { EventTagMap* newTagMap; off_t end; int fd = -1; newTagMap = calloc(1, sizeof(EventTagMap)); if (newTagMap == NULL) return NULL; fd = open(fileName, O_RDONLY); if (fd < 0) { fprintf(stderr, "%s: unable to open map '%s': %s\n", OUT_TAG, fileName, strerror(errno)); goto fail; } end = lseek(fd, 0L, SEEK_END); (void) lseek(fd, 0L, SEEK_SET); if (end < 0) { fprintf(stderr, "%s: unable to seek map '%s'\n", OUT_TAG, fileName); goto fail; } newTagMap->mapAddr = mmap(NULL, end, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0); if (newTagMap->mapAddr == MAP_FAILED) { fprintf(stderr, "%s: mmap(%s) failed: %s\n", OUT_TAG, fileName, strerror(errno)); goto fail; } newTagMap->mapLen = end; if (processFile(newTagMap) != 0) goto fail; return newTagMap; fail: android_closeEventTagMap(newTagMap); if (fd >= 0) close(fd); return NULL; } ~~~ 第13行首先創建一個EventTagMap結構體對象,接著第17行打開文件/system/etc/event-log-tags,第24行得到該文件的大小,第31行將該文件的內容映射到內存中,最后第41行調用函數processFile來解析文件/system/etc/event-log-tags中的內容。 **system/core/liblog/event_tag_map.c** ~~~ /* * Crunch through the file, parsing the contents and creating a tag index. */ static int processFile(EventTagMap* map) { EventTag* tagArray = NULL; /* get a tag count */ map->numTags = countMapLines(map); if (map->numTags < 0) return -1; //printf("+++ found %d tags\n", map->numTags); /* allocate storage for the tag index array */ map->tagArray = calloc(1, sizeof(EventTag) * map->numTags); if (map->tagArray == NULL) return -1; /* parse the file, null-terminating tag strings */ if (parseMapLines(map) != 0) { fprintf(stderr, "%s: file parse failed\n", OUT_TAG); return -1; } /* sort the tags and check for duplicates */ if (sortTags(map) != 0) return -1; return 0; } ~~~ 第9行調用函數countMapLines來計算文件/system/etc/event-log-tags的行數,接著第16行根據這個行數來分配一個EventTag結構體數組,其中,每一行的內容都對應一個EventTag結構體。第21行調用函數parseMapLines來解析文件/system/etc/event-log-tags的內容。函數countMapLines主要是根據文件中的換行符‘\n’來計算行數,而且會忽略文件中的空白行和注釋行。函數parseMapLines對文件中的每一行內容進行解析,從而得到類型為events的日志記錄標簽描述表。下面我們就通過一個例子來說明函數parseMapLines的工作原理。 當目標設備上的文件/system/etc/event-log-tags包含以下一行內容時: `2722 battery_level (level|1|6),(voltage|1|1),(temperature|1|1)` 函數parseMapLines就會將字符串“2722”轉換為數字2722,并且將它保存在一個EventTag結構體的成員變量tagIndex中,然后再將字符“battery_level”在內存中的地址保存在同一個EventTag結構體的成員變量tagStr中,從而將日志記錄標簽號2722與描述字符串“battery_level”關聯起來。 函數parseMapLines執行完成之后,我們就得到了類型為events的日志記錄的日志標簽描述表,它保存在一個EventTagMap結構體內部的數組tagArray中。為了使后面能夠根據日志標簽號快速找到對應的日志標簽描述字符串,函數processFile的第27行調用函數sortTags對這個數組進行排序。 **system/core/liblog/event_tag_map.c** ~~~ /* * Sort the EventTag array so we can do fast lookups by tag index. * the sort we do a quick check for duplicate tag indices. * * Returns 0 on success. */ static int sortTags(EventTagMap* map) { int i; qsort(map->tagArray, map->numTags, sizeof(EventTag), compareEventTags); for (i = 1; i < map->numTags; i++) { if (map->tagArray[i].tagIndex == map->tagArray[i-1].tagIndex) { fprintf(stderr, "%s: duplicate tag entries (%d:%s and %d:%s)\n", OUT_TAG, map->tagArray[i].tagIndex, map->tagArray[i].tagStr, map->tagArray[i-1].tagIndex, map->tagArray[i-1].tagStr); return -1; } } return 0; } ~~~ 第11行調用快排函數qsort對該日志標簽描述數組進行排序,指定的比較函數為compareEventTags,它的實現如下所示。 **system/core/liblog/event_tag_map.c** ~~~ /* * Compare two EventTags. */ static int compareEventTags(const void* v1, const void* v2) { const EventTag* tag1 = (const EventTag*) v1; const EventTag* tag2 = (const EventTag*) v2; return tag1->tagIndex - tag2->tagIndex; } ~~~ 從這里就可以看出,日志標簽描述數組的元素是按照日志標簽號從小到大排列的。 回到函數sortTags中,第13行到第21行的for循環檢查前面獲得的日志標簽描述數組的合法性,即檢查它里面是否有重復的日志標簽號。如果有,就會返回錯誤碼-1,表示目標設備上的文件/system/etc/event-log-tags的內容有問題。 解析完成目標設備上的文件/system/etc/event-log-tags之后,回到函數main中,繼續往下執行。 **system/core/logcat/logcat.cpp** ~~~ android::readLogLines(devices); return 0; } ~~~ 第151行調用函數readLogLines開始讀取前面打開的日志設備的日志記錄。接下來,我們就分析這些日志記錄的讀取過程。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看