人生何处不相逢,我与bug不共戴天!

人生处处有Bug,哪一个最令你目瞪口呆,久久不能忘怀?今天就来浅浅分享一下吧!

工作这么多年,目瞪口呆的多了。我就讲一个前不久遇到的
这里先提供一些名词解释,省得后面说故事的时候影响流畅性。没兴趣的可以直接跳过这段
        1.HDR: 高动态范围的内容,能表示超过传统的0.0-1.0,或者整数的0-255范围的颜色        2.HDR metadata: 描述了HDR内容的最大最小亮度等信息,系统和设备可以通过这人HDRmetadata来微调显示方式。
        3.HDR10:广泛支持的HDR视频格式,RGB每人通道10 bit,metadata只能在一开始的时候确定。
        4.HDR10+: 较新支持的HDR视频格式,也是每个通道10 bit,可以在任何时候插入新的metadata.
        5.DWM: Desktop Window Manager,在Windows里负责把不同窗门的内容染到屏幕.
        6.RS3, RS4: Windows 10 v1709和v1804

        7. Dirty regionq: DWM会把屏幕上需要刷新的部分动态标记为dirty region。只有dirtyregion里的内容会在这一顿更新,其他的保持原样。以此来减少刷新开销。        

        8.MPO: Multi-plane overlays,一些硬件支持>1的MPO,那就表示硬件可以做一定的混合这时候DWM不需要自己做所有的混合,只需要把几个平面都提交给硬件,让硬件做混合就行更高效。最常见的做法就是视频回放。如果一个视频没有被别的窗口遮挡,那么DWM就可以把视频单独放到一个平面。这样虽然视频的内容一直在变化,但并不需要标记成dirty region,也不害要DWM做额外处理。
        9.DXGl: Windows Vista+的图形底层,负责程序和系统打打交道。
        10.dxgkrnl. DirectX的内核组件,负责和硬件的内核驱动打打交道,以及进程之间交换图形数据.

        Win10在RS3加入了播放HDR视频的能力,用Edge开Netflix网站,只要账号允许,就能播放上面的HDR视频。此为前提。
        接着,有人突然发现RS4上播放HDR视频,卡的要死要死要死,完全无法正常使用电脑。而同一台机器上,用RS3却没事。这很严重,说明是个regression只,必须要在发布前修好。因为在RS3的时候,我曾经帮那人解决过一个视频播放的性能问题,所以理所当然他又找了我。我本来以为是因为Edge加了光照造成的,结果发现,我的光照并没有造成任何性能影响。而这个bug本身是人很深的坑。
        首先是重现条件苛刻。我试了youtube上几个HDR视频,都没能重现。然后试了几个Netflix视频发现都开不起来HDR。报bug的人说,必须用Netflix提供的测试账号才能开HDR的功能。行了能播放HDR了,但没有遇到bug描述的性能严重下降。又捣鼓了一阵子,才知道必须播放一个特定的视频会重现: Chefs Table France。

人生何处不相逢,我与bug不共戴天!_第1张图片

        行吧,就放这个视频。结果呢,还是没有重现。我就这么看啊看啊,看他们怎么和面,怎么切,怎么下锅。1天之后,bug? 什么bug? 学会法式摆盘才是最重要的

人生何处不相逢,我与bug不共戴天!_第2张图片

好吧,第二天bug还得搞。我以前在别的贴子也说过,调bug这件事情,应该用科学的方法来处理。也就是,
        1.分离控制变量只,提假说
        2.做实验,证实或证伪假说
        3.如果没解决,回到第一步
那么咱们就按照这个路了来吧,看来是个硬骨头。
        第一个条件比较明确,必须是i3的NUC。这是能支持HDR视频回放的最低端机器。比这个好的都没遇到这个bug。看来这个配置的机器正好处于边缘,这个bug造成的性能下降正好让这个机器卡死
        接下来,奇怪的是,不是每回都能复现。看一小时视频也不见得会出现一次这个状况。但另一个同事却说,他能经常遇到。百思不得其解中,我到他办公室,看他是怎么复现的。一切看上去那么平常,同样的机器,同一段视频,他就能每次都看出来卡,我的就不能,这什么情况。一定是存在某人控制变量没有被发现。
        既然这件事情跟机器和视频无关,难道是网络? 当时我被临时分配到了open space,和别人共享网络出口。所以同一段视频,在我机器上只能到1080p和中等码率,而在办公室内可以到2k以上和高码率。经过进一步分析后发现,码率其实只是问题的一小部分。不同码率造成不同幅度的CPU耗用率上涨,虽然重要,但不是唯一的问题来源

        从内容上没辙了,我只能从操作上找问题。经过观察,发现我的操作方式是,在邮件里copy网址新开一个Edge页面,把网址贴进去看视频。而我同事的操作方式是,最小化Edge页面,在邮件里copy网址,恢复Edge页面,把网址贴进去看视频。难道就是这么一个简单的操作区别造成了问题? 我回到我的座位上,改用最小化的方式试了一下,果然每次都能复现!
        好了,现在可以归纳出复现条件了
                1.特定的机器,必须是i3的NUC
                2.特定码率,太低不行(跟网络速度有关,只有到最高速度才行)
                3.特定分辨率,必须2K-4K (其实也跟码率有关了)
                4.特定帧速率,必须30FPS以上 (其实也跟码率有关了)
                5.特定的加密
                6.特定的操作方式,必须最小化再恢复
        而Chef's Table France正好符合2-4,所以成了复现的利器。而这第5点则成了我debug的突破既然最小化再恢复是个关键的控制变量,我就可以在这基础上频繁提假说了
        假说一,在最小化之前和恢复之后,一定有个状态变化,使得视频播放的代价增大许多
经验告诉我,第一个要查的就是dirty region。于是用一个内部工具,把dirty region显示出来。果然,证实了我的假说。在窗口最小化之前,视频是通过MPO播放的,屏幕上并没有dirty region.在窗口最小化再恢复之后,整个视频区域都成了dirty region。

        假说一得到证实。可以进一步追踪
        假说二,bug在MPO,MPO没有被启用.
内部工具是可以看MPO的区域的。打开之后发现视频区域有启用MPO
        假说二证伪。需要提新假说
        假说三,在有MPO的情况下仍然设置了dirty region。
同时打开dirty region和MPO,发现两者重叠。并且MPO虽然开着,但并没有往里填数据。DWM仍然是自己做混合,开着MPO但不用。
        假说二证实
        假说四,视频部分被错误地设置成dirty region。
        这是最头大的问题。因为在DWM里,dirty region的原因有几十种,又是通过消息驱动机制只来做的,所以即便你看到dirty region了,也并不知道消息是从哪里来的。所以每次遇到dirty region的bug,大家都不愿意碰。因为这次的问题很窄,仅限于HDR视频。那就专门查因为HDR造成的dirtregion吧。看了那部分代码,发现HDR metadata如果发生了变化,就会设置成dirty region。在那里下个断点,发现,在最小化之前,断点没有被hit到,最小化再恢复之后,每一顿都会hit到。
        假说四证实。并且进 步narrow down到HDR metadata 上。
        假说五,是一个RS4对HDR metadata的修改造成的新bug。
        比较了RS3和RS4在HDR部分的代码,发现RS3只支持HDR10,而RS4开始支持HDR10+。两者的metadata格式不同,HDR10的可以转化成HDR10+的。所以RS4的实现方法就是,不管输入是HDR10还是HDR10+,都转成HDR10+处理。这样简单很多,也不容易出问题。
        在RS3里,HDR10视频是由DWM处理的,所以这人数据流的走向是
                1.App把一个swapchain和一个metadata提交给DXGI,告诉系统,从现在开始,我这个窗口就是用这样的metadata来诠释了
                2.DXGI把swapchain的内容和metadata传给dxgkrnl
                3.dxgkrnl如果发现metadata变了,就设置一个metadata changed的标志,并把这些都交给DWM

                4.DWM发现metadata变了,就获取新的metadata,并把swapchain的内容用metadata转换成屏幕的颜色空间,把转换后的结果交回给DXGI去显示,并清除掉metadata changed。
在RS4里,DX说,我在最后显示的时候也要用到metadata,所以DWM你别把metadata吃了,继续传给我用。于是,新的数据流走向就成了
        1.App把一个swapchain和一个metadata提交给DXGI,告诉系统,从现在开始,我这个窗口就是用这样的metadata来诠释了 (没变)
        2.DXGI把swapchain的内容和metadata传给dxgkrnl (没变)

        3.dxgkrnl如果发现metadata变了,就设置 个metadata changed的标志,并把这些都交给DWM (没变)
        4.DWM发现metadata变了,就获取新的metadata,并把swapchain的内容用metadata转换成屏幕的颜色空间,然后把转换后的结果和metadata changed标志Q交回给DXGI去显示
        行了,有了这些分析,事实慢慢浮出水面。这RS3和RS4的第四步不同,很可能就是原因。尤        

        其是,之前是由DWM来清除metadata dirtyq,而现在又是在哪里清除的呢?
        在代码里做了一系列的搜索,发现,没人清除! 所以一但设置,就永远标记为changed,永远划分为dirty region。结果每次都需要DWM去处理整顿数据,性能就低下来了。具体来说,dxgkrnl里那段代码大概长这样:

人生何处不相逢,我与bug不共戴天!_第3张图片

        这TM搞什么啊,一旦改变了,就万劫不复。非常明显的bug,就这么藏在kerne只里好几个月,直到被我偶然发现。要不是有i3那种烂机器,估计还得不知道多久才能知道这里有问题。
        所以要做实验就简单了,但修改kernel还是有点麻烦的,所以我的实验就在DWM里进行,拿到metadata后清除这个changed。实验里不用管DXGI怎么显示,因为不相关。结果果然解决了dirtyregion的问题,CPU耗用率也不再上升。
        假说五证实,并且解决问题
        最后的修改还是在dxgkrnl里做的,把metadataDesc.changed挪到了if之外,一行而已。折腾这么圈,从app到dxgiQ到kernel到dwm,连法式摆盘都学会了,结果是个弱智的bug。

你可能感兴趣的:(学习,bug)