Core Dump 自定义分析

我们提供的标准分析器对于用户编写的闭源应用有时候可能并不适用,在这些情况下需要使用自定义分析器来进行 core dump 分析。本文将介绍如何使用 YLang 编写自定义的 core dump 分析器。

准备

这里我们以一个简单的 C 程序为例,这个程序会因为除零异常而产生 core dump。

#include <stdio.h>

struct Foo {
  char a[10];
  int b;
  float c;
};

int foo(struct Foo *args)
{
    // segment fault
    return 1 / args->b;
}

int main()
{
    struct Foo args = {"foo", 0, 1.0};
    foo(&args);
    return 0;
}

将上面的 C 代码保存为 test.c 文件,使用 gcc 编译并执行。

编译时需要加上 -g 选项,否则我们将无法从编译后的二进制文件中获得调试符号。

$ gcc -g test.c
$ ./a.out
Floating point exception (core dumped)

如果无法找到产生的 core 文件,请检查你的 ulimit 以及 core_pattern 设置。

分析

选择 Analyzers 页面,点击 Add new analyzer 按钮。

选择 YLang,填入以下代码:

_cmd void
full_ubt(void)
{
    _print_full_ubt();
}

上面的代码定义了一个命令函数,会打印当前的调用栈及参数地址。

右侧选择 Core Dump Analyzer,填写之前准备好的 core 文件地址。

点击保存和运行,得到对应命令输出的结果。

(gdb) full_ubt
0x40054a foo [/tmp/test.c:11] (args=0x7ffc8aefeda0)
0x400584 main [/tmp/test.c:17]
    args=0x6f6f66
0x3ad84 __libc_start_main [(null)../csu/libc-start.c:302]
    resume=<optimized>
    personality=<optimized>
    handle=<optimized>
0x40047d _start [??:0]

可以看到分析器打印出来了当前包含行号信息的调用栈,以及 args 参数的地址 0x7ffc8aefeda0。我们根据行号 /tmp/test.c:11 并结合代码分析,是 args->b 的数值为 0 导致了异常,为了验证这一点,通过获取到的 args 参数的地址,使用分析器打印出里面变量 b 的值。

_cmd void
full_ubt(void)
{
    _print_full_ubt();
}

_cmd void
print_args(void)
{
    struct Foo *f = 0x7ffc8aefeda0;
    printf("args.a: %s\nargs.b: %d\nargs.c: %f\n" ,f->a, f->b, f->c);
}

上面的 YLang 代码比原先增加了一个 print_args 命令函数,这个函数会打印结构体里成员变量的值。

保存并运行分析器,得到以下结果:

(gdb) full_ubt
0x40054a foo [/tmp/test.c:11] (args=0x7ffc8aefeda0)
0x400584 main [/tmp/test.c:17]
    args=0x6f6f66
0x3ad84 __libc_start_main [(null)../csu/libc-start.c:302]
    resume=<optimized>
    personality=<optimized>
    handle=<optimized>
0x40047d _start [??:0]

(gdb) print_args
args.a: foo
args.b: 0
args.c: 1.000000

可以看到,里面的成员变量 b 的值确实为 0,验证了我们的猜想。

通过上面这个简单的例子,大家已经了解到如何使用 YLang 创建自定义分析器来分析 core 文件,实际中的程序异常分析要远比这个例子复杂。YLang 还提供了许多其他强大的功能来帮助我们分析,具体请参阅 YLang 的语法文档