2014年8月

使用gdb调试php扩展

我们在工作中可能会遇到使用别人的或者自己开发的php .so扩展的时候出一些莫名其妙的错误,因为这些错误是底层级别的,我们看不见,那怎么来调试并发现这些C层面的错误呢?这时候就需要使用gdb(GNU Debugger)了。我们先去http://ftp.gnu.org/gnu/gdb/ 下载最新版的gdb,然后编译安装。

启动调试:

> gdb /path/to/php //指定您php所在的路径
> run test.php //您要调试的php文件,要包含出错的地方

接着应该就可以看见php的执行中有错误的地方了。

但是在osx下会出现这个问题:

Starting program: /Users/username/ws/hello
Unable to find Mach task port for process-id 358: (os/kern) failure (0x5).
(please check gdb is codesigned - see taskgated(8))

提示以上gdb签名错误。

原因是:

  Darwin kernel出于安全考虑,在没有特殊授权的情况下不允许gdb调试任何程序,因为可以调试就掌握了进程的控制权。不过如果是root用户就没有这个问题,不过一般不会用root来调试程序。

解决方法:

  一个常用的解决方法就是给gdb授予系统完全信任的代码签名权利,以对其他进程。

首先,需要创建一个系统代码签名信任证书:

1、启动“钥匙串访问”应用(/Applications/Utilities/Keychain Access.app)

2、打开菜单:钥匙串访问->证书助理->创建证书...

3、输入证书名称,如:gdb-cert;

4、选择身份类型:自签名根证书 (Identity Type to Self Signed Root)

5、选择证书类型:代码签名 (Certificate Type to Code Signing)

6、勾选:让我覆盖这些默认签名 (select the Let me override defaults)

7、一路继续,直到选择存放证书地址,选择:系统

8、这样证书就创建好了,还要设置证书自定义信任

9、右键刚才创建的 gdb-cert 证书,选择“显示简介” (Get Info)

10、点击“信任”,会显示可以自定义的信任选项

11、“代码签名”选择“总是信任” (Code Signing to Always Trust)

其次,将证书授予gdb,执行命令

>codesign -s gdb-cert /path/to/gdb  // 一般在/usr/local/bin/gdb

注意,需要先退出“钥匙串访问”应用,或者重启下系统

好了,以上就给gdb授予了系统信任的代码签名证书,可以正常使用gdb了。

注意:前提是 php -m 中 包含了您要调试的那个扩展。

我们还可以用

nm /path/to/xxx.so 来查看这个扩展中包含了哪些函数。

然后使用 gdb > break function_xxx 来设置这个函数的断点。

然后使用 gdb > bt 来查看具体的错误上下文信息。

解决Seaslog在osx10.9.4 make错误

  昨天又看见了Seaslog发新版的消息,作为作者的朋友,心想还是试试看吧,于是下载了源代码过来编译安装。地址:http://pecl.php.net/get/SeasLog-1.1.0.tgz

不料在make的时候出现了一下错误:

/Users/leandre/SeasLog-1.1.0/SeasLog/seaslog.c:196:6: error: conflicting types for 'seaslog_init_buffer'
void seaslog_init_buffer(TSRMLS_D)
/Users/leandre/SeasLog-1.1.0/SeasLog/seaslog.c:208:6: error: conflicting types for 'seaslog_clear_buffer'
void seaslog_clear_buffer(TSRMLS_D)

剩下还有很多的warning就不贴出来了。

主要原因就是C99不建议隐含定义函数,太容易出错了。

解决方案:在·Seaslog.c·的42行的

42 void seaslog_init_logger(TSRMLS_D);

后面添加以下代码:

void seaslog_init_buffer(TSRMLS_D);    
void seaslog_clear_buffer(TSRMLS_D);    
int _ck_log_dir(TSRMLS_DC);    
int _seaslog_log_content(TSRMLS_DC);    
int _seaslog_log(TSRMLS_DC);    
int _check_level(TSRMLS_DC);    
int _mk_log_dir(TSRMLS_DC);    
int _php_log_ex(TSRMLS_DC);

去定义这些函数满足C99的标准,就OK了。

PS:新版的1.1.4已经解决这个问题并发布:http://pecl.php.net/get/SeasLog-1.1.4.tgz