Взаимоблокировка процесса отладки (LdrpDrainWorkQueue/LdrpLoadCompleteEvent)

avatar
Aleksey Ivchenko
9 августа 2021 в 03:01
159
1
1

Все!

Я отлаживаю один довольно странный случай зависания процесса/нехватки памяти, используя стандартный аварийный дамп Windows с помощью WinDbg. Очевидно, ему не хватает адресного пространства из-за того, что создается слишком много потоков (это 32-битный процесс), и я пытаюсь выяснить, что не так с инициализацией потоков (см. типичны для этой программы, она имеет несколько потоков со стеками вызовов 3 типов, таких как:

1)

00 02cefb08 77544413 02fc024c 00000000 02cefb8c ntdll_774f0000!NtWaitForAlertByThreadId+0xc
01 02cefb28 7754434d 00000000 00000000 ffffffff ntdll_774f0000!RtlpWaitOnAddressWithTimeout+0x33
02 02cefb6c 7754423f 00000004 00000000 00000000 ntdll_774f0000!RtlpWaitOnAddress+0xa5
03 02cefba8 7752a605 02fc0000 02fc0000 02fc04b0 ntdll_774f0000!RtlpWaitOnCriticalSection+0xaa
04 02cefbc8 7752a525 02fc0248 02cefc88 77533844 ntdll_774f0000!RtlpEnterCriticalSectionContended+0xd5
05 02cefbd4 77533844 02fc0248 62da3da7 02fc04b0 ntdll_774f0000!RtlEnterCriticalSection+0x45
06 02cefc88 77533688 02fc04b0 02fc04b8 00000007 ntdll_774f0000!RtlpFreeHeap+0x174
07 02cefcd8 110d27fc 02fc0000 00000000 02fc04b8 ntdll_774f0000!RtlFreeHeap+0x758
...

Эти потоки застряли за критическим разделом 02fc024c, который занят несуществующим потоком, и довольно сложно понять, что с ним произошло.

Некоторые потоки пытаются нормально завершиться, но застревают в очереди LdrpDrainWorkQueue:

2)

 # ChildEBP RetAddr  Args to Child              
00 05e5fd54 77527631 00000064 00000000 00000000 ntdll_774f0000!NtWaitForSingleObject+0xc
01 05e5fd78 7752b105 65f13f5f 00404e7c 00000000 ntdll_774f0000!LdrpDrainWorkQueue+0xbd
02 05e5fe70 7755179c 00404e7c 00404e7c 1086eb50 ntdll_774f0000!LdrShutdownThread+0x85
03 05e5ff40 00404efe 00000000 0042cef4 0042cefc ntdll_774f0000!RtlExitUserThread+0x4c
04 05e5ff6c 00404ea6 05e5ffcc 004049b8 05e5ff80 abc!EndThread+0x6
05 05e5ff80 743962c4 1086eb50 743962a0 941a355e abc!ThreadWrapper+0x2a
06 05e5ff94 77550779 1086eb50 65f13ef3 00000000 kernel32!BaseThreadInitThunk+0x24
07 05e5ffdc 77550744 ffffffff 77573606 00000000 ntdll_774f0000!__RtlUserThreadStart+0x2f
08 05e5ffec 00000000 00404e7c 1086eb50 00000000 ntdll_774f0000!_RtlUserThreadStart+0x1b

Кроме того, дамп представляет около 1400 потоков на очень ранней стадии инициализации, которые были созданы в течение последних 5 минут жизни процесса со стеком вызовов вида:

3)

 # ChildEBP RetAddr  Args to Child              
00 0ed1fba4 77527631 00000064 00000000 00000000 ntdll_774f0000!NtWaitForSingleObject+0xc
01 0ed1fbcc 7752b586 6ec53d9b ffffffff 1ed9d000 ntdll_774f0000!LdrpDrainWorkQueue+0xbd
02 0ed1fcb4 77557d86 6ec53c27 00000000 00000000 ntdll_774f0000!LdrpInitializeThread+0x8d
03 0ed1fd08 77557ce0 00000000 00000000 0ed1fd24 ntdll_774f0000!_LdrpInitialize+0x6a
04 0ed1fd10 00000000 0ed1fd24 774f0000 00000000 ntdll_774f0000!LdrInitializeThunk+0x10

Эти потоки также ожидают в LdrpDrainWorkQueue сообщения о событии LdrpLoadCompleteEvent. Это событие связано с параллельным загрузчиком (для справки, первый ответ на его вопрос от RbMm, несколько похожая, но другая ситуация здесь) Это событие создается во время инициализации процесса и сигнализируется после параллельного Загрузка DLL завершена, поэтому все LdrpInitializeThread могут проходить через DllMain и сигнализировать THREAD_ATTACH. Но я не понимаю, почему он находится в несигнальном состоянии в процессе, который работает уже несколько недель? Работает ли параллельный загрузчик и с LoadLibrary, поэтому LdrpLoadCompleteEvent сбрасывается? В разборке не нашел.

В любом случае, я пытаюсь понять, почему процесс создал такие странные стеки вызовов, прежде чем он был принудительно завершен. Я мог представить, что какой-то поток начал загружать DLL, что привело к сбросу LdrpLoadCompleteEvent, затем какой-то поток, удерживающий блокировку для кучи, умер плохим образом, поэтому загрузка dll не могла быть завершена, поэтому LdrpLoadCompleteEvent никогда не сигнализировался, следовательно, никаких новых потоки могли быть инициализированы. Однако в дампе нет ни одного потока, загружающего dll.

Любое понимание/подсказка относительно того, как такие стеки вызовов могли быть разработаны, или что еще я мог сделать, чтобы выжать больше информации из дампа, приветствуется.

Спасибо!

Источник

Ответы (1)

avatar
Mike Robinson
9 августа 2021 в 03:22
-1

Ваша фундаментальная проблема архитектурная ... легендарная проблема, известная как "пробуксовка".

Возможно, ваша система спроектирована с использованием того, что я называю "подходом пылающей стрелы". Всякий раз, когда поступает новый запрос, "просто зажгите еще одну пылающую стрелу и подбросьте ее в воздух". К сожалению, вы просто не можете этого сделать.

Постоянное решение вашей проблемы, к сожалению, никогда не будет решено с помощью отладчика: потребуется переделка.

Aleksey Ivchenko
9 августа 2021 в 03:36
0

Это примечание абсолютно, возмутительно не связано с характером описанной проблемы (эта проблема может возникнуть для приложений, не обрабатывающих никаких запросов, всего с парой потоков, см., например, ссылку № 2). Пожалуйста, воздержитесь от заметок не по теме.