У меня проблемы с поиском причины зависания в приложении Win32. Программное обеспечение визуализирует некоторые данные в визуал OpenGL в тесном цикле:
std::vector<uint8_t> indices;
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glEnableClientState(GL_VERTEX_ARRAY);
glVertexPointer(2, GL_DOUBLE, 0, vertexDataBuffer);
while (...) {
// get index type (1, 2, 4) and index count
indices.resize(indexType * count);
// get indices into "indices" buffer
getIndices(indices.data(), indices.size()); //< seems to hang here!
// draw (I'm using the correct parameters)
glDrawElements(GL_TRIANGLES_*, count, GL_UNSIGNED_*);
}
glDisableClientState(GL_VERTEX_ARRAY);
Код скомпилирован с использованием VC11 Update 1 (CTP 3). При запуске оптимизированного бинарного файла он зависает в вызове getIndices()
(подробнее об этом ниже) после нескольких из этих циклов. У меня уже есть…
я сделал не найти любые проблемы с кодом, обращающимся к выделенному буферу, или любое повреждение кучи. Однако, если я отключу куча с низкой фрагментацией, проблема исчезает. Это также исчезает, если я использую отдельную (с низкой фрагментацией) кучу для indices
буфер.
В любом случае, вот трассировка стека, ведущая к взаимоблокировке:
0:000> kb
ChildEBP RetAddr Args to Child
0034e328 77b039c3 00000000 0034e350 00000000 ntdll!ZwWaitForKeyedEvent+0x15
0034e394 77b062bc 77b94724 080d36a8 0034e464 ntdll!RtlAcquireSRWLockExclusive+0x12e
0034e3c0 77aeb652 0034e464 0034e4b4 00000000 ntdll!RtlpCallVectoredHandlers+0x58
0034e3d4 77aeb314 0034e464 0034e4b4 77b94724 ntdll!RtlCallVectoredExceptionHandlers+0x12
0034e44c 77aa0133 0034e464 0034e4b4 0034e464 ntdll!RtlDispatchException+0x19
0034e44c 77b062c5 0034e464 0034e4b4 0034e464 ntdll!KiUserExceptionDispatcher+0xf
0034e7bc 77aeb652 0034e860 0034e8b0 00000000 ntdll!RtlpCallVectoredHandlers+0x61
0034e7d0 77aeb314 0034e860 0034e8b0 0034ec28 ntdll!RtlCallVectoredExceptionHandlers+0x12
0034e848 77aa0133 0034e860 0034e8b0 0034e860 ntdll!RtlDispatchException+0x19
0034e848 1c43c666 0034e860 0034e8b0 0034e860 ntdll!KiUserExceptionDispatcher+0xf
0034ebe8 1c43c4e5 0034ec28 080d35d0 080d35d6 lcdb4!lc::db::PackedIndices::unpackIndices<unsigned char>+0x86
0034ec14 1c45922d 0034ec28 080d35d0 00000006 lcdb4!lc::db::PackedIndices::unpack+0xb5
...
xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx xxxxxxxx getIndices
Для полноты я выложил код lc::db::PackedIndices::unpackIndices()
включая весь код, добавленный для отладки, в http://ideone.com/sVVXX7.
Код запускающий вызов KiUserExceptionDispatcher
является (*p++) = static_cast<T>(index);
(mov dword ptr [esp+10h],eax
).
Я просто не могу понять, что происходит. Кажется, было сгенерировано исключение, но ни один из моих обработчиков исключений не вызывается. Приложение просто зависает. Я проверил любые заблокированные критические разделы (!lock
) но не нашел ни одного. Кроме того, я не понимаю, почему должно возникать исключение, так как все области памяти действительны. Кто-нибудь может дать мне несколько советов?
Я попытался найти тип исключения:
0:000> s -d esp L1000 1003f
0028ebdc 0001003f 00000000 00000000 00000000 ?...............
0028efd8 0001003f 00000000 00000000 00000000 ?...............
0:000> .cxr 0028ebdc
eax=77b94724 ebx=0804be30 ecx=00000002 edx=00000004 esi=77b94724 edi=0804be28
eip=77b062c5 esp=0028eec4 ebp=0028eee4 iopl=0 nv up ei ng nz na pe cy
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010287
ntdll!RtlpCallVectoredHandlers+0x61:
77b062c5 ff03 inc dword ptr [ebx] ds:002b:0804be30=00000001
0:000> .cxr 0028efd8
eax=0000003b ebx=00000001 ecx=0804bd98 edx=0028f340 esi=0028f340 edi=04b77580
eip=1c43c296 esp=0028f2c0 ebp=0028f2fc iopl=0 nv up ei pl nz na po nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010202
lcdb4!lc::db::PackedIndices::unpackIndices<unsigned char>+0x36:
1c43c296 8801 mov byte ptr [ecx],al ds:002b:0804bd98=3e
Поток завис в ожидании эксклюзивной блокировки на SRW (тонкая блокировка чтения-записи), принадлежащей к коду обработки исключений ОС. И это исключение вызвано вашим кодом. Точное исключение и его подробности можно найти с помощью следующего стекового кадра. 0034e848 77aa0133 0034e860 0034e8b0 0034e860 ntdll! RtlDispatchException + 0x19 — аргумент RtlDispatchException это указатель на EXCEPTION_RECORD. Так что если вы введете .exr 0034e860 Вы можете увидеть запись об исключении. Из записи об исключении вы узнаете доступ к тому адресу, который вызывает исключение (если это исключение нарушения прав доступа).
Поскольку после этих шагов вы обнаружили, что нарушение доступа происходило из-за записи по адресу, который вы законно распределили в куче, вы можете найти атрибуты защиты виртуальной страницы, содержащей этот адрес, с помощью команды !адрес «виртуальный адрес»
Как вы узнали, что атрибуты защиты страницы были изменены (некоторым кодом) PAGE_READONLY на этих адресах кучи и после просмотра стека вызовов других потоков у меня возникает следующая гипотеза, которая, я думаю, может помочь вам найти основную причину.
Я предполагаю, что диспетчер кучи Windows изменяет атрибуты страницы перед созданием исключения, указывающего на повреждение кучи. Похоже, что в куче ole также есть некоторые искажения — из стека вызовов других потоков, которые вы показали. Корень проблемы, вероятно, в коде, повреждающем кучу — который куча впоследствии находит и вызывает исключение, после чего код реализации механизма исключений ОС запускается и зависает на блокировке КСВ, прежде чем он сможет вызвать обработчик исключений в вашем или другом коде библиотеки. После этого другой невежественный поток в вашем коде по праву касается кучи памяти, которую куча уже сделала защищенной из-за повреждения, о котором уже узнал, вызывая исключение и заставляя код механизма исключения срабатывать и попадать в ту же самую мертвый замок. Учитывая, что вы сказали, что проблема не воспроизводима, когда программа запускается под отладчиком, можно предположить, что проблема связана с определенными временными или гоночными условиями.
Трассировка стека рассказывает историю. Ваша программа аварийно завершает работу, и, скорее всего, это исключение нарушения прав доступа, типичный режим сбоя для кода C ++ и обычно запускается из-за повреждения кучи. Затем Windows пытается вызвать фильтры исключений для поиска любого кода, который готов обработать исключение. Прежде всего, это обработчики, установленные AddVectoredExceptionHandler (). Для этого требуется блокировка, чтобы предотвратить повторный вход, когда один из этих обработчиков, в свою очередь, вызывает сбой.
И на этом доллар останавливается. Точно, почему неясно из трассировки стека. Это может быть потому, что другой поток также упал на повреждение кучи и занят обработкой исключения и взял блокировку. Используйте Debug + Windows + Threads, чтобы посмотреть на них. Но более вероятно, что состояние процесса настолько искажено, что сам объект блокировки также был поврежден. Вряд ли, но это случается.
И да, отключение кучи с низкой фрагментацией способно скрыть повреждение кучи. Структура памяти будет сильно отличаться, поэтому, какой бы код ни вызывал повреждение, теперь он может ударить по чему-то невинному. Это, конечно, не решение.
Отладка + Исключение, установите флажок Брошенный для «Исключения Win32». Отладчик теперь остановится при возникновении исключения. По крайней мере, вы будете знать, какое исключение выдается. В конечном итоге вам нужно выяснить, где происходит повреждение кучи. Он никогда не находится в коде, который вылетел, удачи в его отладке.
Если вы используете графическую карту ATI (с драйверами ATI), это известная проблема, которая не должна пропускать никакое состояние, иначе повреждение памяти произойдет позже.
Попробуйте отключить все состояния, которые вы можете (glDisableClientState), используйте APITrace, чтобы узнать, какое из них вы забыли.
Один из простых способов проверить повреждение памяти в графическом драйвере — это либо протестировать на другой плате / драйвере, либо форсировать рендеринг программного обеспечения.