_XReply () завершает приложение с _XIOError ()

1

Мы разрабатываем комплексное приложение, состоящее из бинарного пакета Linux, интегрированного с java-jni-вызовами (из JVM, созданного в Linux-бинарнике) из нашего пользовательского файла.jar. Вся работа gui выполняется и выполняется с помощью java-части. Каждый раз, когда какое-либо свойство gui должно быть изменено или нужно перекрасить gui, это делается jni-вызовом JVM.

Полный дисплей /gui перекрашивается (или обновляется) так быстро, как JVM/java может справиться с ним. Это делается итеративно и часто, несколько hunderds или тысячи итераций в секунду.

После некоторого точного времени приложение завершается с exit(1) который я поймал с помощью gdb для _XIOError() из _XIOError(). Это прекращение может быть повторено после более или менее точного периода времени, например, через примерно 15 часов на двухъядерном процессоре с частотой 2,5 ГГц. Если я использую более медленный компьютер, он длится дольше, например, он пропорционален скорости процессора /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 для захвата вызова exit() в gdb с помощью функции abort()/SIGABRT. После некоторой отладки libX11 и libxcb я заметил, что _XReply() получил NULL-ответ (ответ от xcb_wait_for_reply()), который вызывает вызов _XIOError() и exit(1). Более подробно в libxcb в функции xcb_wait_for_reply() я заметил, что одна из причин, по которой он может вернуть ответ NULL, - это когда он обнаруживает сломанное или закрытое соединение сокета, что может быть моей ситуацией.

Для целей тестирования, если я изменяю xcb_io.c и игнорирую _XIOError(), приложение больше не работает. И если я повторяю запрос внутри _XReply(), он терпит неудачу каждый раз, т. xcb_wait_for_reply() Получает NULL-ответ на каждый xcb_wait_for_reply().

Итак, мои вопросы будут _XReply() с _XReply(), почему произошло такое неконтролируемое завершение приложения с выходом (1) из _XReply()XIOError()exit(1) или как я могу выяснить причину, почему и что произошло, поэтому я могу исправить это или сделайте некоторое обходное решение.

Чтобы повторить эту проблему, как я уже писал выше, мне нужно подождать около 15 часов, но в настоящее время я очень короткое время для отладки и не могу найти причину проблемы/завершения. Мы также попытались реорганизовать java-часть, которая обрабатывает обновление gui/display, но проблема не была решена.

Некоторые факты SW:
- java jre 1.8.0_20, даже с java 7 можно повторить проблему
- libX11.so 1.5.0
- libxcb.so 1.8.1
- debian wheezy
- ядро 3.2.0

Теги:
x11
xorg
xcb

1 ответ

4
Лучший ответ

Вероятно, это известная проблема в libX11 в отношении обработки номеров запросов, используемых для xcb_wait_for_reply.

В некоторый момент после того, как был введен код libxcb v1.5 для использования 64-битных порядковых номеров внутри, и была добавлена логика для расширения порядковых номеров при входе в эти общедоступные API, которые по-прежнему принимают 32-битные порядковые номера.

Вот цитата из представленного отчета об ошибке libxcb (удалены фактические сообщения электронной почты):

У нас есть приложение, которое делает много XDrawString и XDrawLine. Через несколько часов приложение выходит из XIOError.

XIOError вызывается в libX11 в файле xcb_io.c, function _XReply. Он не получил ответа от xcb_wait_for_reply.

libxcb 1.5 отлично, libxcb 1.8.1 - нет. Bisecting libxcb указывает на эту фиксацию:

commit ed37b087519ecb9e74412e4df8f8a217ab6d12a9 Автор: Jamey Sharp Дата: Сб Октябрь 9 17:13:45 2010 -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 <[email protected]>

Возврат к вершине 1.8.1 помогает.

Добавление трассировок в libxcb Я обнаружил, что последние номера запросов, используемые для xcb_wait_for_reply, следующие: 4294900463 и 4294965487 (два вызова в цикле while функции _XReply), через полсекунды: 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 (т.е. включает commit 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-битную ошибку обхода последовательности. Второй патч добавляет комментарий к другому соответствующему утверждению.

Патчи содержат некоторые детали. Вот целая история для тех, кто может быть заинтересован:

Xlib (libx11) приведет к сбою приложения с "Fatal IO error 11 (ресурс временно недоступен)" после 4 294 967 296 запросов к серверу. То есть, когда внутренняя 32-битная последовательность Xlib завершается.

Большинство приложений, вероятно, вряд ли достигнут этого числа, но если они это сделают, у них будет шанс умереть таинственной смертью. Например, приложение, над которым я работаю, всегда крутилось примерно через 20 часов, когда я начал выполнять стресс-тестирование. Он интенсивно рисует через Xlib, используя gktmm2, pixmaps и gc, со скоростью 40 кадров в секунду в полном разрешении hd (на Ubuntu). Некоторая оптимизация действительно увеличила изящество до 35 часов, но она все равно потерпит крах.

Затем последовали некоторые разочаровывающие недели рытья и отладки, чтобы понять, что это не в моем приложении, ни в gtkmm, gtk или glib, а в том, что это небольшая ошибка в Xlib, которая существует с 2006-10-06, по-видимому.

Потребовалось некоторое время, чтобы выяснить, что число 0x100000000 (2 ^ 32) имеет некоторую актуальность. (Много позже), оказалось, что он может быть воспроизведен только с помощью Xlib, используя этот код, например:

while (1) {XDrawPoint (display, drawable, gc, x, y); XFlush(display); XFlush (дисплей); } }

Это может занять один или два часа, но когда он достигнет 4294 миллиона, он взорвется в "Fatal IO error 11".

То, что я тогда узнал, состоит в том, что, хотя Xlib использует внутренние 32-битные порядковые номера, они получают (умно) расширенные до 64 бит в процессе, так что 32-битная последовательность может обернуться без какого-либо нарушения расширенной 64-битной последовательности. Очевидно, что с этим что-то не так.

Ошибка Fatal IO выдается в _XReply(), когда он не получает ответа там, где он должен быть указан, но причина ранее в _XSend() в момент, когда 32-разрядный порядковый номер Xlib завершается.

Проблема в том, что когда он обертывается до 0, значение "last_flushed" будет по-прежнему находиться на верхней границе (например, 0xffffffff). В _XSend() (xcb_io.c) есть два местоположения, которые терпят неудачу в этом состоянии, потому что они все время полагаются на последовательные значения, первое место:

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

В случае запроса = 0x0 и last_flushed = 0xffffffff он будет присваивать 0xffffffff00000001 "запросам", а затем XCB в виде количества (количества) запросов. Это главный убийца.

Второе местоположение:

для (sequence = dpy-> xcb-> last_flushed + 1; sequence <= dpy-> request;\++sequence)

В случае запроса = 0x0 (меньше, чем last_flushed) нет возможности войти в цикл заново, и в результате некоторые запросы игнорируются.

Решение состоит в том, чтобы "развернуть" dpy-> запрос в этих двух местах и таким образом сохранить последовательность, связанную с last_flushed.

uint64_t unwrapped_request = ((uint64_t) (dpy-> запрос <\ dpy-> xcb-> last_flushed) << 32) + dpy-> запрос;

Он создает временный 64-битный номер запроса, который имеет бит 8, если "запрос" меньше, чем "last_flushed". Затем он используется в двух местах вместо запроса dpy->.

Я не уверен, может ли быть более эффективным использовать этот оператор inplace вместо использования переменной.

Есть еще одна строка в require_socket(), которая меня сначала беспокоила:

dpy-> xcb-> last_flushed = dpy-> request = sent;

Это 64-битное, 32-битное, 64-битное назначение. Он будет усекать "отправлено" на 32-битный, когда он будет "запрашивать", а затем также назначит усеченное значение (64-разрядному) "last_flushed". Но это кажется интуитивным. Я добавил примечание, объясняющее, что для следующей неудовлетворительной проблемы с отладкой души... :-)

  • Jonas

Jonas Petersen (2): xcb_io: Fix Xlib Оболочка 32-разрядных запросов xcb_io: Добавить комментарий, объясняющий двойное назначение смешанного типа

src/xcb_io.c | 14 +++++++++++ --- 1 файл изменен, 11 вставок (+), 3 удаления (-)

- 1.7.10.4

Удачи!

Ещё вопросы

Сообщество Overcoder
Наверх
Меню