_XReply()使用_XIOError()终止app

时间:2014-05-26 13:41:34

标签: java linux x11 xorg xcb

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

完全显示/ gui重绘(或刷新)的速度与JVM / java可以处理的速度一样快。它以迭代和频繁的方式完成,每秒几次或几千次迭代。

在一段确切的时间之后,应用程序以exit(1)终止,我从g _XIOError()调用了gdb。可以在或多或少精确的时间段之后重复该终止,例如,在x86双核2.5GHz上运行15h后。如果我使用一些较慢的计算机,它会持续更长时间,就像它与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

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

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

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

要重复这个问题,正如我上面所写,我必须等待15个小时,但目前我的时间非常短,无法找到问题/终止的原因。 我们还试图重新组织处理gui / display refresh的java部分,但问题没有解决。

一些SW事实:
- java jre 1.8.0_20,即使用java 7也可以重复问题
- libX11.so 1.5.0
- libxcb.so 1.8.1
- debian wheezy
- 内核3.2.0

1 个答案:

答案 0 :(得分:4)

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

在libxcb v1.5代码之后的某个时刻内部引入了64位序列号,并且在进入那些仍然需要32位序列号的公共API时,添加了逻辑以加宽序列号。

以下是来自submitted libxcb bug report的报价(删除了实际的电子邮件):

  

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

     

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

     

libxcb 1.5很好,libxcb 1.8.1不行。将libxcb等分为   这个提交:

     

commit ed37b087519ecb9e74412e4df8f8a217ab6d12a9作者:Jamey   夏日日期:2010年10月9日星期六17:13:45

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&gt; = 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;

详细技术说明

Plase发现包括detailed technical explanation by Jonas Petersen(也包含在上述错误报告中):

  

您好,

     

这是两个补丁。第一个修复了32位序列包装错误。   第二个补丁仅在另一个相关声明中添加注释。

     

补丁包含一些细节。这是谁的全部故事   可能会感兴趣:

     

Xlib(libx11)将使用&#34;致命IO错误11使应用程序崩溃   (资源暂时不可用)&#34;经过4 294 967 296的要求   服务器。那是Xlib内部32位序列包装的时候。

     

大多数应用程序可能很难达到这个数字,但如果是这样的话   他们有机会死于神秘的死亡。比如说   我工作的应用程序在大约20个小时之后总是崩溃   我开始做一些压力测试。它做了一些密集的绘图   通过Xlib使用gktmm2,pixmaps和gc绘图,每帧40帧   全高清分辨率排名第二(在Ubuntu上)。一些优化确实如此   将恩典延长至约35小时,但仍然会崩溃。

     

接下来是一些令人沮丧的数周挖掘和调试   意识到它不在我的应用程序中,也不在gtkmm,gtk或glib中   但它是自2006-10-06以来存在的Xlib中的这个小错误   显然。

     

需要一段时间才能证明数字为0x100000000(2 ^ 32)   一些相关性。 (很多)后来发现它可以复制   仅限Xlib,例如使用此代码:

     

while(1){         XDrawPoint(显示,可绘制,gc,x,y);         XFlush(显示器); }

     

可能需要一到两个小时,但是当它达到4294万时   它会爆炸成致命的IO错误11&#34;。

     

我学到的是即使Xlib使用内部32位   他们在这个过程中(智能地)扩展到64位的序列号   以便32位序列可以包装而不会中断   加宽了64位序列。显然一定有问题   这一点。

     

致命IO错误是在_XReply()中发出的,如果它没有得到   回复哪里应该有一个,但原因在_XSend()中较早   在Xlib 32位序列号包装的那一刻。

     

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

     

requests = dpy-&gt; request - dpy-&gt; xcb-&gt; last_flushed;

     

我的请求= 0x0和last_flushed = 0xffffffff它将分配   0xffffffff00000001到&#39;请求&#39;然后以数字(数量)到XCB   请求。这是主要杀手。

     

第二个位置是:

     

for(sequence = dpy-&gt; xcb-&gt; last_flushed + 1; sequence&lt; = dpy-&gt; request;   \       ++序列)

     

我的请求= 0x0(小于last_flushed)的情况下没有机会   进入循环,结果一些请求被忽略。

     

解决方案是解开&#34;解开&#34; dpy-&gt;请求在这两个地点和   因此保留与last_flushed相关的序列。

     

uint64_t unwrapped_request =((uint64_t)(dpy-&gt; request&lt; \       dpy-&gt; xcb-&gt; last_flushed)&lt;&lt; 32)+ dpy-&gt; request;

     

它创建一个临时的64位请求编号,如果是,则设置位8   &#39;请求&#39;小于&#39; last_flushed&#39;。然后在两者中使用它   位置而不是dpy->请求。

     

我不确定使用该声明是否更有效   inplace,而不是使用变量。

     

require_socket()中还有另一行让我一开始很担心:

     

dpy-&gt; xcb-&gt; last_flushed = dpy-&gt; request = sent;

     

这是一个64位,32位,64位分配。它会截断发送的&#39;至   32位,当他们要求&#39;请求&#39;然后还分配   截断值为(64位)&#39; last_flushed&#39;。但似乎有意。   我已经添加了一个说明,解释为下一个可怜的灵魂调试   序列问题......: - )

     
      
  • 纳斯
  •   
     

Jonas Petersen(2):xcb_io:修复Xlib 32位请求编号包装   xcb_io:添加解释混合类型双重赋值的注释

     

src / xcb_io.c | 14 +++++++++++++++++++++++   3个删除( - )

     

-   1.7.10.4

祝你好运!