简介
crash-utility 开源项目是用于解析具体版本 Linux 内核 Core 的工具,基于 GDB 之上的程序,支持原始 GDB 指令以及特定的 Linux 模块解析,支持格式化内核数据结构、反汇编源代码、内核堆栈回溯等。对于开发者感兴趣解析的模块,可自定义增加插件模块解析。
基本构成
插件开发构成 | 描述 |
---|---|
defs.h 文件 | 开发必备头文件,提供 crash-utility 环境下的 API 函数声明。 |
void attribute((constructor)) demo_init(void) | demo 插件模块初始化入口,其它插件开发则替换 demo 字符串即可。 |
void attribute((destructor)) demo_fini(void) | 在 crash-utility 环境下移除 demo 插件时自动调用。 |
static struct command_table_entry command_table[] = { “demo”, cmd_demo, help_demo, 0 },{ NULL }register_extension(command_table); | 用于初始化入口注册插件函数。(关键)command_table_entry 结构体由 { 指令名称,指令主函数,指令帮助信息,…… } 组成。 |
void cmd_demo(void) | 命令行传入参数已被 crash-utility 自身处理过,如果需使用 getopt,getopt_long 函数解析参数,使用全局变量 ( argcnt 、args ) 。 |
char *help_demo[] = {“demo”, // 指令名称”demo info”, // 指令具体描述”demo [-p] “, // 指令概要”version 1.0”, // 具体描述信息NULL}; | 该字符串用于在 crash-utility 中输入 help demo 时返回。 |
{ ARM64,X86_64,…… } 编译宏定义 | 必须在编译脚本中确定目标架构,会影响 defs.h 部分函数及宏。 |
-rdynamic 编译 cflags 定义 | 添加到动态符号表 |
自定义插件,以下全局变量,以及宏需重新定义,扩展新的结构体成员。
结构体偏移、大小解析相关 | |
---|---|
extern struct offset_table offset_table;extern struct size_table size_table; | 全局结构体成员偏移表全局结构体以及成员大小表 |
#define OFFSET(X) (… …) | 与 offset_table 绑定,获取指定结构体成员偏移量 |
#define SIZE(X) (… …) | 与 size_table 绑定,获取指定结构体或成员大小 |
#define VALID_MEMBER(X) (… …) | 与 offset_table 绑定,校验成员是否有定义 |
#define ASSIGN_OFFSET(X) (… …) | 与 offset_table 绑定,获取指定结构体成员偏移量,不校验 |
#define MEMBER_OFFSET_INIT(X, Y, Z) (… …) | 初始化 offset_table 表成员偏移量 |
#define ASSIGN_SIZE(X) (… …) | 与 size_table 绑定,获取指定结构体或成员大小,不校验 |
#define MEMBER_SIZE_INIT(X, Y, Z) (… …) | 初始化 size_table 表成员大小 |
#define STRUCT_SIZE_INIT(X, Y) (… …) | 初始化 size_table 表结构体大小 |
命令行处理以及终端输出 | |
---|---|
extern FILE *fp; | 全局变量,fprintf(fp, “xxx\n”); 命令行输出 |
extern char *args[MAXARGS];extern int argcnt;extern int argerrs; | 插件主函数的参数集。 |
常用 API 集
错误处理 | 描述 |
---|---|
#define FAULT_ON_ERROR (0x1) | 错误终止,并输出错误信息 |
#define RETURN_ON_ERROR (0x2) | 错误不终止,但输出错误信息 |
#define QUIET (0x4) | 错误终止且不输出错误信息 |
内存相关 | 描述 |
---|---|
int readmem(ulonglong, int, void *, long, char *, ulong); | 参数含义 { 地址,地址类型,结果集,错误信息,错误处理 }地址类型: { KVADDR,UVADDR,PHYSADDR }例子:ulong value;readmem(0xFFFFC9000D35BCD0, KVADDR, &value, sizeof(void*), “read value err!”, FAULT_ON_ERROR); |
#define GETBUF(X) getbuf((long)(X)) | 申请内存,可用 malloc 替代。 |
#define FREEBUF(X) freebuf((char *)(X)) | 释放内存,与 GETBUF 成对存在。 |
int kvtop(struct task_context *, ulong, physaddr_t *, int); | 内核态虚拟地址转物理地址 |
int uvtop(struct task_context *, ulong, physaddr_t *, int); | 用户态虚拟地址转物理地址 |
机器属性相关 | 描述 |
---|---|
extern struct machdep_table *machdep; | 全局变量描述目标机器的具体信息,如 va_bits、kimage_text等, 可用 help -m 查看。 |
#define PAGESIZE() (machdep->pagesize) | 目标机器页表大小 |
#define PAGESHIFT() (machdep->pageshift) | 目标机器页表移位 |
Task 相关 | 描述 |
---|---|
extern struct task_table task_table, *tt; | 全局变量 tt,保存 task 的信息。 |
#define CURRENT_CONTEXT() (tt->current) | 当前 task context,可用命令 set 查看 |
#define CURRENT_TASK() (tt->current->task) | |
#define CURRENT_PID() (tt->current->pid) | |
#define CURRENT_COMM() (tt->current->comm) | |
int set_context(ulong, ulong); | 设置当前 task context。 |
ulong pid_to_task(ulong); | pid 转 task_struct 地址 |
ulong task_to_pid(ulong); | task_struct 地址转 pid |
#define RUNNING_TASKS() (tt->running_tasks) | |
#define FIRST_CONTEXT() (tt->context_array) | 所有 task ,可用 ps 查看。首个 task context 地址,一般用于遍历所有 task 时使用。 |
符号相关 | 描述 |
---|---|
ulong symbol_value(char *);ulong symbol_value_module(char *, char *); | 获取符号地址在 xxx.ko 模块中获取符号地址 |
int symbol_exists(char *s);int kernel_symbol_exists(char *s); | 判断符号是否存在 |
struct syment *value_search(ulong, ulong *);struct syment *value_search_module(ulong, ulong *); | 查询地址是某个符号。 |
链表、树等遍历函数。、 | 描述 |
---|---|
struct list_data {ulong flags;ulong start;long member_offset;long list_head_offset;ulong end;ulong searchfor;char **structname;int structname_args;char *header;ulong *list_ptr;int (*callback_func)(void *, void *);void *callback_data;long struct_list_offset;}; | flags:#define VERBOSE 1#define LIST_ALLOCATE (VERBOSE << 10)LIST_ALLOCATE 该 flags 参数会为 list_ptr 分配对应内存,并将遍历得到的所有成员填充到该内存地址上,最后开发者需要用 FREEBUF 将其释放。VERBOSE 该 flags 参数会在遍历的同时打印输出结构。start: 遍历的起始地址member_offset: 链表节点在结构体中的偏移地址 |
int do_list(struct list_data *); | 可用于 list_head, hlist_head 数据结构的遍历。 |
int hq_open(void);int hq_close(void); | hash_table 启用备份遍历数据,crash-utility 提供的遍历函数都会使用 hq_enter(value) 将数据备份到此处。 |
int retrieve_list(ulong *buf, int); | 拷贝 hash_table 数据到 buf 内存。 |
int do_rdtree(struct tree_data *); | 可用 radix_tree 数据结构的遍历。 |
int do_rbtree(struct tree_data *); | 可用 rb_root 数据结构的遍历。 |
int do_xatree(struct tree_data *); | 可用 xarray 数据结构的遍历。 |
Int do_mptree(struct tree_data *); | 可用 maple 数据结构的遍历。 |
ulong do_maple_tree(ulong, int, struct list_pair *); | 可用 maple 数据结构的遍历。 |
#define MAPLE_TREE_COUNT (1) | MAPLE_TREE_COUNT 仅统计数量 |
#define MAPLE_TREE_SEARCH (2) | MAPLE_TREE_SEARCH 检索 |
#define MAPLE_TREE_DUMP (3) | MAPLE_TREE_DUMP 遍历同时打印结果 |
#define MAPLE_TREE_GATHER (4) | MAPLE_TREE_GATHER 遍历结果拷贝到 list_pair 内存 |
#define MAPLE_TREE_DUMP_CB (5) | |
ulong do_xarray(ulong, int, struct list_pair *); | 与 do_maple_tree 同理,用于 xarray 数据结构的遍历。 |
#define XARRAY_COUNT (1) | |
#define XARRAY_SEARCH (2) | |
#define XARRAY_DUMP (3) | |
#define XARRAY_GATHER (4) | |
#define XARRAY_DUMP_CB (5) | |
ulong do_radix_tree(ulong, int, struct list_pair *); | 同理。 |
#define RADIX_TREE_COUNT (1) | |
#define RADIX_TREE_SEARCH (2) | |
#define RADIX_TREE_DUMP (3) | |
#define RADIX_TREE_GATHER (4) | |
#define RADIX_TREE_DUMP_CB (5) |
案例参考
开源项目: github.com/Penguin38/O… ,该项目目前已支持转储进程 Core 文件,支持解析 zram,shmem 交换页内存。关于转储细节均与 OpenCoreSDK 一致,内核版本 5.10 ~ 6.1,其它版本未做测试,性能比 gcore 插件快上许多。
1 | lua复制代码crash> mod -s zram zram.ko |
结合开源项目: github.com/Penguin38/O… 进行解析。
1 | perl复制代码core-parser> top 20 -d --app |
目前公版的状态,未来可能会支持功能,可翻阅历史文章,精力有限不定期更新。
本文转载自: 掘金