我们正在开发一些复杂的应用程序,它包含从我们自定义的.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
答案 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