0号事件何处来?

暑假开始,格蠹开始对NDB调试器的新一轮升级。研发团队里,有几位新的面孔,包括远程的志愿者,还有新来格蠹的实习生。

本地调试Linux应用是新增的一个较大功能。为了支持这个功能,我们特意把本来集成在ndstub模块中的Linux本地调试代码独立成ndu模块,以便可以同时为ndstub(远程连接)、ndb命令程序(本地命令行接口)和ndb图形前端使用。

代码调整之后,从Windows连接ndstub进行远程调试的功能不工作了。排除一些小问题后,一块大石头挡在面前:收到一个莫名其妙的0号事件。

Debug event 0 for 0.0
ERROR: failed to find process 0.
Unknown event number 0x00000000


event status 6
meta poll returned 0

观察收到的调试事件结构体,整个结构体的数据都是0。所以从事件ID,到进程ID、线程ID都变成了0,把调试引擎搞晕了。

起初,我以为是填写调试事件的代码有问题,但是跟踪一番,没有找到问题。

接下来,我准备深入跟踪传递调试事件的过程。目标机上使用gdb调试ndstub,主机上使用VS调试前端脚本。

这时,奇怪的现象发生了。当主机端通过socket发出poll调用时,超乎寻常的快速得到结果,而这个结果就是诡异的0号事件,而且gdb里的poll断点并没有命中。

跟踪过程中,还有一个奇怪现象,那就是感觉创建子进程的行为不是很顺利。

于是我扩大跟踪范围,对创建被调试进程的函数也设置断点。

在跟踪创建子进程的fork函数时,我感觉到眼前一亮,“哇,原来如此!”

    问题的诱因是字符串转换。Linux下的宽字符(wchar_t)是4字节,Windows下的宽字符(wchar_t)是2字节,同时还要支持普通的单字节char,而ndb要支持两个系统,所以字符串转换成了一件麻烦事情。

错误的过程是这样的。来自Windows主机的创建进程请求传递的是2字节的宽字符,而ndu的CreateProcessW函数接受的wchar_t,其内部又调用wcstombs转换为单字符,以便调用Linux本地API。

在CreateProcessW的某些版本里,使用自己编写的utf162ansi函数,可以把Windows的宽字符转为单字符,但是最近代码改动频繁,utf162ansi被改为wcstombs,于是转换失败,转出来的字符串是空串,这导致传给exec的主程序路径为空。

如果exec成功,那么fork出来的子进程(pid 14323)便会执行自己的新程序,也就是被调试进程。但是现在exec失败了,调用exec的代码打印了一句错误信息后没有做更多处理。

exec  failed with err=2

这导致新的子进程在父进程里继续奔跑。它跑到了本不该它跑的父进程逻辑,提前回复了创建进程请求。

主机端收到创建进程的回复后,继续发出gettargetinfo等请求,都被子进程给回复了。

当主机端调用poll请求时,目标机的原本工作线程执行好真正的创建新进程处理工作,再次回复创建进程请求。这导致主机端的poll请求立刻收到回复。但收到的结果并不是对poll的回复,而是第二次对创建进程请求的回复。

0708114745U#14320:<<<ndpcall getconnectioninfo, in 4, out 16, event 0, 0:0
0708114745U#14320:>>>ndpcall getconnectioninfo[5], in 4, out 16, hr 0x0
0708114745U#14320:<<<ndpcall createprocessw, in 48, out 24, event 0, 0:0
0708114745U#14320:creating process ""
0708114745U#14320:[ndt_createp]
[Detaching after fork from child process 14323]
0708114745E#14320:New Thread 14323 is created
0708114745E#14323:exec  failed with err=2
0708114745U#14323:Process "" is created with pid=0 ret 0
0708114745U#14323:>>>ndpcall createprocessw[11], in 48, out 24, hr 0x0
0708114745U#14323:<<<ndpcall gettargetinfo, in 0, out 1108, event 0, 0:0
0708114745E#14323:failed to open proc file /proc/version_signature for 2
0708114745U#14323:Machine type: aa64
0708114745U#14323:>>>ndpcall gettargetinfo[4], in 0, out 1108, hr 0x0
0708114745U#14323:<<<ndpcall getprocessorid, in 0, out 8, event 0, 0:0
0708114745U#14323:>>>ndpcall getprocessorid[6], in 0, out 8, hr 0x0
0708114929E#14320:waitpid on newly process 14323 got 0x27f�� signaled pid 14323
0708114929E#14320:set PTRACE_SETOPTIONS 0x48 for pid 14323 ret 0, errno 84
0708114929U#14320:process inited for 14323 ndstub

调试的逻辑本来复杂,这又是故障情况,看官可能晕了吧?

来个手工的时序图吧,左侧是目标机的14320号线程,右侧是主机的请求线程,中间是fork出来的14323号线程,本来它应该跑自己的/bin/ls程序的但是exec失败后,它子承父业,跑起了父线程的逻辑。

1fee9932e1601c9f3504a55b2c056853.png

Linux的fork和exec逻辑源自古老的unix,思想朴素,实现灵活,同时也狂野奔放,有时有意想不到的效果。描述这样的复杂问题,文字有点不够了,词不达意,不知道各位格友看懂没有?

7fc562f4593dc504676be3bd178e7c47.png

(写文章很辛苦,恳请各位读者点击“在看”,也欢迎转发)

*************************************************

正心诚意,格物致知,以人文情怀审视软件,以软件技术改变人生

扫描下方二维码或者在微信中搜索“盛格塾”小程序,可以阅读更多文章和有声读物

56384698e05a8cb1e521a3ddd99f58c6.png

也欢迎关注格友公众号

a892c4ee07c6d14cccfdb41f89992a79.jpeg

  • 5
    点赞
  • 20
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值