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 的語法文件