用GDB 调试 PHP Segmentation fault 错误

转自 http://hi.baidu.com/thinkinginlamp/blog/item/5e8062d9744f012511df9b93.html

Linux下,Segmentation fault很常见,此时,使用GDB调试可以帮助你确定出现错误的原因。

我们先想办法重现一下Segmentation fault现象,下面我们先使用Shell的方式来试验,编写demo.sh脚本:

#!/bin/bash
kill -s SEGV $$

其中$$就是脚本的进程号,SEGV信号的作用等同于Segmentation fault,整句基本意思就是让自己崩溃。

保存好,并且加上执行权限:chmod +x demo.sh

# ./demo.sh
Segmentation fault

在出现Segmentation fault的时候,Linux有能力生成一份Core Dump文件,我们激活这种能力:

# ulimit -c unlimited

生成的Core Dump文件的具体信息可以通过如下命令查看:

# cat /proc/sys/kernel/core_pattern
core

这表示会在当前目录生成core文件,接着运行一次demo.sh文件就能生成core文件了:

# ./demo.sh
Segmentation fault (core dumped)
# ls
core.16069

下面到了使用gdb的时候了:

# gdb /bin/bash core.16069
(gdb) bt
#0 0x00c7c410 in __kernel_vsyscall ()
#1 0x00733146 in kill () from /lib/libc.so.6
#2 0x0807a561 in kill_pid ()
#3 0x080a2b9f in kill_builtin ()
#4 0x0806b81c in dispose_exec_redirects ()
#5 0x0806d010 in new_fd_bitmap ()
#6 0x0806dc17 in execute_command_internal ()
#7 0x0806f443 in execute_command ()
#8 0x0805ed21 in reader_loop ()
#9 0x0805e817 in main ()

至于这些堆栈信息怎么看就靠你自己的经验了。

下面再看一个GDB调试PHP脚本的例子:一般来说,PHP程序员遇到Segmentation fault的情况并不多,不过有时候PHP内部的Bug还是可能触发这类错误,而且一旦出现,没有明确的错误信息,不易确定原因。我这里举的例子是当前CURL模块的一个Bug,版本信息是PHP 5.2.9,CURL 7.15.5。

编写脚本demo.php:

<?php
$url = “http://localhost/”;

$ch = curl_init();

curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, array(‘Hello’ => ‘World’));
curl_setopt($ch, CURLOPT_URL, $url);

$copy = curl_copy_handle($ch);

curl_close($ch);

curl_exec($copy);

curl_close($copy);
?>

执行脚本:

# php demo.php
Segmentation fault (core dumped)

不出所料的话就会出现Segmentation fault错误,还有Core Dump文件。这次我们换一个GDB调用方式:

# gdb php
GNU gdb Red Hat Linux (6.5-37.el5rh)
Copyright (C) 2006 Free Software Foundation, Inc.
GDB is free software, covered by the GNU General Public License, and you are
welcome to change it and/or distribute copies of it under certain conditions.
Type “show copying” to see the conditions.
There is absolutely no warranty for GDB. Type “show warranty” for details.
This GDB was configured as “i386-redhat-linux-gnu”…Using host libthread_db library “/lib/libthread_db.so.1”.

(gdb) run demo.php
Starting program: /usr/local/php/bin/php demo.php
[Thread debugging using libthread_db enabled]
[New Thread -1208236336 (LWP 11970)]

Program received signal SIGSEGV, Segmentation fault.
[Switching to Thread -1208236336 (LWP 11970)]
0x010200a5 in memcpy () from /lib/libc.so.6
(gdb) bt
#0 0x010200a5 in memcpy () from /lib/libc.so.6
#1 0x002423b3 in Curl_FormBoundary () from /usr/lib/libcurl.so.3
#2 0x0024268f in Curl_getFormData () from /usr/lib/libcurl.so.3
#3 0x00247a13 in Curl_http () from /usr/lib/libcurl.so.3
#4 0x002529b0 in Curl_do () from /usr/lib/libcurl.so.3
#5 0x0025ebe7 in Curl_perform () from /usr/lib/libcurl.so.3
#6 0x0025f1d9 in curl_easy_perform () from /usr/lib/libcurl.so.3
#7 0x080c5808 in zif_curl_exec …

如果脚本代码很多,什么mysql, gd, curl一大堆的话,通过上面的调试信息,应该立刻就能判断出是CURL模块的问题,这无疑会大大节省我们的时间。

作者: Su

等待完善