Есть ли способ определить, какая часть процесса использовала большую часть памяти, только глядя на сгенерированный файл ядра?
У меня есть процесс (который запускается сторожевой собакой каждый раз, по какой-то причине он останавливается), который обычно использует память около 200 МБ. Как только я увидел, что он ел память - с объемом памяти около 1,5-2 ГБ, что определенно означает "утечка памяти" где-то ( "утечка памяти" в кавычках, поскольку это не настоящая утечка памяти - как выделенная память, strong > и unreachable - обратите внимание, что используются только интеллектуальные указатели. Поэтому я думаю о каком-то огромном контейнере (я не нашел) или что-то вроде этого)
Позже процесс разбился, из-за высокой загрузки памяти и дампа ядра было создано около 2 ГБ. Но проблема в том, что я не могу воспроизвести проблему, поэтому valgrind
здесь не поможет (я думаю). Это происходит очень редко, и я не могу "поймать" его.
Итак, мой вопрос: есть ли способ, используя exe и основной файл, определить, какая часть процесса использовала большую часть памяти?
Я взглянул на основной файл с gdb
, там ничего необычного. Но ядро большое, так что должно быть что-то. Есть ли разумный способ понять, что произошло, или только угадывание может помочь (но для такого большого exe.., 12 потоков, около 50-100 (может быть больше) классов и т.д. И т.д.)
Это приложение C++
, работающее на RHEL5U3.
Ответы
Ответ 1
Откройте этот coredump в шестнадцатеричном формате (в виде bytes/words/dwords/qwords). Начиная с середины файла, попробуйте заметить любой повторяющийся шаблон. Если что-то найдено, попробуйте определить начальный адрес и длину некоторой возможной структуры данных. Используя длину и содержимое этой структуры, попробуйте угадать, что это может быть. Используя адрес, попробуйте найти некоторый указатель на эту структуру. Повторяйте до тех пор, пока не попадете в стек или какую-либо глобальную переменную. В случае переменной стека вы легко узнаете, в какой функции запускается эта цепочка. В случае глобальной переменной вы знаете, по крайней мере, ее тип.
Если вы не можете найти какой-либо шаблон в coredump, вероятность того, что просачивающаяся структура будет очень большой. Просто сравните то, что вы видите в файле, с возможным содержимым всех больших структур в программе.
Обновление
Если ваш coredump имеет действительный стек вызовов, вы можете начать с проверки его функций. Искать что-нибудь необычное. Проверьте, не слишком ли запрашиваются распределения памяти в верхней части стека вызовов. Проверьте возможные бесконечные циклы в функциях стека вызовов.
Слова " используются только умные указатели" пугают меня. Если значительная часть этих интеллектуальных указателей является общим указателем (shared_ptr, intrusive_ptr,...), вместо поиска огромных контейнеров стоит искать общие циклы указателей.
Обновление 2
Попробуйте определить, где ваша куча заканчивается в файле corefile (brk
value). Запустите обработанный процесс под gdb и используйте команду pmap
(с другого терминала). gdb также должен знать это значение, но я не знаю, как его спросить... Если большая часть памяти процесса находится выше brk
, вы можете ограничить свой поиск большими выделениями памяти (скорее всего, std::vector).
Чтобы улучшить шансы найти утечки в области кучи существующего корунда, может использоваться некоторая кодировка (я не делал этого сам, просто теория):
- Прочитайте файл coredump, интерпретируя каждое значение как указатель (игнорируйте сегмент кода, значения без знака и указатели на область без кучи). Сортировка списка, вычисление различий соседних элементов.
- В этот момент вся память разделяется на многие возможные структуры. Вычислите гистограмму размеров структуры, снимите любые незначительные значения.
- Вычислить разницу адресов указателей и структур, в которых эти указатели принадлежат. Для каждого размера структуры вычислите гистограмму смещения указателей, снова снимите любые незначительные значения.
- Теперь у вас достаточно информации, чтобы угадать типы структуры или построить ориентированный граф структур. Найдите исходные узлы и циклы этого графика. Вы даже можете визуализировать этот график, как в "списке" холодных "областях памяти" .
Файл Coredump находится в формате elf
. Из заголовка требуется только начало и размер сегмента данных. Чтобы упростить процесс, просто прочитайте его как линейный файл, игнорируя структуру.
Ответ 2
Как только я увидел, что он ел память - с объемом памяти около 1,5-2 ГБ
Довольно часто это был бы конечный результат ошибки, сбивающейся с пути. Что-то вроде:
size_t size = 1;
p = malloc(size);
while (!enough_space(size)) {
size *= 2;
p = realloc(p, size);
}
// now use p to do whatever
Если enough_space()
ошибочно возвращает false при некоторых условиях, ваш процесс будет быстро расти, чтобы потреблять всю доступную память.
используются только интеллектуальные указатели
Если вы не контролируете весь код, связанный с этим процессом, оператор выше false. Цикл ошибок может находиться внутри libc
или любой другой библиотеки, которой вы не являетесь.
только гадание может помочь
Это в значительной степени. Ответ Евгения имеет хорошие отправные точки, чтобы помочь вам догадаться.
Ответ 3
Нормальные распределители памяти не отслеживают, какая часть распределенной памяти процесса - в конце концов, память все равно будет освобождена, а указатели удерживаются кодом клиента. Если память действительно просочилась (т.е. Нет указателей на нее), вы в значительной степени потеряли и смотрите на огромный блок неструктурированной памяти.
Ответ 4
Valgrind
, вероятно, найдет несколько возможных ошибок, и стоит проанализировать их все. Вам нужно создать файл подавления и использовать его как --suppressions=/path/to/file.supp
. Для каждой возможной ошибки, что флаги Valgrind
, либо добавить предложение в файл подавления, либо изменить вашу программу.
В Valgrind
ваша программа будет работать медленнее, поэтому время событий будет иным, поэтому вы не можете быть уверены в том, что увидите свою ошибку.
Существует графический интерфейс для valgrind, называемый Alleyoop, но я не использовал его много.