1

我们正在开发一些复杂的应用程序,它由 linux 二进制文件与我们定制的 .jar 文件中的 java jni 调用(来自在 linux 二进制文件中创建的 JVM)集成。所有的 gui 工作都是由 java 部分实现和完成的。每次必须更改某些 gui 属性或必须重新绘制 gui 时,都是通过 jni 调用 JVM 来完成的。

以 JVM/java 可以处理的速度重新绘制(或刷新)完整的 display/gui。它以迭代和频繁的方式完成,每秒数百或数千次迭代。

在某个确切的时间之后,应用程序被终止exit(1),我使用 gdb 来调用它_XIOError()。这种终止可以在或多或少精确的时间段之后重复,例如在 x86 双核 2.5GHz 上大约 15 小时之后。如果我使用一些速度较慢的计算机,它会持续更长时间,就像它与 cpu/gpu 速度成正比一样。一些结论是 xorg 的某些部分用完了某些资源或类似的东西。

这是我的回溯:

#0  0xb7fe1424 in __kernel_vsyscall ()
#1  0xb7c50941 in raise () from /lib/i386-linux-gnu/i686/cmov/libc.so.6
#2  0xb7c53d72 in abort () from /lib/i386-linux-gnu/i686/cmov/libc.so.6
#3  0xb7fdc69d in exit () from /temp/bin/liboverrides.so
#4  0xa0005c80 in _XIOError () from /usr/lib/i386-linux-gnu/libX11.so.6
#5  0xa0003afe in _XReply () from /usr/lib/i386-linux-gnu/libX11.so.6
#6  0x9fffee7b in XSync () from /usr/lib/i386-linux-gnu/libX11.so.6
#7  0xa01232b8 in X11SD_GetSharedImage () from /usr/lib/jvm/jre1.8.0_20/lib/i386/libawt_xawt.so
#8  0xa012529e in X11SD_GetRasInfo () from /usr/lib/jvm/jre1.8.0_20/lib/i386/libawt_xawt.so
#9  0xa01aac3d in Java_sun_java2d_loops_ScaledBlit_Scale () from /usr/lib/jvm/jre1.8.0_20/lib/i386/libawt.so

exit()在 liboverrides.so 中进行了自己的调用,并将其与 LD​​_PRELOAD 一起使用,在/SIGABRTexit()的帮助下捕获 gdb 中的调用。abort()在对 libX11 和 libxcb 进行了一些调试后,我注意到_XReply()得到了 NULL 回复(来自 的响应xcb_wait_for_reply()),导致调用_XIOError()and exit(1)。更深入地研究 libxcb in xcb_wait_for_reply()function,我注意到它可以返回 NULL 回复的原因之一是它检测到断开或关闭的套接字连接,这可能是我的情况。

出于测试目的,如果我更改 xcb_io.c 并忽略_XIOError(),应用程序将不再工作。如果我在里面重复请求_XReply(),它每次都会失败,即每个都得到 NULL 响应xcb_wait_for_reply()

所以,我的问题是为什么这种不受控制的应用程序终止与 exit(1) from _XReply()-> XIOError()->exit(1)发生,或者我怎样才能找出发生的原因和发生了什么,所以我可以修复它或做一些解决方法。

为了重复这个问题,正如我上面所写的,我必须等待大约 15 小时,但目前我的调试时间非常短,并且找不到问题/终止的原因。我们还尝试重新组织处理 gui/显示刷新的 java 部分,但问题没有解决。

一些软件事实:
- java jre 1.8.0_20,即使使用 java 7 也会重复这个问题
- libX11.so 1.5.0
- libxcb.so 1.8.1
- debian wheezy
- kernel 3.2.0

4

1 回答 1

4

这可能是 libX11 中有关处理用于 xcb_wait_for_reply 的请求号的已知问题。

在引入 libxcb v1.5 代码以在内部各处使用 64 位序列号的某个时间点,并添加了逻辑以在进入那些仍采用 32 位序列号的公共 API 时扩大序列号。

以下是提交的 libxcb 错误报告的引述(实际电子邮件已删除):

我们有一个执行大量 XDrawString 和 XDrawLine 的应用程序。几个小时后,应用程序因 XIOError 退出。

XIOError 在文件 xcb_io.c 中的 libX11 中调用,函数 _XReply。它没有得到 xcb_wait_for_reply 的响应。

libxcb 1.5 很好,libxcb 1.8.1 不是。二等分 libxcb 指向此提交:

提交 ed37b087519ecb9e74412e4df8f8a217ab6d12a9 作者:Jamey Sharp 日期:2010 年 10 月 9 日星期六 17:13:45 -0700

xcb_in: Use 64-bit sequence numbers internally everywhere.

Widen sequence numbers on entry to those public APIs that still take
32-bit sequence numbers.

Signed-off-by: Jamey Sharp <jamey@xxxxxx.xxx>

在 1.8.1 之上恢复它会有所帮助。

向libxcb添加跟踪我发现用于xcb_wait_for_reply的最后一个请求号是:4294900463和4294965487(_XReply函数的while循环中的两个调用),半秒后:63215(然后调用XIOError)。widen_request 也是 63215,我预计是 63215+2^32。因此,请求似乎没有正确扩大。

上面的提交还将 poll_for_reply 中的比较从 XCB_SEQUENCE_COMPARE_32 更改为 XCB_SEQUENCE_COMPARE。也许扩展从未正常工作,但从未观察到,因为只比较了较低的 32 位。

重现问题

这是提交的错误报告中用于重现问题的原始代码片段:

  for(;;) {
    XDrawLine(dpy, w, gc, 10, 60, 180, 20);
    XFlush(dpy);
  }

显然这个问题可以用更简单的代码重现:

 for(;;) {
    XNoOp(dpy);
  }

根据提交的 libxcb 错误报告,这些条件需要重现(假设重现代码在 xdraw.c 中):

  • libxcb >= 1.8(即包括提交 ed37b08)
  • 32位编译:gcc -m32 -lX11 -o xdraw xdraw.c
  • 序列计数器换行。

建议的补丁

可以在 libxcb 1.8.1 之上应用的建议补丁是这样的:

diff --git a/src/xcb_io.c b/src/xcb_io.c
index 300ef57..8616dce 100644
--- a/src/xcb_io.c
+++ b/src/xcb_io.c
@@ -454,7 +454,7 @@ void _XSend(Display *dpy, const char *data, long size)
        static const xReq dummy_request;
        static char const pad[3];
        struct iovec vec[3];
-       uint64_t requests;
+       unsigned long requests;
        _XExtension *ext;
        xcb_connection_t *c = dpy->xcb->connection;
        if(dpy->flags & XlibDisplayIOError)
@@ -470,7 +470,7 @@ void _XSend(Display *dpy, const char *data, long size)
        if(dpy->xcb->event_owner != XlibOwnsEventQueue || dpy->async_handlers)
        {
                uint64_t sequence;
-               for(sequence = dpy->xcb->last_flushed + 1; sequence <= dpy->request; ++sequence)
+               for(sequence = dpy->xcb->last_flushed + 1; (unsigned long) sequence <= dpy->request; ++sequence)
                        append_pending_request(dpy, sequence);
        }
        requests = dpy->request - dpy->xcb->last_flushed;

详细的技术说明

请在下面找到Jonas Petersen 的详细技术解释(也包含在上述错误报告中):

你好,

这里有两个补丁。第一个修复了 32 位序列换行错误。第二个补丁只为另一个相关声明添加了注释。

补丁包含一些细节。以下是可能感兴趣的人的全部故事:

在向服务器发出 4 294 967 296 个请求后,Xlib (libx11) 将使应用程序崩溃并显示“致命 IO 错误 11(资源暂时不可用)”。这就是 Xlib 内部 32 位序列回绕的时候。

大多数应用程序可能很难达到这个数字,但如果它们达到了,它们就有机会神秘死亡。例如,当我开始进行一些压力测试时,我正在处理的应用程序总是在大约 20 小时后崩溃。它使用 gktmm2、像素图和 gc 以每秒 40 帧的速度以全高清分辨率(在 Ubuntu 上)通过 Xlib 进行一些密集绘图。一些优化确实将宽限期延长到大约 35 小时,但它仍然会崩溃。

接下来是令人沮丧的几周的挖掘和调试,以意识到它不在我的应用程序中,也不在 gtkmm、gtk 或 glib 中,而是 Xlib 中的这个小错误,显然自 2006 年 10 月 6 日以来就存在。

花了一段时间才发现数字 0x100000000 (2^32) 具有一定的相关性。(很多)后来证明它只能用 Xlib 复制,例如使用以下代码:

while(1) { XDrawPoint(display, drawable, gc, x, y); XFlush(显示);}

这可能需要一两个小时,但是当它达到 42.94 亿时,它会爆炸成“Fatal IO error 11”。

然后我了解到,即使 Xlib 使用内部 32 位序列号,它们在此过程中也会(巧妙地)扩大到 64 位,以便 32 位序列可以在扩大后的 64 位序列中不中断地进行换行。显然,这肯定有什么问题。

Fatal IO 错误在 _XReply() 中没有得到应有的回复时发出,但原因是在 Xlib 32 位序列号换行时的 _XSend() 中较早。

问题是当它回绕到 0 时,'last_flushed' 的值仍将位于上边界(例如 0xffffffff)。_XSend() (xcb_io.c) 中有两个位置在此状态下失败,因为它们依赖于这些值始终是连续的,第一个位置是:

请求= dpy->请求-dpy->xcb->last_flushed;

在 request = 0x0 和 last_flushed = 0xffffffff 的情况下,它将 0xffffffff00000001 分配给“请求”,然后分配给 XCB 作为请求的数量(数量)。这是主要杀手。

第二个位置是这样的:

for(sequence = dpy->xcb->last_flushed + 1; 序列 <= dpy->request;\++sequence)

在请求 = 0x0(小于 last_flushed)的情况下,永远没有机会进入循环,因此忽略了一些请求。

解决方案是在这两个位置“解包”dpy->request,从而保留与last_flushed相关的序列。

uint64_t unwrapped_request = ((uint64_t)(dpy->request < \ dpy->xcb->last_flushed) << 32) + dpy->request;

如果“request”小于“last_flushed”,它会创建一个临时的 64 位请求编号,该编号设置为第 8 位。然后在两个位置使用它而不是 dpy->request。

我不确定在原地使用该语句是否比使用变量更有效。

require_socket() 中的另一行一开始让我担心:

dpy->xcb->last_flushed = dpy->request = 发送;

那是一个 64 位、32 位、64 位的赋值。当将其分配给“请求”时,它将截断“发送”到 32 位,然后还将截断的值分配给(64 位)“last_flushed”。但这似乎很重要。我添加了一个注释,解释了下一个糟糕的灵魂调试序列问题...... :-)

  • 乔纳斯

Jonas Petersen (2): xcb_io: Fix Xlib 32-bit request number wrapping xcb_io: 添加注释解释混合类型双重赋值

src/xcb_io.c | 14 +++++++++++--- 1个文件更改,11个插入(+),3个删除(-)

-- 1.7.10.4

祝你好运!

于 2014-05-26T21:59:17.543 回答