——聂诚、余昇锦、罗朝江
这个分析绐我上了生动的一课,看看别人是怎么分析问题的,一个问题的分析,你和别人会差在哪里?
一方面是沟通能力,这个问题的分析涉及到很多知识点。首先要有自己分析的手段,陈述自己的猜测,对比和别人同一个问题有不同
一、问题概述
1070 较新版本使用过程中 会出现窗口黑块问题,在长时间使用后容易复现,复现一次后,后面出现黑块的概率会变高。
a)复现后桌面现象:
b)目前发现在企业微信打开的时候复现概率最高, 且只要频繁的进行窗口操作(变换、放大缩小,新建窗口等)就会出现黑块。
c)通过抓取xorg 端的pixelmap数据发现窗口是正常的,但是合成后就会显示黑色
合成前:
二、黑色窗口原因
通过北京窗管那边同事对kwin分析定位到是合成路径中 glXCreatePixmap失败,但是kwin中的代码并没有对返回值进行错误处理,继续合成才产生的黑屏。
分析代码调试kwin,创建pixmap失败的原因是申请的xid 冲突(申请的xid 对应的resource 有值)
三、xid 冲突分析
a)Xid 申请的原理如图:
客户端通过Xallocid 申请id时,如果id没有用完则默认+1, 如果id不够了则向xserver 申请新的区域,xorg这边收到请求后使用算法检索一个没有使用过的连续区域返回。(一般区域大小为0x1fffff ,可以使用0x1fffff这么多资源,用完后重新向xorg 申请。)
b)一般短时间内不会用完,所以走左边流程直接++,
c)只有在客户端申请AddResource后,xorg 才会记录xid 已使用。
d)当xid 区域使用完, 向xorg申请新的区域时 会存在一个上游已知bug:线程1 向xorg 申请xid 区域且还没申请AddResource时, 线程2这时又去申请xid区域,此时返回的区域是一样的,会存在xid冲突。
上游目前没有解这个bug,因为这里存在一个不可能:线程1申请xid区域到申请addresource 期间有0x1fffff多个可用窗口,不可能那么快用完。
只有上游这个bug才会导致xid冲突,最终导致创建pixmap失败而黑屏。但是是什么应用能这么快使用完这个窗口区域?
通过调试发现:正常申请xid 区域时, 返回的区域长度一般都是 0x1fffff 长,但是出现问题后返回的区域都是 1、或者2。
如果区域大小为1的时候,就很容易触发上游的bug: 线程1 申请xid,xid不够向xorg 申请xid 区域,还没来的及AddResource,此时线程2也申请xid,但是xid的可用数量不够,也去申请xid区域,那线程2获取的xid区域和线程1 是一样的,这个时候就有xid 冲突,后面就会导致创建pixmap失败,导致kwin黑屏。
四、为什么xserver 返回的xid区域变小了?
Xserver 通过hash 桶来存储每个客户端的 资源,出现问题时发现 kwin客户端使用了 41111多个资源, 而其他客户端使用的资源数量很少,打印展开发现这41111多个资源的type=48 (damage类型),有4w多个 damage类型的资源都在
调试发现kwin 有的窗口(如unmanaged的窗口)存在资源泄露, 申请了资源,但是没有释放资源。
最终定位到是 有一处代码注释掉了?
在xorg 申请资源的时候 每个客户端可以申请的资源大小为 0x1fffff , 明显 41111 远小于这个值,为什么xorg 最后返回的区域长度只有1 或者2 ,很小
分析xorg 申请xid区域的算法发现原因和kwin的资源泄露有关:
正常流程下,xorg 会返回一个连续连续区域,(不一定是最大,只要找到了就返回)
正常使用情况下 都是和进程pid类似,慢慢增长,只有id数值达到max 才会向xorg申请新的,此时kwin之前申请id 没有释放对应的资源,存在id泄露,那么肯定不会申请到这个id,但是这个id会占一个位置,而有的资源又不存在泄露,就存在下面一个场景:
Xorg 这不的资源桶就因为泄露变成这种类型,使用时间越久就越稀疏,所以很容易申请到长度为1的区域,触发上游bug,导致pixmap创建失败。
打印xorg 维护的资源信息如下:
总结:
1、窗口使用发黑的原因是因为kwin 这边没对glcreatepixmap 返回值做异常处理,导致后续流程异常
2、Glcreatepixmap 创建失败原因是因为xserver一个上游bug,而刚好kwin的资源泄露加大了复现概率。
3、Xserver 这边对id 的申请算法比较无脑,找到了就返回,且xid 和 客户使用的资源没办法保证原子性,所以才有上游这个bug。
对这个问题复盘一下
1.首先看报错日志,这一点大家都一样。
2.有没有机器测试,这一点大家是不一样的,实际上用 intel 集显的机器都能复现。但是刚开始不知道。复现的机器和环境认为不多,没有机器的话是无法分析的。
3.窗管同事提供了 grab-offscreen 的程序。这个程序是别人绐的,如果一直分析是可以通过这个提供思路的。
4.除此之外还有一些开源的调试工具和手段。比如 wmctrl -l 这个命令可以看到一些窗口信息,这个是可以通过 GPT 了解一些调试工具和手段的。还有导出的方式用 xwd -id xxx -out 导出窗口图象,这个是对的。这样就和窗管的分析有了不同的地方。这就是突破口。分析代码有什么区别?用 dbus 来拿图片的方法:
dbus-send --session --print-reply --dest=org.kde.KWin /Screenshot org.kde.kwin.Screenshot.screenshotForWindowExtend uint64:104857865 uint32:240 uint32:130
5.怀疑是 resize 旋转引起的问题,从现象推断的,这个方向错了。
6.窗管加日志继续分析。dpkg -i 安装不解析依赖,加日志遇到了一点挫折。
7.窗管同时尝试 intel 的驱动去掉了 damage 部分。简单做就是回退了。问题依旧。复现时间长,导致排除问题的时间就也比较长。这样相关的干系人聂大佬也跟进分析。此处排除了用户态 intel 驱动修改的问题。
8.余大佬写个 python 的小程序来模拟创建窗口
import time
from pynput.keyboard import Key, Controller
keyboard = Controller()
try:
while True:
# Press and release the Super key
keyboard.press(Key.cmd)
keyboard.release(Key.cmd)
# Wait for one second
time.sleep(1)
except KeyboardInterrupt:
9.使用当前的pixmap id再去xorg获取图片失败; 文管pixmap转到texture之后黑了
图1 利用现有的pixmap id 通过xcb_composite_name_window_pixmap xcb相关函数再获取一下;
图2 将转换后的texture对象 直接toimage 。这是窗管的代码。
10.这个问题在 9 的时候就有了重大进展。后面看代码,从xorg获取pixmap 是正常,在 glXCreatePixmap 转换到texture之后,图像变黑了,而这个 glXCreatePixmap 是 mesa(或者说opengl)当中的。
11.modesettings 这个可能和dri模式有关系,modeset用的dri3,sna用的dri2。sna 驱动就是 intel 集显驱动。
pms 的过程保存在知识库当中了。定位到报错的位置是 opengl 。但是修复的地方在 kwin-x11 的 glx backend 当中。
最终的解决是 kwin 找到了上游的代码修复的。
12.最终修复的 patch 是 unmanaged 窗口的问题,能找到这个上游 patch 不知道是如何找到的,要对 kwin 是比较熟悉的。
在计算机图形界面的上下文中,“managed” 和 “unmanaged” 窗口通常指的是窗口的管理方式或生命周期管理方式的不同。
- Managed 窗口
Managed 窗口通常由操作系统的窗口管理器来管理。在这种情况下,窗口管理器负责窗口的创建、显示、焦点管理、窗口移动和调整大小等操作。具体来说,对于操作系统的窗口管理器,managed 窗口具有以下特征:
由操作系统管理生命周期:操作系统负责创建和销毁这些窗口,确保它们正确地显示在屏幕上,并且可以响应用户输入。
提供标准化的用户体验:窗口管理器确保所有窗口遵循操作系统的用户界面准则,例如窗口的外观和行为。
支持窗口操作:用户可以通过操作系统的窗口装饰和边框来移动、最大化、最小化和关闭窗口。
接收系统事件:managed 窗口可以接收操作系统发送的事件,例如键盘输入、鼠标点击、焦点变化等。
在大多数桌面操作系统中,如Windows、macOS和主流的Linux 桌面环境(如GNOME、KDE等),大部分窗口都是 managed 窗口。
- Unmanaged 窗口
Unmanaged 窗口指的是不受操作系统窗口管理器管理的窗口。通常,这些窗口由应用程序本身直接管理。特点包括:
应用程序负责生命周期管理:应用程序负责创建和销毁这些窗口,以及决定它们的显示和隐藏。
定制化用户体验:应用程序可以自由设计和控制窗口的外观和行为,不受操作系统窗口管理器的限制。
自定义窗口操作:应用程序可以选择是否提供窗口的装饰和边框,以及如何响应窗口的移动、调整大小和关闭操作。
独立处理事件:应用程序需要自行处理来自操作系统的事件,例如输入事件和焦点变化。
Unmanaged 窗口通常用于一些特定的应用场景,例如游戏引擎中的游戏窗口、一些特定类型的工具软件或者需要非标准化界面的应用程序。
总之,“managed” 和 “unmanaged” 窗口的区别主要在于窗口的创建、生命周期管理和用户交互体验上是否由操作系统窗口管理器负责。
分析的两个项目一个是 deepin-kde/kwin ;另外一个是 mesa 。而引入问题的是前者的升级。所以到底是 sna 的问题还是 kwin 的问题呢?东西大了,就像这个社会一样,不好说谁对谁错了,因为都有看起来对一方面,也都有看起来不对的方面。而如何解决问题,也不一定是最好的路径,而是现实妥协的路径。