#multithreading #debugging #com #com-interop
#многопоточность #отладка #com #com-взаимодействие
Вопрос:
Мы сталкиваемся с периодическими катастрофическими сбоями среды выполнения COM в большом серверном приложении.
Вот что мы имеем:
Серверный процесс, работающий как служба Windows, содержит множество компонентов COM со свободным потоком, написанных на C / ATL. Несколько клиентских процессов, написанных на C / MFC и .NET, используют эти компоненты с помощью перекрестных вызовов COM (вкл .СЕТЕВОЕ взаимодействие) на том же компьютере. Операционная система — Windows Server 2008 Terminal Server (32-разрядная версия). Весь программный пакет был разработан собственными силами, у нас есть исходный код для всех компонентов. Инструментарий отслеживания записывает ошибки и исключения, сгенерированные во время работы.
Что происходит:
После некоторого случайного периода плавного плавания (от 5 дней до 3 недель) среда выполнения COM сервера, похоже, разваливается при любом сочетании этих симптомов:
- RPC_E_INVALID_HEADER (0x80010111) — «OLE получил пакет с недопустимым заголовком», возвращаемый вызывающей стороне при межпроцессных вызовах методов серверного компонента
- Вызовы CoCreateInstance (CCI) завершаются ошибкой для контекста CLSCTX_LOCAL_SERVER
-
Вызовы CoInitializeEx(COINIT_MULTITHREADED) завершаются ошибкой с CO_E_INIT_TLS (0x80004006)
-
Все операции COM в процессе продолжают выполняться, CCI работает для CLSCTX_INPROC_SERVER.
- Общая система остается отзывчивой, SQL Server работает, никаких признаков проблем за пределами нашего процесса обслуживания.
- Системные ресурсы в порядке, нет утечек памяти, нет аномальной загрузки процессора, нет сбоев
Единственное средство — перезапустить сломанную службу.
Другие (связанные) наблюдения:
- Количество ядер в процессоре оказывает негативное влияние — шестиядерный блок Xeon выходит из строя примерно через 5 дней, меньшие блоки занимают 3 недели или дольше.
- Может быть задействовано взаимодействие с .NET, поскольку выполняется много вызовов, связанных с взаимодействием.СЕТЕВЫЕ клиенты для неуправляемых компонентов COM-сервера также негативно влияют на систему.
- Включение кода трассировки внутри серверного процесса продлевает время работы до следующего сбоя.
Трассировка вводит некоторую частичную синхронизацию и, таким образом, может скрыть эффекты многопоточной гонки. С другой стороны, при работе на большем количестве ядер с гиперпоточностью параллельно выполняется больше потоков и увеличивается частота отказов.
Кто-нибудь сталкивался с подобным поведением или даже сталкивался с результатом RPC_E_INVALID_HEADER HRESULT? Практически нет полезной информации об этой конкретной ошибке и ее потенциальных причинах. Есть ли способы заглянуть внутрь среды выполнения COM, чтобы получить более полезную информацию об использовании пула частных ресурсов COM, например, памяти, дескрипторов, примитивов синхронизации? Можно ли отслеживать состояние TLS-слота процесса (CO_E_INIT_TLS)?
Комментарии:
1. CO_E_INIT_TLS означает, что локальное хранилище потока не может быть найдено / выделено или повреждено. Я бы посмотрел на повреждение памяти, которое может легко вызвать поведение, на которое вы смотрите. Попробуйте подключить windbg к своей службе и завершить процесс, затем используйте! heap -s -v для проверки поврежденных куч.
2. Мы интенсивно проверяли повреждение памяти. Внутренние пулы памяти среды выполнения COM вполне могут быть повреждены, но в этом случае мы ожидаем других ошибок. CO_E_INIT_TLS фактически возвращается, если не осталось выделенных слотов TLS. Слоты TLS являются ресурсом процесса, а не частью динамической памяти.
3. Данные OLE TLS выделяются из кучи процесса по умолчанию, и если выделение кучи завершается неудачно, среда выполнения возвращает CO_E_INIT_TLS. (Он также возвращает CO_E_INIT_TLS, если слот TLS не может быть выделен.)
4. Кроме того, для чего нужен код сбоя
CoCreateInstance(...,CLSCTX_LOCAL_SERVER, ...)
?5. Возможно ли воспроизвести проблему с помощью тестовой программы, которая просто создает и уничтожает объекты в замкнутом цикле? Если это так, это может помочь вам в отладке?
Ответ №1:
Мы уверены, что причина этого дефекта связана с утечкой ресурсов в .NET framework 4.0.
Установки нашего серверного приложения, работающего на .NET 4.0 (clr.dll : 4.0.30319.1) показывают прерывистый сбой во время выполнения COM и легко исправляются путем обновления .NET framework до версии 4.5.1 (clr.dll : 4.0.30319.18444)
Вот как мы определили причину:
Поиск в Интернете обнаружил запись на форуме MSDN: http://social.msdn.microsoft.com/Forums/pt-BR/f928f3cc-8a06-48be-9ed6-e3772bcc32e8/windows-7-x64-com-server-ole32dll-threads-are-not-cleaned-up-after-they-end-causing-com-client?форум = vcmfcatl
В OP там описано получение результата RPC_X_BAD_STUB_DATA (0x800706f7) от CoCreateInstanceEx (CLSCTX_LOCAL_SERVER) после запуска COM-сервера с приложением взаимодействия в течение некоторого времени (месяц или около того).). Он отследил проблему до утечки ресурсов потока, которая наблюдалась косвенно через увеличивающуюся переменную внутри ole32.dll : EventPoolEntry::s_initState, который приводит к сбою CCI, как только его значение становится 0xbfff…
Проверка EventPoolEntry::s_initState в наших ошибочных установках показала, что его значение начиналось примерно с 0x8000 после перезагрузки, а затем постоянно увеличивалось от 100 до 200 в час при нормальной загрузке приложения. Как только s_initState достиг 0xbfff, произошел сбой приложения со всеми симптомами, описанными в нашем первоначальном вопросе. OP на форуме MSDN заподозрил утечку локального ресурса COM-потока, поскольку он наблюдал асимметричные вызовы инициализации потока и очистки потока — 5 x инициализация против 3 x очистка.
Автоматически отслеживая значение s_initState в течение нескольких дней, мы смогли продемонстрировать, что обновление .NET framework до версии 4.5.1 с оригинальной версии 4.0 полностью устраняет утечку.