前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >跨平台PHP调试器设计及使用方法——探索和设计

跨平台PHP调试器设计及使用方法——探索和设计

作者头像
方亮
发布2019-01-16 15:41:17
9320
发布2019-01-16 15:41:17
举报
文章被收录于专栏:方亮方亮

        在《跨平台PHP调试器设计及使用方法——立项》一文中,我确定了使用xdebug作为调试器插件部分的基础组件。xdebug提供了一个远程调试的功能(相关资料可以详见https://xdebug.org/docs/remote),我们这个项目便是基于这个功能实现的。(转载请指明出于breaksoftware的csdn博客)

        远程调试是基于网络传输方式进行交互的一种调试方式,那么其必定有服务端和客户端两部分组成。这儿的服务端和客户端都是相对的,因为一个客户端可能在和服务器通信后就变成了一个服务端,而服务端则在一次通信后就变成了客户端。xdebug在这个模型中属于服务端,因为它是嵌入到PHP执行器内部,影响PHP执行流程的部分,这些核心功能肯定是作为服务端的一部分而存在。同时它也应该有接收和响应请求的功能。

        xdebug提供了两种连接的方式,一种是固定地址单线连接。一种是未知地址多线连接。

        我们先看下固定地址单线连接,它的执行流程如下图

  1. 嵌入在PHP执行程序中的Xdebug开启了一个80端口
  2. 控制调试过程的IDE发起一次HTTP的调试请求
  3. Xdebug根据配置项中的remote_host和remote_port字段(也就是IDE所在机器的IP和IDE开放的端口),向IDE发起连接请求
  4. IDE和Xdebug建立连接,相互通信
  5. Xdebug应答2过程中的HTTP请求

        上述方式存在一个问题,就是要在Xdebug里配置好IDE的IP和PORT,这样就只能有一台机器上的IDE可以与该Xdebug通信。为了可以支持多用户同时连接方式,Xdebug还提供了一种针对未知IP连接的方式,我们先看下流程图:

  1. 嵌入在PHP执行程序中的Xdebug开启一个80端口
  2. 控制调试过程的IDE发起一次HTTP的调试请求
  3. Xdebug的配置项中药配置remote_connect_back为1或者on,还要配置remote_port。Xdebug根据2中的请求解析出远端IDE的IP,然后通过该IP和remote_port发起一次连接请求
  4. IDE和Xdebug建立连接,相互通信
  5. Xdebug应答2过程中的HTTP请求

        面对这两种方式,我们需要如何选择呢?首先我们看一个问题,如果配置过netbeans和Xdebug连接的朋友,肯定记得netbeans中要配置代码FTP地址。因为作为IDE需要能查看到远程机器上这些要被执行的文件(因为要展现给用户看执行到哪儿了,哪儿要下断点等)。虽然xdebug的source命令可以获取当前执行文件的内容,而对于一款调试器来说,我们往往需要很多尚未发生的内容。所以IDE要能访问远程文件是必要的。

        但是这一步骤,也将影响用户配置调试器的进度。因为为了调试,我还要给远程机器开启一个FTP服务,还要配置服务对应的本地地址,这些似乎都和我们要进行调试的行为无关。所以为了解决这个问题,我们索性在调试器中不放开编辑源码的功能。同时我们将IDE和Xdebug放在同一台机器上,这样IDE可以读取本地的PHP执行的文件,这样也就不用开启FTP服务了。于是,我们就选择固定IP单线连接的方式。

        这儿需要指出的是,我们在配置remote_host时肯定不能写死一个IP。因为我们代码和配置随时会被拷贝到其他环境,所以写死一些值将严重影响其适用性,于是我们可以使用localhost来代替固定IP

xdebug.remote_enable=On xdebug.remote_handler=dbgp  xdebug.remote_host=localhost xdebug.remote_port=9000

        通信方式解决后,我们便需要关注通信协议的问题。xdebug使用的是一个叫dpgp的协议,其协议文档见https://xdebug.org/docs-dbgp.php。

        这份协议文档虽然比较长,但是还算简单。作为行为请求的发起方,需要向Xdebug发送command -a value -b value……这种类型的请求内容,而Xdebug会返回一个XML内容。对于这种看似不对称的请求类型造成的原因,文档中解释是说XML内容生成是容易的,但是解析却需要其他的库。然而作者明显不想引入这些并不太重要的第三方库。其实我觉得这种请求方式挺好的,它非常像我们使用的其他输入式调试器,比如windbg。

        接下来我们看下调试的过程

  1. IDE获取Xdebug支持的一些属性(不同版本的Xdebug支持不同的功能,所以IDE要先探知它的支持什么不支持什么)
  2. IDE设置一些Xdebug属性、断点等信息
  3. Xdebug让代码运行起来,直到遇到断点或者运行结束
  4. 如果遇到断点,IDE可以向Xdebug询问一些变量值,堆栈信息,或者修改一些变量值等

        我们再看下Xdebug文档中一段比较类似人通话的过程,它也展现了整个调试过程的样貌

IDE:  feature_get supports_async DBG:  yes IDE:  stdin redirect DBG:  ok IDE:  stderr redirect DBG:  ok IDE:  run DBG:  stdin data... DBG:  stdin data... DBG:  reached breakpoint, done running IDE:  give me some variables DBG:  ok, here they are IDE:  evaluate this expression DBG:  stderr data... DBG:  ok, done IDE:  run IDE:  break DBG:  ok, breaking DBG:  at breakpoint, done running IDE:  stop DBG:  good bye

        虽然看似整个协议非常简单,但是我并没打算去实现一套发送请求并解析XML返回结果的库。我看了一下其他软件的Xdebug通信库基本上都是调用了一套名叫pydbgp的Python实现库,我也准备使用它。有时候还是非常必要站在巨人的肩膀上去实现一些事。

        pydbgd的官方地址是http://jaredforsyth.com/pydbgp/。我们只需要取用下载包中的bin和dbgp两个目录下文件。因为这块的资料非常少,所以研究使用这套库也花费了我一定的时间。而且这套库实现中也存在不少缺陷,我也总是在不停探索和打patch中前行。好在经过一段努力,终于把它和Xdebug打通了。下面我展示一段pydbgp和Xdebug的交互过程

        第1部分是告诉IDE,调试用的IDE-KEY是什么,要监听哪个端口的。因为我是以netbeans的调试作为模板,所以我的IDE-KEY也是Netbeans和Xdebug交互的IDE-Key:netbeans-xdebug。当然这个值可以改成别的,但是要和xdebug的配置文件的idekey值一样

xdebug.idekey="netbeans-xdebug"

        然后我启动了监听本地9000端口。这个9000端口号也不是随便设置的,也要和Xdebug配置文件中的remote_port值一样

xdebug.remote_port=9000 

        此时我们可以在网页中发起一次请求,用于触发php执行。这种触发行为分为两种,我会在之后做介绍。

        网页此时一直处在等待状态,这表示Xdebug已经把PHP的执行过程给中断了。于是我们可以进行下步操作。

        我们执行sessions指令,用于查看目前有哪些连接已经建立过了。如上图,pydbgp返回了连接信息。当然这个展现不是Xdebug的原始数据——原始数据是XML的。

        知道连接号后,我们使用select指令进入特定的连接。之后使用status查看调试的状态。第一次status执行后,表示调试器处在开始状态,这种状态是一种中断状态,它还没进入PHP代码层。我们执行“步过”——step over操作一次,这个时候PHP执行便进入代码了。使用stack_get指令查看当前的调用堆栈信息,这些信息中包括了栈号、文件路径、函数名。如果我们不关心之后的执行,就直接调用run指令,让程序跑到底。这个时候调用status命令,可以看到调试器处在stopping的状态。此时再run一下,本次调试就彻底结束了。结束后,我们使用quit指令退出当前调试。如果还有别的调试请求过来,则可以再调用sessions查看连接号,重复上述的调试过程。上图中4是这个过程的一个体现。

        如果不想进行调试了,则可以调用quit退出整个pydbgp。

        pydbgp帮助我们连接Xdebug,转发命令,解析返回的XML结果。但是它只提供了标准输入方式的请求接口,我们没法像直接调用API一样调用这些接口。而且我也无意于将这套接口改成API的形式。于是我决定采用父子进程通信的方式,父进程是我们的业务逻辑,子进程是pydbgp,父子进程通过重定向输入输出来进行通信。这是我最初的想法,但是最后重定向的方案也被否掉了,因为python在不同平台上(windows和linux)对这种方式存在兼容问题。这也是迫使我在项目后期老老实实使用标准socket去通信,还好我之前代码耦合做的比较好,替代方案很快就用上了。这儿感慨下,一些事情想时总是美好的样子,而现实往往是曲折的样子。当然随着阅历的增多,想像也会越来越靠谱,这就是经验的作用。         Xdebug给我们提供了很多调试的基础功能。但是作为调试器,我们应该在这些基础之上开发出更多组合性的功能,这样可以帮助使用者更快的发现问题。所以我们还需要对这些功能进行一些高阶封装组合,这些内容我们会在之后介绍。还有就是有些功能可能不是需要调试器提供的,比如日志文件监控,所以这块也将是我们调试器的一些辅助功能。于是我们调试器的结构是这样的

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016年10月13日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
远程调试
远程调试(Remote Debugging,RD)在云端为用户提供上千台真实手机/定制机/模拟器设备,快速实现随时随地测试。运用云测技术对测试方式、操作体验进行了优化,具备多样性的测试能力,包括随时截图和记录调试日志,稳定的支持自动化测试, 设备灵活调度,用例高效执行, 快速定位产品功能和兼容性问题。云手机帮助应用、移动游戏快速发现和解决问题,节省百万硬件费用,加速敏捷研发流程。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档