什么叫在线调试?就是在线上生产环境进行调试,假设有一天用户报某个页面某个数据怎么不对啊,看来线上出BUG了,你要迅速找出原因,首先看日志,可悲剧的是没有足够的日志让你确定BUG的原因,也许你这时想看某个PHP变量的值,可是如果直接在线上加代码调试,风险极大,不可行,如果用线下环境,可是数据环境不同,可能难以复现线上的BUG呢,这个php在线调试工具就是为解决这种问题而生,可以让你方便地查看任意线上PHP变量的值而不用改动代码,就像C的调试工具gdb那样,以下叫它ydb。它还有不少其它功能,所有功能参见本文目录。
ydb采用扩展实现,在url中输入相关调试参数,即可查看相关变量值,参数包括类名,函数名,变量名等。ydb不但可以让你查看任意变量值,而且可以查看函数执行耗时情况,也就是不但可以用来排除业务逻辑BUG,也可以用查看性能问题。你也许担心一个扩展放在线上是否会影响性能,它实际上对性能的影响很小,相关额外计算都是调试者本身发的请求产生,如果你觉得很小的性能影响也不能容忍,那可以在调试时才加上该扩展,重启下php进程即可。
下面来看看ydb的具体使用
假设有如下代码在index.php中是我们的调试对象
<?php
class CB {
private $pv = 'abc';
function add($a,$b){
return $a+$b;
}
}
class CA {
public function look_value($pa,$pb){
$fa = $pa;
$fb = $pb;
$fc = $fa+$fb;
$arr = array(1,2,3);
$objCb = new CB();
$fd = $objCb->add(3,4);
$arr = array(4,5,6);
return $fd;
}
function look_cost(){
$this->fun1();
$this->fun2();
$this->fun3();
usleep(300000);
}
function fun1(){
usleep(100000);
return;
}
function fun2(){
usleep(200000);
return;
}
function fun3(){
usleep(500000);
return;
}
}
$objAt = new CA();
$res = $objAt->look_value(1, 2);
$res = $objAt->look_cost();
echo "should not be here";
一. 查看变量值
假设我们要查看类CA函数look_value中变量arr的值我们需要在浏览器中输入如下url: http://php.wenda.so.com/index.php?c=CA&f=look_value&v=arr ,
各参数意义如下
c : 表示类名
f : 表示函数名
v: 表示你想查看的变量名
键入url的结果是
从结果可以看出值是arr第二次的赋值,所以ydb的显示值都是函数执行完毕变量最后的值(什么?你想看中间值,以后会有的)。
另外,如果不输入v参数,则会打印出函数中的所有变量,输入http://php.wenda.so.com/index.php?c=CA&f=look_value的结果如下所示
可以看到函数look_value中的所有变量包括参数及对象的值都打印出来了
上面说的是get请求,如果是post请求,那该怎么办呢?同样是在url中输入参数,该 url打开提交的表单,在提交表单时,即可打印相应变量值,不过加上参数o标识这是一个post表单请求,这样就不会在当前请求中进行变量打印了。打开url: http://php.wenda.so.com/post.html?c=CA&f=look_value&o=1,显示表单:
点提交按钮后,同样打印类函数look_value中的值,与get请求中一样,如下图所示
扩展中即通过读取post请求的refer值来实现变量值打印。
有时你不想把相关值显示在浏览器上而影响页面的正常展示,而想打印在日志文件中,这时你可以在url中添加l=1参数即可将相关变量输出打印在日志文件中,日志文件是log/ydbinfo.log,在php安装根目录中。
二. 查看执行耗时
ydb可以查看某个函数中所有执行子函数的从高到低的执行耗时排行以及该函数执行的总耗时,通过一个个函数跟踪下去,就很容易找出系统的瓶颈在哪,到底哪个函数执行花费了最长时间。查看执行耗时,也要输入 c,f参数,表示要查看哪个类的哪个函数的耗时情况,另外需要加上t=1参数,表示是查看耗时,而不是查看变量值。继续使用之前index.php中的代码,输入url : http://php.wenda.so.com/index.php?c=CA&f=look_cost&t=1,即可看到如下结果
最上面一行表示look_cost总的耗时,接下来是look_cost中调用的三个函数耗时从大到小的耗时排行,在实际情况中,我们可以进一步将f参数改为耗时最大的那个子函数,一步步跟踪找到系统瓶颈所在。
对于post请求以及打印信息到日志中,查看耗时与查看变量值一样,相应使用o,l参数即可。
三. 更改运行时变量值-强大的单元测试工具
<?php
class CB {
private $pv = 'abc';
function add($a,$b){
return $a+$b;
}
}
class CA {
public function look_value($pa,$pb,$arr){
$pa = 8;
$fa = $pa;
$fb = $pb;
$fc = $fa+$fb;
$arr = array(1,2,3);
$objCb = new CB();
$fd = $objCb->add(3,4);
$arr = array(4,5,6);
return $fd;
}
function look_cost(){
$this->fun1();
$this->fun2();
$this->fun3();
usleep(300000);
}
function fun1(){
usleep(100000);
return;
}
function fun2(){
usleep(200000);
return;
}
function fun3(){
usleep(500000);
return;
}
}
$objAt = new CA();
$arr = array('aa' => 'vvv' ,'bb' =>123);
$res = $objAt->look_value(1, 2,$arr);
$res = $objAt->look_cost();
echo "should not be here";
实际上我们可以在a参数构建自己想要的任意复杂多层次的数组,而不管之前是什么形式的数组,甚至可以将pa这样不是数组的变量覆盖为数组变量,反之,我们也可以将原来是数组的变量覆盖为普通非数组变量。
一般情况下,我们只需要覆盖函数的输入参数,这样我们就可以很方便地对函数进行单元测试了。经常我们只改动了某个函数的一小段代码,也就只需要验证它的正确性即可,我们可以使用ydb通过覆盖各种参数组合对它进行单元测试,在浏览器页面操作中可能很难构造各种特殊输入参数的情况,人为可以定制参数是很方便的。
另外基于这种变量覆盖功能,我们可以建立一个强大的单元测试框架,测试数据都通过url中的a参数进行输入。这个单元测试框架是和语言无关,因为它与被测试对象是通过HTTP协议交互的,这样我们可以用其它语言写单元测试客户端对PHP代码进行单元测试。同样地,我们可以用统一的单元测试客户端对多种语言的项目进行测试,只要被测试的对象是支持类似 ydb这种HTTP协议的交互方式的。
四. 跨网络的调试工具
而ydb是支持跨网络调试的,假设我们的浏览器请求首先到了A模块,而A模块通过HTTP协议调用了B模块,之前描述的所有调试功能都能在B模块中使用。我们可以在url中输入B模块的类名,函数名即可查其它变量值或是耗时排行。当然假设B模块又调用了C 模块,我们同样可以对C模块进行调试,只要相应的模块装上了ydb扩展,ydb目前只支持HTTP协议的网络调用。
<?php
$url = "http://127.0.0.1/index.php";
$host="php.wenda.so.com";
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('host:'.$host));
$res = curl_exec($ch);
//print_r($res);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('host:'.$host));
$res = curl_exec($ch);
//print_r($res);
$ch = curl_init($url);
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('host:'.$host));
$res = curl_exec($ch);
五.网络调用性能分析
在浏览器中输入url php.wenda.so.com/rpcclient.php?r=1,显示结果如下:
通过这样的功能,我们很快就能知道到底是哪个外部接口调用耗费了最长的时间,目前ydb只支持对HTTP协议接口调用耗时情况的显示,未来考虑对mysql,redis,memcache等常用的其它网络调用的支持。
六.dump代码到页面
这是其中的小功能,你可以在url中加上s参数将相应函数的代码dump到页面上,这让你在分析变量值的情况时不用去其它地方看代码。在浏览器键入如下url
http://php.wenda.so.com/index.php?c=CA&f=look_value&s=1 显示结果如下:
是不是跟gdb有点像了,在看变量值的时候同样可以看代码。ydb还有更多强大的功能已在计划中,它只会比gdb更好用,它的最终目标是一个系统出了任何问题只需要在url中简单敲入参数就可以将其以直观方式显示到页面上。
该扩展已放在github 上 https://github.com/micweaver/ydb,ydb支持php5.2及以上版本,将Makefile中的php源码路径改为自己目录实际路径,编译得到ydb.so文件即可,然后在php.ini文件中加入如下配置,重启php进程就可以使用拉
zend_extension=”ydb.so文件的路径 ”
编译生成扩展so文件的具体过程见底下的评论回复,目前只支持linux环境下的使用,你也可以直接下载已经编译好的扩展文件:
http://download.youkuaiyun.com/detail/micweaver/6823323 适用于php5.3以上版本
http://download.youkuaiyun.com/detail/micweaver/6823361 适用于php5.2版本