Разбор аварийных дампов 1С: почему они пустые по умолчанию и как читать их без WinDbg
Иван Недомолков · Опубликовано:
Падает rphost. В каталоге сервера копятся файлы .mdmp, вы делаете всё по инструкции из популярных статей: ставите WinDbg (низкоуровневый отладчик Microsoft для разбора аварийных дампов), открываете дамп, выполняете !analyze -v и получаете в ответ короткое ERROR: Exception information not found. Ни кода ошибки, ни адреса сбоя, ни намёка, что пошло не так.
Скажу сразу, без интриги: так и будет, и дело не в ваших руках. Причина в настройке сбора дампов, и почти на каждом сервере 1С она стоит так, что разобрать по этим файлам ничего нельзя. Ниже - почему так, как за десять секунд понять, есть ли у вас хоть один пригодный дамп, как прочитать дамп средствами самой 1С без WinDbg, и что сделать, чтобы следующее падение было разбираемым.
Это полная техническая версия. Короткий диагностический разбор, только по официальным механизмам, у нас опубликован на Инфостарте; здесь - всё целиком, включая чтение двоичного формата дампа.
Материал на данных одной инсталляции: клиент-серверная, два узла кластера, платформа 8.3.27, Windows Server 2016, семь месяцев наблюдений, 354 собранных дампа.
Почему дамп пустой
В файле logcfg.xml сервера тип дампа по умолчанию нулевой:
<dump create="false" type="0" prntscrn="false"/>
На дефолтной настройке (type="0") эти дампы шли без потока ExceptionStream: ни кода ошибки, ни адреса сбоя, ни контекста нити. Из такого файла причину не достанет ни один инструмент, включая WinDbg, - он честно ответит, что записи об исключении нет, и будет прав.
Насколько это массово: из 310 читаемых дампов запись об исключении нашлась ровно в одном. Полгода сервер аккуратно складывал в каталог файлы, по которым невозможно понять, отчего он упал. Это не поломка, это настройка по умолчанию.
Но кое-что в файле есть всегда, даже при минимальном типе: список загруженных библиотек, счётчики памяти и число нитей. По этой части в большинстве случаев видно, чей код живёт внутри рабочего процесса и что там произошло с памятью. К ней мы ещё вернёмся, но сначала - как не тратить время на заведомо пустые файлы.
Где взять дампы
Файлы .mdmp пишутся туда, что задано в атрибуте location тега <dump> в logcfg.xml (каталог conf сервера, по умолчанию C:\Program Files\1cv8\conf\). Если location не задан или самого logcfg.xml нет, дампы всё равно складываются в рабочий каталог процесса. Быстрее всего найти их поиском по диску сервера:
Get-ChildItem C:\,D:\ -Recurse -Filter *.mdmp
Скопируйте нужные файлы на свою машину: разбирать их можно на клиенте, права серверной учётной записи не нужны.
Как за десять секунд понять, есть ли у вас пригодные дампы
Открывать файлы для этого не нужно, достаточно посмотреть на имена. Имя дампа устроено так:
rphost_8.3.27.1989_00000000_20260806160704_14720.mdmp
^^^^^^^^ хеш сбоя нулевой - записи об исключении внутри НЕТ
rmngr_8.3.27.1989_6040bd55_20260520170945_6408.mdmp
^^^^^^^^ хеш ненулевой - есть что читать
Восьмизначный шестнадцатеричный токен в имени - это хеш сбоя. Нулевой означает, что внутри пусто; ненулевой - что есть запись об исключении. На всех 354 файлах правило выполнилось без единого промаха: единственный файл с ненулевым хешем оказался единственным же файлом с потоком исключения.
Практический вывод: если во всём каталоге хеши нулевые - там нет ни одного пригодного для разбора дампа, и чинить надо logcfg.xml, а WinDbg тут вообще ни при чём.
Заодно поправлю частое заблуждение. Тот же восьмизначный hex многие принимают за PID. Это не PID. PID - последний числовой токен в имени (в примере это 14720), записан десятичным числом.
Три строки, чтобы следующее падение было разбираемым
Лечится это одним изменением в logcfg.xml - поднять тип дампа до полного:
<dump create="true" type="3" location="D:\1c-dumps"/>
При type="3" в дамп попадает и поток исключения, и контекст нити - то, ради чего вообще открывают дамп. Дальше можно ловить два-три падения и возвращать тип обратно.
Честно про цену. Дамп полного типа занимает столько же, сколько процесс держал памяти. При среднем 2,5-3 ГБ на процесс и двух десятках падений в месяц это порядка 50-60 ГБ в месяц, а один эпизод в наших данных дошёл до 41 ГБ разом. Поэтому location - на отдельный диск, а режим включать временно, под отлов, а не навсегда.
Два момента оставлю как открытые, потому что на своих данных не смог их закрыть до конца, а врать не хочу. Первый: атрибут create="false" стоял, а 354 файла при этом существовали - рабочая гипотеза, что он управляет дампами по обрабатываемым исключениям, а дамп при аварийном завершении пишется независимо. Второй: DumpCount по умолчанию равен 10, но файлов в каталоге куда больше, значит ограничение либо не применяется, либо задано иначе. Если у вас есть точная трактовка по ИТС - это будет полезно.
Как вообще не допускать падений
Это главная часть, и я делю её на три уровня честности: что работает гарантированно, что убирает класс причин, и что выглядит виноватым, но не доказано.
Работает гарантированно. Задайте в свойствах рабочего сервера “Максимальный объём памяти рабочих процессов” и “Безопасный расход памяти за один вызов”. Это не гипотеза: при 31 дампе выше 4 ГБ и рекорде 41 839 МБ лимит превращает аварию в управляемый перезапуск. Падение не исчезает, но перестаёт уносить чужие сеансы. Скорее всего, это же уберёт и большую часть пустышек, файлов нулевого размера: они пустые потому, что на запись дампа памяти уже не осталось.
Тут стоит объяснить, почему лимит важнее, чем кажется. Есть две цифры памяти, и их путают. Резидент (WorkingSet) - то, что процесс физически держит в оперативной памяти, это показывает диспетчер задач. Коммит (PrivateUsage) - то, что процесс попросил у системы и получил, независимо от того, вытеснено оно в подкачку или нет. В одном из наших дампов резидент был 379 МБ, а коммит - 1371 МБ, разница в 3,6 раза. Мониторинг, следящий за резидентом, роста просто не увидит. На другом разборе так и вышло: процесс прибавлял полгигабайта в сутки одиннадцать суток подряд, и заметили это только по коммиту - как снять наклон и что выставить в кластере. А падения по “не удалось выделить память” считаются по коммиту: когда общий лимит (ОЗУ плюс файл подкачки) исчерпан, отказ получает не тот, кто память съел, а тот, кто следующим попросил. Падают посторонние процессы, и по их дампам виновника не видно.
Убирает класс причин. Пятая часть всех падений в наших данных пришлась на 03:00, окно регламентных заданий, и ровно в этих дампах чаще всего встречались IFilter-компоненты (nlhtml.dll, offfilt.dll и подобные). Это библиотеки Windows для разбора вложений при обновлении полнотекстового индекса: чужой файл разбирается сторонним кодом прямо в адресном пространстве rphost, и повреждённое вложение роняет процесс так, что обработчик 1С этому помешать не может. Вынесите обновление полнотекстового индекса в отдельный рабочий процесс или ограничьте типы индексируемых файлов. И общий принцип: не тащите COM и .NET в серверный процесс без нужды, а внешние компоненты подключайте файлом с диска, а не из макета, тогда у них в следующем дампе будут имя и версия.
Выглядит виноватым, но не доказано, и я так и напишу. Среда .NET (clr.dll), WPF и безымянная распакованная компонента присутствовали в 99 % дампов обоих узлов. Соблазн сказать “вот кто их роняет” большой, но присутствие - это не причинность. Библиотека может быть загружена всегда и быть ни при чём. Единственное доказанное падение в наших данных - вообще не в .NET, а в платформенной библиотеке (о нём ниже). Поэтому честная формулировка - “вот кто живёт внутри вашего процесса и почему за этим стоит следить”, а не “вот кто виноват”.
Как читать дамп средствами 1С, без WinDbg
Здесь многие останавливаются, потому что думают, что нужен WinDbg и отладочные символы. Для триажа - понять, чей код в процессе и куда смотреть дальше - хватает средств самой 1С. Дамп в формате MINIDUMP это обычные двоичные данные с простой структурой:
Заголовок: +0 сигнатура U32 = 'MDMP' · +8 число потоков · +12 адрес каталога
Каталог: записи по 12 байт - тип потока U32, размер U32, смещение U32
Поток 3 ThreadList: число нитей
Поток 4 ModuleList: число модулей + записи по 108 байт (база, размер, имя, версия)
Поток 6 Exception: код исключения (+8), адрес сбоя (+24) - его-то и нет при type=0
Поток 7 SystemInfo: архитектура, версия ОС
Поток 22 VmCounters: PeakWorkingSet, WorkingSet, PrivateUsage (+72)
Прочитать сигнатуру и каталог потоков средствами встроенного языка - вот столько кода:
// заголовок - первые 32 байта; сигнатура по смещению 0 должна быть MDMP
Шапка = ПрочитатьОбласть(Чтец, 0, 32);
Если Шапка.ПрочитатьЦелое32(0) <> 1347241037 Тогда // 'MDMP' как беззнаковое целое
ВызватьИсключение "Это не minidump";
КонецЕсли;
ЗаписейКаталога = Шапка.ПрочитатьЦелое32(8); // число потоков данных в файле
RvaКаталога = Шапка.ПрочитатьЦелое32(12); // где лежит каталог потоков
// каталог: записи по 12 байт - тип потока, размер, смещение
Каталог = ПрочитатьОбласть(Чтец, RvaКаталога, ЗаписейКаталога * 12);
Для Номер = 0 По ЗаписейКаталога - 1 Цикл
Смещение = Номер * 12;
ТипПотока = Каталог.ПрочитатьЦелое32(Смещение);
РазмерПотока = Каталог.ПрочитатьЦелое32(Смещение + 4);
RvaПотока = Каталог.ПрочитатьЦелое32(Смещение + 8);
Сообщить("Поток " + ТипПотока + ", " + РазмерПотока + " байт");
КонецЦикла;
ПрочитатьОбласть - это чтение куска из буфера с проверкой границ (любой битый адрес превращается в “нет данных” по одному показателю, а не роняет разбор целиком). Дальше по каталогу видно, какие потоки в файле есть. Нет потока 6 - причину не назвать, и это ровно тот случай type="0", с которого мы начали. Есть поток 4 - читаем список модулей и по путям раскладываем их на “платформа 1С”, “Windows”, “среда .NET”, “сторонняя компонента”.
Пара граблей встроенного языка, на которых тут легко обжечься. ПрочитатьЦелое32 у платформы беззнаковое, и поле версии однажды вернулось как 2 700 191 635 - больше 2^31, так что знаковую интерпретацию надо разворачивать в беззнаковую. Имена модулей лежат как MINIDUMP_STRING (длина в байтах, дальше UTF-16LE) - читать их посимвольно на двух сотнях модулей в каждом из пятисот файлов нельзя, это миллионы вызовов; быстрый путь - ПолучитьСтрокуИзДвоичныхДанных(..., "UTF-16LE").
Единственный настоящий дамп. Тот самый файл с ненулевым хешем: процесс rmngr (менеджер кластера), код 0xC0000005 ACCESS_VIOLATION, чтение по адресу 0xFFFFFFFF, и упало это внутри платформенной библиотеки самой 1С. То есть лёг не рабочий процесс, а менеджер кластера, а с ним весь узел, со всеми базами и пользователями. Адрес 0xFFFFFFFF - классический признак работы с указателем, который уже не действителен. Вывод “упало в платформе” меняет адресата: это тот случай, когда обращаться надо именно в поддержку 1С, приложив этот файл, а не искать ошибку у себя в конфигурации. И это же лучший аргумент за смену типа сбора: один пригодный дамп дал больше, чем остальные 353 вместе взятые.
Шпаргалка: признак - диагноз - действие
| Признак в дампе | Что значит | Что сделать |
|---|---|---|
| Хеш в имени нулевой, потока 6 нет | Записи об исключении внутри нет, причину не назвать | Поднимите type до 3 в logcfg.xml, поймайте два-три падения |
| Файл нулевого размера | Процесс завершился так аварийно, что дамп не дописался - обычно нехватка памяти | Задайте лимит памяти рабочих процессов; смотрите долю таких файлов как симптом |
| Коммит сильно выше резидента | Память выделена и вытеснена в подкачку - утечка, которую мониторинг по резиденту не видит | Следите за PrivateUsage, а не за WorkingSet |
| Максимум памяти в десятки ГБ | Роста памяти ничто не ограничивает | Поставьте “Максимальный объём памяти рабочих процессов” |
Процесс - rmngr или ragent | Упал не сеанс, а весь узел кластера | Разбирайте в первую очередь; при падении в платформе - в поддержку 1С |
| Среда .NET и WPF почти в каждом дампе | В процесс затянут чужой управляемый код; вина не доказана | Найдите источник (Новый COMОбъект, ПодключитьВнешнююКомпоненту), компоненту - файлом с диска |
Безымянная <guid>.tmp версии 0.0.0.0 | Внешняя компонента, распакованная из макета: ни имени, ни версии | Подключайте компоненту по пути, чтобы в дампе были имя и версия |
sqlncli11.dll в большинстве дампов | Прямой доступ к СУБД через ADO мимо платформы, драйвер снят с поддержки | Найдите ADODB.Connection, замените на Microsoft OLE DB Driver for SQL Server |
| IFilter-библиотеки, пик падений ночью | Полнотекстовый индекс разбирает вложения сторонним кодом в rphost | Вынесите обновление индекса в отдельный процесс или ограничьте типы файлов |
И маленький словарь кодов, которые встречаются в потоке 6 чаще всего:
0xC0000005 ACCESS_VIOLATION обращение по недоступному адресу (чаще всего - битый указатель)
0xC00000FD STACK_OVERFLOW переполнение стека, обычно бесконечная рекурсия
0xC0000374 HEAP_CORRUPTION повреждение кучи, часто чужой компонентой
0x80000003 BREAKPOINT сработала точка останова / assert внутри библиотеки
Коротко
- Дампы по умолчанию снимаются минимальным типом - записи об ошибке в них нет, и WinDbg тут бессилен не по вашей вине.
- Проверьте
logcfg.xmlдо следующего падения, иначе весь архив окажется бесполезным. - Хеш в имени файла за десять секунд говорит, есть ли внутри что читать.
- Формат MINIDUMP читается средствами встроенного языка 1С, без WinDbg и отладочных символов - для триажа этого достаточно.
- Поставьте лимиты памяти рабочих процессов - это превращает аварию в перезапуск и заодно убирает пустышки.
- Считайте инциденты, а не файлы, и следите за коммитом, а не за резидентом.
Последний пункт шире дампов. Падения, ошибки проведения, обрывы фоновых заданий одинаково живут в состоянии “формально видно, фактически нет”: они лежат в журнале, который никто не читает, и потому не чинятся годами. Что даёт переход от “файлы копятся” к “инциденты посчитаны”, мы разбирали на своих цифрах в статье “Почему ошибки в 1С не чинятся”.
Готовые обработки по теме
Разбирать двоичный формат руками каждый раз не нужно - но диагностика сервера и СУБД этим не заканчивается. Наши инструменты по соседней теме, все на Инфостарте:
- Карта объёмов базы 1С - из чего состоит база и где лишний вес;
- Чек-ап СУБД под 1С - правильно ли SQL Server настроен под 1С;
- Трансформатор SQL в запрос 1С - что делает конкретный медленный запрос;
- Аудит паролей СУБД - извлекаемость пароля СУБД из файла кластера.
Если сервер падает регулярно, а разбираться с дампами и памятью некому, мы делаем оптимизацию и диагностику 1С как услугу - от чтения дампов до настройки лимитов и разгрузки регламентных заданий.