天天看點

處理segment fault 的一種方法

// author: hjjdebug

segment fault 是記憶體通路錯誤。

如果想不怕segment fault, 就要主動克服它。了解對付它的方法。

由應用引起的segment fault, 是輕量級的,因為當你非法讀時, 或者非法寫時,系統都及時給了你提示,找到代碼行修改錯誤即可。

當出現記憶體通路錯誤時,

較好的情況是: 此時函數調用棧沒有遭到破壞,此時系統可以直接給出是什麼位置引起異常。

較糟的情況是: 堆棧段已經被破壞(非法驅動引起當機,重新開機等),系統僅能給出最底層或根本給不出引起異常的準确位置。 雖然這種情況更難下手,

但可以斷定是不久前有人非法清理或覆寫了堆棧記憶體。

下面僅就第一種情況給出一個例子及定位方法。

1. 創造一個會引起segment fault 的程式。如下例:

[email protected]:~/share/hjj$ cat crash.cpp
#include <stdio.h>

int test_crash2(int *p)
{
        int i;
        i=*p;  // 此句會引起segment fault,因為傳入的位址p 非法.
        return i;
}

int test_crash(int *p)
{
        int j;
        j=test_crash2(p);
        return j;
}

int main(int argc, char *argv[])
{
        printf("hello\n");
        int k = test_crash(0);  // 傳入一個非法位址
        printf("k is %d\n",k);
        return 0;
}
           

這裡面有一個非法通路記憶體問題,會引起segment fault 錯誤。

2. 編譯:

[email protected]:~/share/hjj$ gcc -g -o crash crash.cpp

3. 準備:

[email protected]:~/share/hjj$ sudo dmesg -c

目的: 清理核心ring buffer, 使得更易于觀察segment fault 時核心記錄的消息

4. 執行:

[email protected]:~/share/hjj$ ./crash

hello

Segmentation fault

5.  處理:

記憶體崩潰,系統不會坐視不管,它在/var/log 下會有記錄,實際上對于ubuntu 來說, 它在dmesg 指令, kern.log 及syslog 下都留下了記錄.

5.1 輸入指令 dmesg

[email protected]:~/share/hjj$ dmesg

[238213.905770] crash[31448]: segfault at 0 ip 0804841d sp bf8f568c error 4 in crash[8048000+1000]

5.2運用addr2line 工具分析

[email protected]:~/share/hjj$ addr2line -e crash 0x804841d -f

_Z11test_crash2Pi

/home/gitserver/share/hjj/crash.cpp:6

得到結果:

是crash.cpp 第6行, 在test_crash2 函數中

addr2line 還可有定位核心子產品代碼, 例如。 調試romfs64.ko 子產品時遇到記憶體崩潰。

--------------------------------------------------------------------------------

[  349.754982] BUG: unable to handle kernel paging request at 000000005e6d9000

[  349.754985] IP: [<ffffffffa00685b8>] romfs_lookup+0xc8/0x200 [romfs64]

[  349.754990] PGD 7ad70067 PUD 0

[  349.754992] Oops: 0000 [#1] SMP

[  349.754994] last sysfs file: /sys/devices/virtual/block/loop0/queue/rotational

[  349.754997] CPU 1

[  349.754998] Modules linked in: romfs64 nfsd lockd nfs_acl auth_rpcgss exportfs autofs4 sunrpc ip6t_REJECT xt_tcpudp nf_conntrack_ipv6 xt_state nf_conntrack ip6table_filter ip6_tables x_tables binfmt_misc uinput ppdev parport_pc parport psmouse serio_raw i2c_i801 evbug dcdbas pcspkr iTCO_wdt iTCO_vendor_support e1000e snd_hda_codec_analog snd_hda_intel snd_hda_codec snd_hwdep snd_seq snd_seq_device snd_pcm snd_timer snd soundcore snd_page_alloc intel_agp usbhid hid radeon ttm drm_kms_helper drm i2c_algo_bit [last unloaded: speedstep_lib]

[  349.755025] Pid: 2502, comm: gvfsd-trash Not tainted 2.6.32 #2 OptiPlex 780                 

[  349.755026] RIP: 0010:[<ffffffffa00685b8>]  [<ffffffffa00685b8>] romfs_lookup+0xc8/0x200 [romfs64]

[  349.755030] RSP: 0018:ffff88007a8e3bc8  EFLAGS: 00010282

[  349.755031] RAX: 000000005e6d9000 RBX: 0000000000000040 RCX: 0000000000000000

[  349.755033] RDX: 0000000000000017 RSI: ffff8800377b5170 RDI: 0000000000000021

[  349.755034] RBP: ffff88007a8e3c38 R08: ffff8800377b5000 R09: 0000000000000000

[  349.755036] R10: 0000000000000005 R11: 0000000000000000 R12: ffff88005bf14000

[  349.755037] R13: ffff88005bf0e540 R14: ffff88005bf0e5e0 R15: ffff880037a93e00

[  349.755039] FS:  00007f971ad0a7a0(0000) GS:ffff880003a40000(0000) knlGS:0000000000000000

[  349.755041] CS:  0010 DS: 0000 ES: 0000 CR0: 0000000080050033

[  349.755043] CR2: 000000005e6d9000 CR3: 000000007ad45000 CR4: 00000000000406e0

[  349.755044] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000

[  349.755046] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400

[  349.755048] Process gvfsd-trash (pid: 2502, threadinfo ffff88007a8e2000, task ffff8800797396d0)

[  349.755049] Stack:

[  349.755050]  0000000000000006 ffffffff8114ebe7 ffff88007a8e3be8 0000000003800880

[  349.755053] <0> 0000000000000089 0000000000000040 0000000000000000 00000000ffffff09

[  349.755056] <0> ffff88007a8e3c38 ffff88005bf0e540 ffff88007a8e3d18 ffff88007a8e3dd8

[  349.755059] Call Trace:

[  349.755064]  [<ffffffff8114ebe7>] ? d_alloc+0x27/0x1c0

[  349.755067]  [<ffffffff81144d3b>] do_lookup+0x18b/0x220

[  349.755070]  [<ffffffff81145955>] __link_path_walk+0x755/0xfa0

[  349.755073]  [<ffffffff81143d85>] ? path_to_nameidata+0x45/0x60

[  349.755075]  [<ffffffff81145c8b>] ? __link_path_walk+0xa8b/0xfa0

[  349.755077]  [<ffffffff8114642a>] path_walk+0x6a/0xe0

[  349.755080]  [<ffffffff811465fb>] do_path_lookup+0x5b/0xa0

[  349.755082]  [<ffffffff811472b7>] user_path_at+0x57/0xa0

[  349.755086]  [<ffffffff8129bac8>] ? idr_get_empty_slot+0x108/0x2c0

[  349.755089]  [<ffffffff81523fce>] ? _spin_lock+0xe/0x20

[  349.755092]  [<ffffffff8116d3b9>] ? __fsnotify_update_child_dentry_flags+0x109/0x120

[  349.755095]  [<ffffffff8113dc6c>] vfs_fstatat+0x3c/0x80

[  349.755097]  [<ffffffff81143c61>] ? path_put+0x31/0x40

[  349.755099]  [<ffffffff8113dd1e>] vfs_lstat+0x1e/0x20

[  349.755101]  [<ffffffff8113dd44>] sys_newlstat+0x24/0x50

[  349.755105]  [<ffffffff810b8e6a>] ? audit_syscall_entry+0x24a/0x270

[  349.755108]  [<ffffffff81012072>] system_call_fastpath+0x16/0x1b

[  349.755110] Code: 31 c9 49 8b 00 4c 89 c6 48 8b 00 48 83 e0 e0 48 39 d8 75 23 e9 8c 00 00 00 66 2e 0f 1f 84 00 00 00 00 00 48 8b 46 10 48 83 c6 10 <48> 8b 00 48 83 e0 e0 48 39 d8 74 6c 83 c2 01 39 fa 7c e5 f6 45

[  349.755133] RIP  [<ffffffffa00685b8>] romfs_lookup+0xc8/0x200 [romfs64]

[  349.755136]  RSP <ffff88007a8e3bc8>

[  349.755137] CR2: 000000005e6d9000

[  349.755139] ---[ end trace 8183515136c1a034 ]---

[  356.456806] Bridge firewalling registered

[  357.154018] Enter romfs_statfs()

[  357.154021] Leave romfs_statfs()

[  362.537033] ip_tables: (C) 2000-2006 Netfilter Core Team

--------------------------------------------------------------------------------

[[email protected] ~/software/romfs]# cat /sys/module/romfs64/sections/.text

0xffffffffa0068000

ffffffffa00685b8 - 0xffffffffa0068000 = 0x5b8

[[email protected] ~/software/romfs]# addr2line -e romfs64.ko  0x5b8 -f

romfs_lookup

/root/software/romfs/super.c:299

oop 也是一種segment fault, 但這是輕量級的記憶體錯誤,系統可以跟蹤定位。

另有一種重量級記憶體錯誤,例如調試驅動時,堆棧毀壞或溢出, 會令你一時無從下手。

僅能依賴于對代碼的熟悉程度及自己的智能判斷。 就需要更高的積澱。

對于一個占用記憶體較多的對象,要在堆中建立(用new), 因為如果在棧中使用,可能會把棧撐破.

棧的大小由編譯器确定,一般比堆要小的多了.

現在我用objdump -S 來檢視, 比addr2line 功能更強。

而且c++filt 不直接對檔案操作,而隻對文本行操作,是以,你可以用 echo "檔案名" | c++filt 方式轉換。

如果用c++filt "檔案名", 它把檔案名不當成檔案,隻當成一個字元串!

繼續閱讀