1. 程式人生 > >Android7.0 klog機制(如何將android log列印到kernel log中)

Android7.0 klog機制(如何將android log列印到kernel log中)

    在分析Android7.0 init程序一文中提到,在init程序中是通過klog來輸出log資訊的,但是由於log的級別不同可能導致有些新增的log無法輸出來。在init .cpp的main函式中初始化klog。

    klog_init();          //初始化klog
    klog_set_level(KLOG_NOTICE_LEVEL);   //設定klog的級別為NOTICE
system/core/libcutils/klog.c
void klog_init(void) {
    if (klog_fd >= 0) return; /* Already initialized */      //klog_fd預設值為-1,如果大於等於1表示klog已經初始化過,返回

    klog_fd = open("/dev/kmsg", O_WRONLY | O_CLOEXEC);   //開啟/dev/kmsg檔案,獲得檔案描述符
    if (klog_fd >= 0) {                      //大於等於0表示,開啟成功,init程序的log都會輸出到kernel log中
        return;  
    }

    static const char* name = "/dev/__kmsg__"; 
    if (mknod(name, S_IFCHR | 0600, (1 << 8) | 11) == 0) {   //如果上面沒有開啟成功,建立/dev/_kmsg_檔案
        klog_fd = open(name, O_WRONLY | O_CLOEXEC);      //開啟檔案,獲取檔案描述符
        unlink(name);
    }
}
void klog_set_level(int level) {
    klog_level = level;                  //將設定的level賦值給klog_level
}

在init.cpp通過NOTICE函式輸出log資訊。但是INFO函式就輸不出log,下面我們看一下具體原因。

    NOTICE("init %s started!\n", is_first_stage ? "first stage" : "second stage");
在system/core/init/log.h標頭檔案中對NOTICE,INFO等函式進行定義
#define ERROR(x...)   init_klog_write(KLOG_ERROR_LEVEL, x)
#define WARNING(x...) init_klog_write(KLOG_WARNING_LEVEL, x)
#define NOTICE(x...)  init_klog_write(KLOG_NOTICE_LEVEL, x)
#define INFO(x...)    init_klog_write(KLOG_INFO_LEVEL, x)
#define DEBUG(x...)   init_klog_write(KLOG_DEBUG_LEVEL, x)
#define VERBOSE(x...) init_klog_write(KLOG_DEBUG_LEVEL, x)
呼叫NOTICE函式,也就調到了函式init_klog_wirte傳入的引數為NOTICE log level,如果呼叫別的函式就傳入對應的log level。還有傳入需要列印的資訊。

先看一下各個log level的值,程式碼定義位置system/core//include/cutils/klog.h

#define KLOG_ERROR_LEVEL   3
#define KLOG_WARNING_LEVEL 4
#define KLOG_NOTICE_LEVEL  5
#define KLOG_INFO_LEVEL    6
#define KLOG_DEBUG_LEVEL   7

繼續分析程式碼system/core/init/log.cpp

void init_klog_write(int level, const char* fmt, ...) {
    va_list ap;
    va_start(ap, fmt);
    init_klog_vwrite(level, fmt, ap);        //函式呼叫
    va_end(ap);
}
static void init_klog_vwrite(int level, const char* fmt, va_list ap) {
    static const char* tag = basename(getprogname());

    if (level > klog_get_level()) return;      //之前設定的level為NOTICE 5,現在level也為5相等為false,INFO level為6所以return

    // The kernel's printk buffer is only 1024 bytes.
    // TODO: should we automatically break up long lines into multiple lines?
    // Or we could log but with something like "..." at the end?
    char buf[1024];             //kernel 的printk buffer只有1024個位元組, 將長的單行log,變為多行,或者直接省略掉
    size_t prefix_size = snprintf(buf, sizeof(buf), "<%d>%s: ", level, tag);
    size_t msg_size = vsnprintf(buf + prefix_size, sizeof(buf) - prefix_size, fmt, ap);
    if (msg_size >= sizeof(buf) - prefix_size) {
        msg_size = snprintf(buf + prefix_size, sizeof(buf) - prefix_size,
                            "(%zu-byte message too long for printk)\n", msg_size);
    }

    iovec iov[1];
    iov[0].iov_base = buf;
    iov[0].iov_len = prefix_size + msg_size;

    klog_writev(level, iov, 1);       //傳送整理好的資訊
}
system/core/libcutils/klog.c
void klog_writev(int level, const struct iovec* iov, int iov_count) {
    if (level > klog_level) return;           //由於level與klog_level都為5,所以繼續執行
    if (klog_fd < 0) klog_init();               //前面已經初始化過klog_fd大於等於0
    if (klog_fd < 0) return;                     
    TEMP_FAILURE_RETRY(writev(klog_fd, iov, iov_count));   
}
//system/core/include/log/uio.h
extern int  readv( int  fd, struct iovec*  vecs, int  count );   //定義readv函式
extern int  writev( int  fd, const struct iovec*  vecs, int  count );  /定義writev函式
//system/core/liblog/uio.c
#include <log/uio.h>
LIBLOG_ABI_PUBLIC int writev(int fd, const struct iovec *vecs, int count)
{
    int   total = 0;

    for ( ; count > 0; count--, vecs++ ) {
        const char*  buf = vecs->iov_base;
        int          len = vecs->iov_len;

        while (len > 0) {
            int  ret = write( fd, buf, len ); //呼叫linux的write函式,fd為/dev/kmsg的檔案描述符,所以將log寫入到kmsg中即kernel log中。
            if (ret < 0) {
                if (total == 0)
                    total = -1;
                goto Exit;
            }
            if (ret == 0)
                goto Exit;

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

    之前做專案時遇到一個問題,在開機過程中kernel 與 init程序中的log都輸在kernel log中,而啟動zygote之後log是輸出在android中的,這樣就發生了init程序到framework這段時間無法精確計算,對分析開機流程總耗時帶來不小的困擾。所以我們就仿照klog自己定義TAG來輸出資訊,將android 中的log輸出到kernel中,這樣同一行log即在kernel log中有也在android log中有就可以精確計算時間了。

首先在system/core/include/cutils/klog.h中定義log level

	#define KLOG_PERFORMANCE_LEVEL 0              //根據之前分析,將level設定小一點,至少要小於5
	#define KLOG_BOOTINFO(tag,x...) klog_write(KLOG_PERFORMANCE_LEVEL, "<0>" tag ": " x)    //定義函式關係

定義完之後就可以在framework中使用了,

例如init程序啟動完zygote服務,會進入/frameowrk/base/cmds/app_process/app_main.cpp我們可以在這裡使用

#include <cutils/klog.h>              //先include對應的標頭檔案
#define BOOTINFO(x...) KLOG_BOOTINFO("bootinfo", x)       //定義對應的函式關係
 BOOTINFO("Entered app_main.cpp main() \n");    //就可以通過BOOTINFO函式將log輸出到kernel log中
不過將編譯好的檔案替換到手機中仍然無法輸出對應log資訊,經過分析發現是有與selinux許可權,輸出log存在selinux安全問題。

為了除錯我們本地只好將selinux關閉了,程式碼還是在init .cpp 中我們之前將init程序時有過提到。

static void selinux_initialize(bool in_kernel_domain) {
    Timer t;

    //..............
    if (in_kernel_domain) {
       //.........

        bool kernel_enforcing = (security_getenforce() == 1);
        bool is_enforcing = selinux_is_enforcing();
        if (kernel_enforcing != is_enforcing) {
            if (security_setenforce(is_enforcing)) {     //可以直接將is_enforcing改為false,就將selinux關閉了
                ERROR("security_setenforce(%s) failed: %s\n",
                      is_enforcing ? "true" : "false", strerror(errno));
                security_failure();
            }
        }

        //.............
    } else {
        selinux_init_all_handles();
    }
}

相關推薦

Android7.0 klog機制如何android log列印kernel log

    在分析Android7.0 init程序一文中提到,在init程序中是通過klog來輸出log資訊的,但是由於log的級別不同可能導致有些新增的log無法輸出來。在init .cpp的main函式中初始化klog。 klog_init();

Python備份檔案、檔案版本的學生管理系統如何實現資料儲存在txt檔案

完成檔案的備份案例答:# 根據輸入的檔名進行復制新的檔名old_file_name = input("請輸入要複製的檔名:")new_file_name = old_file_name[:old_file_name.rfind(".")] + "_copyfile" + ol

Eclipse如何快速檢視jar包 的class原始碼jd-gui整合在Eclipse

1、開啟eclispe,在eclipse的選單欄上選擇Help->Install New Software 2,點選Install New Softeware,進入如下視窗。3,在對話方塊中點選add按鈕,然後在彈出的對話方塊中輸入名字和下載連結(因為它在下載,時間可能

Android7.0適配:關於Android 7.0 在應用間共享檔案的適配

前言 隨安卓版越來越高,對隱私保護力度亦越來越大。從Android6.0動態許可權控制(Runtime Permissions)到Android7.0私有目錄限訪、StrictMode API政策等的更改,這些更改在為使用者帶來更加安全的作業系統的同時也為開發者

Android訊息處理機制、Hanlder機制Handler、Looper、MessageQueue和Message

·前言長久以來,我使用Hanlder僅限於它的基本使用:post 和 sendMessage而對於Hanlder的原理和訊息處理機制並不清楚。找了兩篇比較深入的部落格:學習了一下,又對照了原始碼,小有收穫,俗話說“好記性不如爛筆頭”,所以做一下筆記,總結一下,方便以後回顧。·

Android7.0 Watchdog機制

    對手機系統而言,因為肩負著接聽電話和接收簡訊的“重任”,所以被寄予7x24小 時正常工作的希望。但是作為一個在嵌入式裝置上執行的作業系統,Android執行中必須面對各種軟硬體干擾,從最簡單的程式碼出現死鎖或者被阻塞,到記憶體越界導致的記憶體破壞,或者由於硬體問題導

Redis系列--內存淘汰機制含單機版內存優化建議

del dbn amp 一段 最簡 nal imp 同學 博客 https://blog.csdn.net/Jack__Frost/article/details/72478400?locationNum=13&fps=1 每臺redis的服務器的內存都是有限的,而

轉換流位元組流轉換為字元流

1、將位元組流轉換為字元流 (1)名稱是前面四種抽象類的組合 (2)功能都是將位元組流轉換成字元流,但是沒有將字元流轉換成位元組流的類,因為,已經獲得了字元流,根本沒有必要轉換成位元組流 (3)InputStreamReader是將InputStream中的位元組轉換成字元

Redis 哨兵節點之間相互自動發現機制自動重寫哨兵節點的配置檔案

Redis的哨兵機制中,如果是多哨兵模式,哨兵節點之間也是可以相互感知的,各種搜尋之後出來的是千篇一律的一個基礎配置檔案,在配置當前哨兵節點的配置檔案中,並沒有配置其他哨兵節點的任何資訊。 如下是一個哨兵節點的配置資訊,可以看到,哨兵與哨兵之間沒有任何配置,死活想不明白,哨兵之間是如何自動識別的。 #se

動手造輪子,用DownLoadManage封裝一個App的更新元件相容android 6、7、8

前言 android app的更新是我們在平時開發的時候常常需要遇到的問題。通常的情況是我們用第三方的網路載入庫去進行地址的下載,然後進行更新。例如okHttp、volley等,都具備了下載的功能。 但是我們在用這些第三方庫進行下載的時候可能需要做很多之外的處理,比如更新的時候處理進度。寫一個notifi

mybatis 排序指定的排在後/前面

public List<SuitEvidenceVo> selectWithFileByCaseId(Long caseId, List<Long> userIdList, Page evidencePge) { SuitEvi

Python之Excel圖片處理excel chart另存為圖片

Python之Excel chart另存為圖片 大家好,好久沒有更新部落格了,這一段時間有點忙,公司接觸到了大量的excel檔案處理,現將自己在工作中積累的經驗分享大家,供大家參考學習。 業務說明:這段時間我主要做的工作有: 解析excel,將目

Leetcode 146:LRU快取機制超詳細的解法!!!

運用你所掌握的資料結構,設計和實現一個 LRU (最近最少使用) 快取機制。它應該支援以下操作: 獲取資料 get 和 寫入資料 put 。 獲取資料 get(key) - 如果金鑰 (key) 存在於快取中,則獲取金鑰的值(總是正數),否則返回 -1。 寫入資料 put(key,

mybatis的快取機制一級快取二級快取和重新整理快取和mybatis整合ehcache

1      查詢快取 1.1  什麼是查詢快取 mybatis提供查詢快取,用於減輕資料壓力,提高資料庫效能。 mybaits提供一級快取,和二級快取。   一級快取是SqlSession級別的快取。在操作資料庫時需要構造 sqlSession物件,在物件中有一個(記憶

java 使用圖片代理程式,解決網站圖片防盜鏈機制測試百度,QQ空間有效

業務場景 1、頁面引用其他站點圖片的時候,由於某些站點存在圖片的防盜鏈機制,所以在引用圖片的時候,返回的一張預設的圖片,而不是原圖片。 2、使用java完成一個代理程式,代理所有的存在防盜鏈機制的圖片請求,繞過防盜鏈機制,返回原圖片 解決思路 1、代

nodejs之事件處理機制丟擲事件、監聽事件

程式執行到一定階段的時候會發出一個訊息,對這個訊息進行監聽,作出響應;==========================================***************建立伺服器var http = require('http'); var fs = requ

Axure RP 8 7.0 註冊碼試了可以用,留備

僅供個人學習交流使用,建議獲取正版Axure授權註冊碼。 Licensee:University of Science and Technology of China (CLASSROOM) Key:DTXRAnPn1P65Rt0xB4eTQ+4bF5IUF0gu

java事件監聽機制觀察者設計模式的實際運用

package cn.yang.test.controller; /**java的事件監聽機制和觀察者設計模式 * Created by Dev_yang on 2016/3/1. */ publ

JVM:類載入機制類載入過程和類載入器

一、為什麼要使用類載入器?Java語言裡,類載入都是在程式執行期間完成的,這種策略雖然會令類載入時稍微增加一些效能開銷,但是會給java應用程式提供高度的靈活性。例如:1.編寫一個面向介面的應用程式,可能等到執行時再指定其實現的子類;2.使用者可以自定義一個類載入器,讓程式在