Linux Kernel Crash Dump

需求:
Linux发生panic之后,如果/proc/sys/kernel/panic没有值的话,会一直停留在panic的界面。如果有值,则会自动重启。在一般的客户版本中,一般都会设置成自动重启,但这样的话就无法查看重启的原因了。
为了调查问题,经常需要保留现场。因此,希望在重启之前把相应的信息以文件的形式保存下来。但是如果是文件系统发生panic,这个方案明显不行。我们可以将现场的信息暂时保留在内存里面,重启的话再从内存中将信息以log的形式保存下来。

本文主要讲述在linux3.0的版本上实现这个方案


所要解决的问题

按照上述的方案,我们需要解决以下几个问题:
(1)、panic的信息保存在内存哪个位置?什么时候申请,怎么申请大块内存区域?
(2)、怎么获取到发生panic的信息?
(3)、发生panic后重启的代码位置在哪?
(4)、重启之后判断哪个标记是否需要保存log?
(5)、应用层怎么读取信息?

以下用例的实现使用xxx_lkcd(xxx project, Linux Kernel Crash Dump)来表示。

解答

(1)、这里涉及到内存的layout,要明确信息保存在哪个地址,保存的大小,地址要唯一,并且这部分的内存区域要reserve,不能被其他使用。
保存的地址为:
#define XXX_LKCD_RESERVE_AREA_ADDRESS 0xb9c00000,内存区域大小为3M。
为了要保证这3M的内存区域不被使用,要在kernel一起来就要向内核进行申请保留,因此在DT_MACHINE_START().reserve对应的函数去申请内存区域。注:

  • reserve 3M的地址有讲究,不能reserve到DDR最后的地址处。因为在uboot中,会有个relocate的动作,将会使用到高端地址,有可能会被覆盖掉;
  • 使用dts的kernel中支持往reserve_memory设备节点直接reserve memory,无需再去代码中reserve。也就是说,不一定需要再调用xxx_lkcd_reserve()函数。

(2)、对于驱动工程师而言,获取panic信息主要就是kernel log,也就是printk的log。
内核提供了一个syslog_print_all()的函数,通过该接口应用层可以获取kernel log的信息。函数定义如下:<kernel_dir>/kernel/printk/printk.c

1
2
3
4
5
6
7
/*
* Dump the kernel log contents to user space, pointed by buf
* @buf: where to save the dump log_buffer
* @size: dump log_buffer size
* @clear: clear the log_buffer after dump
*/
static int syslog_print_all(char __user *buf, int size, bool clear)

怎么办呢?这个接口是供应用层使用的,内核怎么用?
其实很简单,完全拷贝syslog_print_all()函数的内容为dump_logbuffer(),将其buf保存在kernel就可以,不用传到应用层即可。

(3)、发生panic,会去调用DT_MACHINE_START()所指定的.restart对应的函数。
因此,我们可以在kernel panic发生之后,给.restart对应的函数传递特定的参数,根据参数判断是正常重启还是由于kernel panic重启。
如果是由于kernel panic重启的话,可以做一些保护现场的操作,也就是保存信息到指定的内存区域中去。

(4)、重启之后,判断是否需要保存log是通过申请到的3M内存区域的前4个字节(magic)字段,如果这个字段为XXX_LKCD_MAGIC,那么驱动中需要创建/proc接口供应用程序读写并保存log到文件系统中去。

(5)、如第(4)步所说的,应用程序通过/proc接口来获取数据并以文件的形式保存起来。


详细流程图

流程图


代码实现

Kernel_SrcDir/drivers/目录下创建xxx_lkcd/的目录文件,在里面添加如下文件:

  • Makefile
  • Kconfig
  • xxx_lkcd.c:实现大块内存区域的申请以及重启之前的信息保存动作;
  • xxx_lkcd.h:定义内存区域大小、文件名等内容;
  • xxx_lkcd_proc.c: 创建/proc/xxx_lkcd/接口供应用层读取数据

(1)、Makefile

1
2
obj-$(CONFIG_XXX_LKCD) += xxx_lkcd.o
obj-$(CONFIG_XXX_LKCD) += xxx_lkcd_proc.o

(2)、Kconfig

1
2
3
4
5
6
7
8
9
10
11
12
13
#
# ARM linux crash dump configuration
#

menu "xxx linux kernel crash dump"

config XXX_LKCD
bool "Enable xxx linux kernel crash dump"
default n
depends on PROC_FS && ARM
help
Enable xxx linux kernel crash dump
endmenu

(3)、xxx_lkcd.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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
#include "xxx_lkcd.h"

struct xxx_lkcd_mem_info xxx_lkcd_mem = {
.psize = sizeof(struct xxx_lkcd_area_info),
};

/**
* xxx_lkcd_reserve() - 一开机申请大块内存区域
*/
void __init xxx_lkcd_reserve(void)
{
int ret = -1;
phys_addr_t alignment = SZ_1M;

printk("3333333333333333333333\n");

// 检查申请的内存区域不能超过 SIZE_MAX
BUILD_BUG_ON(SIZE_MAX < FIELD_SIZEOF(struct xxx_lkcd_area_info, logger_kernel));
BUILD_BUG_ON(SIZE_MAX < FIELD_SIZEOF(struct xxx_lkcd_area_info, logger_main));
BUILD_BUG_ON(SZ_3M < sizeof(struct xxx_lkcd_area_info));

xxx_lkcd_mem.psize = ALIGN(xxx_lkcd_mem.psize, alignment);

BUILD_BUG_ON(XXX_LKCD_RESERVE_AREA_ADDRESS != ALIGN(XXX_LKCD_RESERVE_AREA_ADDRESS, PAGE_SIZE));

xxx_lkcd_mem.pstart = XXX_LKCD_RESERVE_AREA_ADDRESS;

// 申请保留大的内存空间供存储发生 panic 后的 log
if (XXX_LKCD_RESERVE_AREA_ADDRESS && xxx_lkcd_mem.psize){
if (0 == memblock_is_region_reserved(xxx_lkcd_mem.pstart, xxx_lkcd_mem.psize))
printk("[xxx_lkcd] store log area is free to use\n");
else{
printk("[xxx_lkcd] store log area is reserved by others\n");
return;
}

ret = memblock_reserve(xxx_lkcd_mem.pstart, xxx_lkcd_mem.psize);
if (ret<0){
printk("[xxx_lkcd] can't memblock_reserve at %08lx, ret = %d\n", xxx_lkcd_mem.pstart, ret);
return;
} else{
printk("[xxx_lkcd] memblock_reserve at %08lx\n", xxx_lkcd_mem.pstart);
}

ret = memblock_free(xxx_lkcd_mem.pstart, xxx_lkcd_mem.psize);
if (ret<0){
printk("[xxx_lkcd] memblock_free failed, ret = %d\n",ret);
return;
}

ret = memblock_remove(xxx_lkcd_mem.pstart, xxx_lkcd_mem.psize);
if (ret<0){
printk("[xxx_lkcd] memblock_remove failed, ret = %d\n",ret);
return;
}
}else{
printk("[xxx_lkcd] store log area is NULL\n");
return;
}

printk("[xxx_lkcd] reserved %ldM at %08lx\n", xxx_lkcd_mem.psize/SZ_1M, xxx_lkcd_mem.pstart);

}

/**
* xxx_lkcd_save() - 重启之前保存现场信息
*/
void xxx_lkcd_save(void)
{
struct timespec cur;
unsigned long i = 0;
printk("[xxx_lkcd] xxx_lkcd_save\n");

struct xxx_lkcd_area_info *local_xxx_lkcd_area;

if (xxx_lkcd_mem.vstart) {
local_xxx_lkcd_area = (struct xxx_lkcd_area_info *)xxx_lkcd_mem.vstart;
if (XXX_LKCD_MAGIC != local_xxx_lkcd_area->magic) {
printk("[xxx_lkcd] not previous data, start to dump data !!\n");
memset(local_xxx_lkcd_area, 0, sizeof(*local_xxx_lkcd_area));

getnstimeofday(&cur);
local_xxx_lkcd_area->time = cur.tv_sec;

// 保存 panic 发生时的 kernel log 和 android log
local_xxx_lkcd_area->logger_kernel_len = dump_logbuffer(local_xxx_lkcd_area->logger_kernel, sizeof(local_xxx_lkcd_area->logger_kernel), 0);
//local_xxx_lkcd_area->logger_main_len = dump_android_log("log_main", local_xxx_lkcd_area->logger_main, sizeof(local_xxx_lkcd_area->logger_main));

// 设置 panic 的标记
local_xxx_lkcd_area->magic = XXX_LKCD_MAGIC;

} else {
printk("[xxx_lkcd] there is old data,not dump\n");
}
printk("[xxx_lkcd] dump logger_kernel_len = %d\n", local_xxx_lkcd_area->logger_kernel_len);
printk("[xxx_lkcd] dump logger_main_log_len = %d\n",local_xxx_lkcd_area->logger_main_len);
}
}

(4)、xxx_lkcd.h

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
45
46
47
48
#ifndef __XXX_LKCD_H_
#define __XXX_LKCD_H_

#include <asm/mach/time.h>
#include <asm/setup.h>
#include <asm/mach/arch.h>
#include <linux/sizes.h>
#include <asm/sizes.h>
#include <asm/mach-types.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/kmsg_dump.h>
#include <linux/memblock.h>
#include <asm/setup.h>
#include <asm/io.h>
#include <linux/ioport.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>


#define XXX_LKCD_RESERVE_AREA_ADDRESS 0xb9c00000 // log 存放的内存地址
#define XXX_LKCD_MAGIC 0xaaaaaaaa // 标记是否发生 panic
#define XXX_LKCD_PROC_PATH_NAME "xxx_lkcd"
#define XXX_LKCD_TIME_NAME "time"
#define XXX_LKCD_CONTROL "control" // 控制接口
#define XXX_LKCD_LOGGER_KERNEL_NAME "logger_kernel" // 存放 kernel 的log
#define XXX_LKCD_LOGGER_MAIN_NAME "logger_main" // 存放 android 的log

extern size_t dump_android_log(char *, char *, size_t);

// 设置 3M 空间详细的分布以存储log
struct xxx_lkcd_area_info {
u32 magic;
time_t time;
size_t logger_kernel_len;
char logger_kernel[256*1024];
size_t logger_main_len;
char logger_main[2*1024*1024];
};

// 存储存储空间的大小以及制定位置
struct xxx_lkcd_mem_info {
unsigned long pstart;
unsigned long psize;
unsigned long vstart;
};

#endif

(5)、xxx_lkcd_proc.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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
#include "xxx_lkcd.h"
#include <asm/uaccess.h>
#include <asm/ioctl.h>

#define XXX_LKCD_IOC_MAGIC 'x'
#define IOC_XXX_LKCD_PANIC _IOW(XXX_LKCD_IOC_MAGIC, 0, __u8)
#define IOC_XXX_LKCD_CLEAR_DATA _IOW(XXX_LKCD_IOC_MAGIC, 1, __u8)
#define IOC_XXX_LKCD_FILL_DATA _IOW(XXX_LKCD_IOC_MAGIC, 2, __u8)

extern struct xxx_lkcd_mem_info xxx_lkcd_mem;

struct xxx_lkcd_area_info *xxx_lkcd_area;

int xxx_lkcd_control_proc_open(struct inode * inode, struct file * filp)
{
return 0;
}

// /proc/xxx_lkcd/control 控制接口,可以往里面做相应的控制
static int xxx_lkcd_control_proc_write(struct file * file, const char __user * buf, size_t count, loff_t *f_pos)
{
char cmd;

if (get_user(cmd, buf)){
return -EFAULT;
}
printk("[xxx_lkcd_proc] cmd = %d\n", cmd);

if (cmd == 'h'){
printk("[xxx_lkcd_proc] show this help\n");
printk("echo 'p' > /proc/xxx_lkcd/control : create panic manually\n");
printk("echo 'h' > /proc/xxx_lkcd/control : show this help\n");
}
if (cmd == 'p'){
printk("[xxx_lkcd_proc] creat kernel panic manually\n");
panic("[xxx_lkcd_proc] creat kernel panic manually in xxx_lkcd_proc module\n");
}

return count;
}

long xxx_lkcd_control_proc_ioctl(struct file *filp, unsigned int cmd, unsigned long arg)
{
int retval = 0;

printk("[xxx_lkcd_proc] this is in %s, cmd = %d, arg = %d\n", __func__, cmd, arg);

switch(cmd) {
case IOC_XXX_LKCD_PANIC:
break;
default:
break;
}
return retval;
}

static int xxx_lkcd_proc_read(struct seq_file *seq, void *vo)
{
//printk("[xxx_lkcd_proc] xxx_lkcd_proc_read, xxx_lkcd_area->magic = 0x%x\n", xxx_lkcd_area->magic);
if (XXX_LKCD_MAGIC == xxx_lkcd_area->magic) {
char *filename = (char *)seq->private;
//printk("[xxx_lkcd_proc] read filename = %s\n", filename);

if (!strcmp(XXX_LKCD_LOGGER_KERNEL_NAME, filename)) {
char *logger_kernel = xxx_lkcd_area->logger_kernel;
int logger_kernel_size = sizeof(xxx_lkcd_area->logger_kernel);
while ((logger_kernel < (xxx_lkcd_area->logger_kernel + logger_kernel_size)) && *logger_kernel) {
seq_putc(seq, *logger_kernel++);
}
}

if (!strcmp(XXX_LKCD_LOGGER_MAIN_NAME, filename)) {
char *logger_main = xxx_lkcd_area->logger_main;
int logger_main_size = sizeof(xxx_lkcd_area->logger_main);
while ((logger_main < (xxx_lkcd_area->logger_main + logger_main_size)) && *logger_main) {
seq_putc(seq, *logger_main++);
}
}
}

return 0;
}

int xxx_lkcd_proc_open(struct inode * inode, struct file * filp)
{
//printk("[xxx_lkcd_proc] xxx_lkcd_proc_open, d_iname = %s\n", filp->f_path.dentry->d_iname);
return single_open(filp, xxx_lkcd_proc_read, filp->f_path.dentry->d_iname);
}

// 控制接口操作集合
static const struct file_operations xxx_lkcd_control_proc_fops = {
.open = xxx_lkcd_control_proc_open,
.write = xxx_lkcd_control_proc_write,
.unlocked_ioctl = xxx_lkcd_control_proc_ioctl,
};

// 读取接口操作集合
static const struct file_operations xxx_lkcd_proc_fops = {
.open = xxx_lkcd_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};

// 创建与应用层通信的 /proc/xxx_lkcd 接口
static int xxx_lkcd_proc_init(void)
{
if (xxx_lkcd_mem.pstart && xxx_lkcd_mem.psize) {
if (!request_mem_region(xxx_lkcd_mem.pstart, xxx_lkcd_mem.psize, "xxx_lkcd_region_area" )) {
pr_err("[xxx_lkcd_proc] request mem region failed, start=0x%08lx,size=0x%08lx\n", xxx_lkcd_mem.pstart, xxx_lkcd_mem.psize);
return -1;
}
xxx_lkcd_mem.vstart = (unsigned long)ioremap_nocache(xxx_lkcd_mem.pstart, xxx_lkcd_mem.psize);
if (NULL == xxx_lkcd_mem.vstart) {
pr_err("[xxx_lkcd_proc] get xxx lkcd virtual start address error\n");
} else {
pr_err("[xxx_lkcd_proc] get xxx lkcd virtual start address %08lx ok\n", xxx_lkcd_mem.vstart);
}
}

// 获取到该内存区域的首地址
xxx_lkcd_area = (struct linux_crash_dump *)xxx_lkcd_mem.vstart;

if (NULL != xxx_lkcd_area) {
printk("[xxx_lkcd_proc] xxx_lkcd_area->magic = 0x%x\n", xxx_lkcd_area->magic);
} else {
printk("[xxx_lkcd_proc] xxx_lkcd_area is NULL !!!!!!!\n");
}

// 创建 /proc/xxx_lkcd/control 的控制接口
printk("[xxx_lkcd_proc] create /proc/xxx_lkcd/control interface\n");
if( !proc_mkdir(XXX_LKCD_PROC_PATH_NAME, NULL) ||
!proc_create(XXX_LKCD_PROC_PATH_NAME"/"XXX_LKCD_CONTROL, S_IRUSR, NULL, &xxx_lkcd_control_proc_fops))
{
printk("[xxx_lkcd_proc] create xxx lkcd control proc interface Error\n");
release_mem_region(xxx_lkcd_mem.pstart, xxx_lkcd_mem.psize);
return -ENOMEM;
}

// 如果有保存 kernel panic 的log,那么此时就创建 proc 的节点供应用程序读取
if ((NULL != xxx_lkcd_area) && (XXX_LKCD_MAGIC == xxx_lkcd_area->magic)) {
printk("[xxx_lkcd_proc] create /proc/xxx_lkcd/normal interface\n");
if( !proc_create(XXX_LKCD_PROC_PATH_NAME"/"XXX_LKCD_TIME_NAME, S_IRUSR, NULL, &xxx_lkcd_proc_fops) ||
!proc_create(XXX_LKCD_PROC_PATH_NAME"/"XXX_LKCD_LOGGER_KERNEL_NAME, S_IRUSR, NULL, &xxx_lkcd_proc_fops) ||
!proc_create(XXX_LKCD_PROC_PATH_NAME"/"XXX_LKCD_LOGGER_MAIN_NAME, S_IRUSR, NULL, &xxx_lkcd_proc_fops) )
{
printk("[xxx_lkcd_proc] xxx lkcd normal proc interface Error\n");
release_mem_region(xxx_lkcd_mem.pstart, xxx_lkcd_mem.psize);
return -ENOMEM;
}
}
return 0;
}


module_init(xxx_lkcd_proc_init);


详细步骤

(1)、添加DT_MACHINE_START()指定的.reserve.restart对应的函数,此处以i.MX6平台为例。
arch/arm/mach-imx/board-xxx.c文件的末尾都会定义如下的结构,用于与dts中指定的compatible进行匹配,匹配完毕之后就会执行其相应的内容:

1
2
3
4
5
6
7
8
9
10
DT_MACHINE_START(IMX6Q, "Freescale i.MX6 Quad/DualLite (Device Tree)")
.smp = smp_ops(imx_smp_ops),
.map_io = imx6q_map_io,
.init_irq = imx6q_init_irq,
.init_machine = imx6q_init_machine,
.init_late = imx6q_init_late,
.reserve = mx6q_reserve,
.restart = imx6q_restart,
.dt_compat = imx6q_dt_compat,
MACHINE_END

  • 在机器启动的时候会执行.init_machine对应的函数imx6q_init_machine().reserve对应的函数mx6q_reserve()
    因此,我们可以在mx6q_reserve()中事先去申请一块大块的内存用于保存log,这块内存在一开机就申请,不让其他程序使用。也就是说在mx6q_reserve()中调用xxx_lkcd_reserve()函数。

  • 在机器重启之前,会去执行.restart对应的函数imx6q_restart()
    因此,我们可以在kernel panic发生之后,给imx6q_restart()函数传递特定的参数,根据参数判断是正常重启还是由于kernel panic重启,如果是由于kernel panic 重启的话,可以做一些保护现场的操作。
    因此,在imx6q_restart()中调用xxx_lkcd_save()函数即可。

(2)、xxx_lkcd_reserve()函数用来申请3M的内存区域,起始地址为xxx_lkcd_mem.pstart,这是真正的内存物理地址,大小为 xxx_lkcd_mem.psize。在执行xxx_lkcd_proc_init()内容的时候会去获取到虚拟地址,如下:

1
xxx_lkcd_mem.vstart = (unsigned long)ioremap_nocache(xxx_lkcd_mem.pstart, xxx_lkcd_mem.psize);

因此我们就可以将现场的信息写入到虚拟地址xxx_lkcd_mem.vstart指定的区域中去。可以通过#cat /proc/iomem查看地址分配的情况。

(3)、xxx_lkcd_save()是用来保护现场信息的,将其内容写入到struct xxx_lkcd_area_info中去,总共3M大小。

1
2
3
4
5
6
7
8
struct xxx_lkcd_area_info {
u32 magic;
time_t time;
size_t logger_kernel_len;
char logger_kernel[256*1024];
size_t logger_main_len;
char logger_main[2*1024*1024];
};

(4)、我们会固定的去加载xxx_lkcd_proc驱动,也就是:module_init(xxx_lkcd_proc_init);
在这个驱动中会去固定创建一个/proc/xxx_lkcd/control控制接口,可以通过该接口模拟发生panic或者控制3M的内存区域。之后根据xxx_lkcd_area->magic字段的值来判断是否有panic发生,如果有panic发生,那么就创建通常的读写接口给应用程序保存现场信息。比如说/proc/xxx_lkcd/logger_kernel来保存kernel 的log信息。

(5)、驱动中提供给应用程序读取的接口为:xxx_lkcd_proc_read()


应用层测试程序

应用层首先会创建文件用于保存从/proc/xxx_lkcd/logger_kernel接口读取到的数据,也就是XXX_LKCD_MakeDir()
XXX_LKCD_SaveFile()用于从/proc接口读取数据并写入到创建的文件中。xxx_lkcd_test.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
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
#include <sys/mount.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <sys/stat.h>
#include <string.h>
#include <fcntl.h>
#include <stdarg.h>
#include <sys/reboot.h>
#include <errno.h>
#include <dirent.h>
#include <string.h>
#include <stdio.h>


#define NEED_MOUNT 0

// 读取 linux kernel crash dump 的文件
#define XXX_LKCD_PROC_PATH "/proc/xxx_lkcd"
#define XXX_LKCD_LOGGER_KERNEL "/proc/xxx_lkcd/logger_kernel"
#define XXX_LKCD_LOGGER_MAIN "/proc/xxx_lkcd/logger_main"

// 保存从 /proc/xxx_lkcd 中读到数据的路径
#define XXX_LKCD_STOREAGE_DIR_1 "/var"
#define XXX_LKCD_STOREAGE_DIR_2 "/var/xxx_lkcd"
#define XXX_LKCD_STOREAGE_DEV "/dev/block/mmcblk0p3"

#define XXX_LKCD_LOGGER_KERNEL_NAME "logger_kernel"
#define XXX_LKCD_LOGGER_MAIN_NAME "logger_main"

#define XXX_LKCD_FILE_MODE 0777
#define XXX_LKCD_DUMP_KERNEL_SIZE (256*1024)
#define XXX_LKCD_DUMP_MAIN_SIZE (2*1024*1024)

int XXX_LKCD_SaveOneFile(char *src_path, char *dest_path, int size)
{
int src_fd;
int dest_fd;
char *buffer;
int ret = 0;
buffer = (char *)malloc(size * sizeof(char));

src_fd = open(src_path, O_RDONLY);
if (-1 == src_fd) {
printf("[xxx_lkcd_test] open src file: %s failed\n", src_path);
return -1;
}
else{
//printf("[xxx_lkcd_test] open file: %s ok\n", src_path);
}

dest_fd = open(dest_path, O_CREAT|O_RDWR, XXX_LKCD_FILE_MODE);
if (-1 == dest_fd) {
printf("[xxx_lkcd_test] open dest file: %s failed\n", dest_path);
return -1;
}
else{
//printf("[xxx_lkcd_test] open file: %s ok\n", dest_path);
}

ret = read(src_fd, buffer, size);
if (-1 == ret) {
printf("[xxx_lkcd_test] read src failure\n");
return ret;
}

ret = write(dest_fd, buffer, ret);
if (-1 == ret) {
printf("[xxx_lkcd_test] write dest failure\n");
return ret;
}
close(src_fd);
close(dest_fd);
return 0;
}


// 保存log
int XXX_LKCD_SaveFile(void)
{
char logger_path[256];
int ret = 0;

snprintf(logger_path, 255, "%s%s%s", XXX_LKCD_STOREAGE_DIR_2, "/", XXX_LKCD_LOGGER_MAIN_NAME);
//printf("[xxx_lkcd_test] logger_path = %s\n", logger_path);
ret = XXX_LKCD_SaveOneFile(XXX_LKCD_LOGGER_MAIN, logger_path, XXX_LKCD_DUMP_MAIN_SIZE);

snprintf(logger_path, 255, "%s%s%s", XXX_LKCD_STOREAGE_DIR_2, "/", XXX_LKCD_LOGGER_KERNEL_NAME);
//printf("[xxx_lkcd_test] logger_path = %s\n", logger_path);
ret = XXX_LKCD_SaveOneFile(XXX_LKCD_LOGGER_KERNEL, logger_path, XXX_LKCD_DUMP_KERNEL_SIZE);

return (-1 == ret)?-1:0;
}

// 创建保存 log 的目录
int XXX_LKCD_MakeDir(void)
{
int ret = 0;

ret = mkdir(XXX_LKCD_STOREAGE_DIR_1, XXX_LKCD_FILE_MODE);

if ((-1 != ret) || (-1 == ret)) {
//printf("[xxx_lkcd_test] mkdir %s sucess\n", XXX_LKCD_STOREAGE_DIR_1);

#if NEED_MOUNT
if (0 != mount(XXX_LKCD_STOREAGE_DEV, XXX_LKCD_STOREAGE_DIR_1, "ext4", MS_NODEV|MS_NOSUID, NULL)) {
printf("[xxx_lkcd_test] mount %s failed\n", XXX_LKCD_STOREAGE_DEV);
return -1;
}
//printf("[xxx_lkcd_test] mount %s succeed\n", XXX_LKCD_STOREAGE_DEV);
#endif

ret = mkdir(XXX_LKCD_STOREAGE_DIR_2, XXX_LKCD_FILE_MODE);
if ((-1 != ret) || (-1 == ret)) {
//printf("[xxx_lkcd_test] mkdir %s sucess\n", XXX_LKCD_STOREAGE_DIR_2);
}
else {
printf("[xxx_lkcd_test] main() mkdir %s failed\n", XXX_LKCD_STOREAGE_DIR_2);
}
}
else {
printf("[xxx_lkcd_test] main() mkdir %s failed\n", XXX_LKCD_STOREAGE_DIR_1);
return -1;
}

return 0;
}

int main(int argc, char** argv)
{
int fd = open(XXX_LKCD_PROC_PATH, O_RDONLY);

if (-1 != fd) {
close(fd);
//printf("%s,start do dump ====\n", XXX_LKCD_PROC_PATH);
if( 0 == XXX_LKCD_MakeDir() ) {
XXX_LKCD_SaveFile();
}
}
else {
printf("[xxx_lkcd_test] main() %s not exist, return\n", XXX_LKCD_PROC_PATH);
}

return 0;
}

Title:Linux Kernel Crash Dump

Author:Victor Huang

Time:2019-03-17 / 16:03

Link:http://wowothink.com/90870e97/

License: Attribution-NonCommercial-NoDerivatives 4.0 International (CC BY-NC-ND 4.0)