首页
论坛
课程
招聘
[系统相关] [源码分析] 玩转Android10源码开发定制(12)内核篇之logcat输出内核日志
2021-1-7 22:10 1154

[系统相关] [源码分析] 玩转Android10源码开发定制(12)内核篇之logcat输出内核日志

2021-1-7 22:10
1154

一、安卓内核日志系统介绍

Android系统中日志信息分为内核空间日志信息和用户空间日志信息,查看用户空间的log直接用logcat就可以了。比如如下命令:

1
adb logcat

或者使用如下命令:

1
adb shell logcat

如果需要查看内核控件日志信息,有以下几种方式查看。

  1. 读取/proc/kmsg,命令如下

    1
    adb shell cat /proc/kmsg

    读取/proc/kmsg属于消费型读取,读取之后再次读取不会显示已经读取过的日志信息。

  2. 读取/dev/kmsg,命令如下

    1
    adb shell cat /dev/kmsg

    读取/dev/kmsg会显示缓存区里面的所有日志信息。新写入的日志信息会不断累加到日志缓冲器中。

  3. 使用dmesg命令读取

    1
    adb shell dmesg

    dmesg命令读取一次只显示一部分日志,非阻塞执行,从他的源码中分析他是读取/dev/调用klogctl方式读取。demsg源码实现方式如下。

实现代码路径位于源码目录:

external/toybox/toys/lsb/dmesg.c

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
void dmesg_main(void)
{
   //...省略
  if (!(toys.optflags&FLAG_S)) {
        //...省略
    fd = open("/dev/kmsg", O_RDONLY|(O_NONBLOCK*!(toys.optflags&FLAG_w)));
    if (fd == -1) goto klogctl_mode;
    lseek(fd, 0, SEEK_DATA);
    for (;;) {
      if (-1==(len = read(fd, msg, sizeof(msg))) && errno==EPIPE) continue;
      // read() from kmsg always fails on a pre-3.5 kernel.
      if (len==-1 && errno==EINVAL) goto klogctl_mode;
      if (len<1) break;
      msg[len] = 0;
      format_message(msg, 1);
    }
    close(fd);
  } else {
    char *data, *to, *from, *end;
    int size;
 
klogctl_mode:
    // Figure out how much data we need, and fetch it.
    if (!(size = TT.size)) size = xklogctl(10, 0, 0);
    data = from = xmalloc(size+1);
    data[size = xklogctl(3+(toys.optflags&FLAG_c), data, size)] = 0;
 
    // Send each line to format_message.
    to = data + size;
    while (from < to) {
      if (!(end = memchr(from, '\n', to-from))) break;
      *end = 0;
      format_message(from, 0);
      from = end + 1;
    }
 
    if (CFG_TOYBOX_FREE) free(data);
  }
no_output:
  // Set the log level?
  if (toys.optflags & FLAG_n) xklogctl(8, 0, TT.level);
  // Clear the buffer?
  if (toys.optflags & (FLAG_C|FLAG_c)) xklogctl(5, 0, 0);
}

二、logcat输出内核日志方案设计

在本文中将采用init.rc中注册后台服务kernellogd。kernellogd不断读取/proc/kmsg信息,然后写到用户层的日志系统的方式达到目的。
流程图如下:

三、开发kernellogd程序

kernellogd有两种方式集成到系统,一种是直接将源码放到系统某一个文件夹,创建Android.mk文件,加入到系统编译链中;另种方式使用android studio工具开发,使用cmake或者ndk将源码编译成二进制可执行文件。目前我采用了使用android studio开发的方式,编译为arm64平台的可执行程序kernellogd。android studio开发流程此处不讨论,直接贴几句核心代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
//内核日志tag标识,可以用这个标识进行日志过滤
#define LOG_TAG  "kERNEL_LOG"
#define LOGkD(...)  __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
  FILE* fp=fopen("/proc/kmsg","r");
  if(fp==NULL)
  {
      LOGkE("fopen /proc/kmsg error");
      return -1;
  }
  while(true)
  {
      char buf[512]={0};
      char* ptrstr=fgets(buf,510,fp);
      if(ptrstr==NULL)
      {
          sleep(1);
      }else{
          LOGkD("%s",buf);
      }
  }
  fclose(fp);

四、配置kernellogd为系统服务

具体步骤如下:

  1. 内置kernellogd可执行二进制文件到系统

    如何内置可以参考之前的文章:

    玩转Android10源码开发定制(九)内置frida-gadget so文件和frida-server可执行文件到系统

  2. 配置kernellogd到init.rc

    安卓10系统源码中,init.rc文件路径为:

    system/core/rootdir/init.rc

    在init.rc文件末尾添加kernellogd后台服务的配置,内容如下:

    1
    2
    3
    4
    5
    6
    # ///ADD START
    # ///ADD END
    service kernellogd  /system/bin/kernellogd
       class main
       user root
       seclabel u:r:init:s0
  3. 修改init.te文件,配置init进程可以执行kernellogd命令的seandroid权限

    涉及修改的init.te文件路径分别如下:

    system/sepolicy/public/init.te

    system/sepolicy/prebuilts/api/29.0/public/init.te

    以上两个文件内容是一样的,需要保证同时被修改。修改如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    #...省略很多
    # 将下面的neverallow屏蔽掉
    # ///ADD START
    #neverallow init { file_type fs_type }:file execute_no_trans;
    # ///ADD END
    #...省略了很多
    # ///ADD START
    # ///ADD END
    allow init  system_file:file {execute_no_trans};

    allow init system_file:file {execute_no_trans}语句含义:允许所有属于init域的进程对所有属于system_file类型的文件具有执行权限。

五、测试验证

以上配置完成之后编译刷机。
在终端执行adb logcat之后可以看到内核日志和用户空间日志都输出来了。测试数据样本如下:

1
2
3
4
5
6
7
8
9
10
11
01-07 21:26:41.617  1393  2142 I ActivityManager: Killing 5097:com.android.keychain/1000 (adj 999): empty for 6733s
01-07 21:26:41.633   912   912 I Zygote  : Process 5097 exited due to signal 9 (Killed)
01-07 21:26:41.662   462   462 I hwservicemanager: getTransport: Cannot find entry android.hardware.graphics.mapper@3.0::IMapper/default in either framework or device manifest.
01-07 21:27:43.847   997  7946 E ResolverController: No valid NAT64 prefix (100, <unspecified>/0)
01-07 21:27:48.017   894   985 I ThermalEngine: Monitor : quiet_therm = 25, msm_therm = 25, ufs_therm = 25, battery_therm = 231,current_now = 209000
01-07 21:28:00.031  1932  1932 D KeyguardClockSwitch: Updating clock: 9:28
01-07 21:28:00.792   891   891 D kERNEL_LOG: <12>[ 6859.400113] healthd: battery l=100 v=4313 t=23.1 h=2 st=5 c=208 fc=2966000 chg=u
01-07 21:28:06.799   891   891 D kERNEL_LOG: <12>[ 6865.406981] healthd: battery l=100 v=4313 t=23.2 h=2 st=5 c=202 fc=2966000 chg=u
01-07 21:28:08.525   891   891 D kERNEL_LOG: <4>[ 6867.133081] helloworld: module license 'unspecified' taints kernel.
01-07 21:28:08.525   891   891 D kERNEL_LOG: <4>[ 6867.133092] Disabling lock debugging due to kernel taint
01-07 21:28:08.526   891   891 D kERNEL_LOG: <1>[ 6867.133659] Hello World!

[注意] 招人!base上海,课程运营、市场多个坑位等你投递!

最后于 2021-4-12 22:08 被蟑螂一号编辑 ,原因:
收藏
点赞0
打赏
分享
最新回复 (1)
雪    币: 350
活跃值: 活跃值 (68)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
mllaopang 活跃值 2021-1-8 07:44
2
0

谢谢分享 
游客
登录 | 注册 方可回帖
返回