Есть ли способ определить, какая часть процесса использовала большую часть памяти, глядя только на сгенерированный файл ядра?
У меня есть процесс (который запускается сторожем каждый раз, по какой-то причине он останавливается), который обычно использует около 200 МБ памяти. Однажды я увидел, что он поглощает память - с использованием памяти около 1,5-2 ГБ, что определенно означает "утечку памяти" где-то ( "утечка памяти" в кавычках, поскольку это не настоящая утечка памяти - как выделенная память, никогда не освобождаемая и недоступен - обратите внимание, что используются только умные указатели. Итак, я думаю о каком-то огромном контейнере (я не нашел) или о чем-то вроде этого)
Позже процесс завершился сбоем из-за высокого использования памяти и создания дампа ядра - около 2 ГБ. Но проблема в том, что я не могу воспроизвести проблему, поэтому valgrind
здесь не поможет (наверное). Это случается очень редко, и я не могу "поймать" это.
Итак, мой вопрос - есть ли способ, используя exe и файл core, чтобы определить, какая часть процесса использовала большую часть памяти?
Я посмотрел на основной файл с gdb
Нет ничего необычного. Но ядро большое, поэтому должно быть что-то. Есть ли умный способ понять, что произошло, или только догадки могут помочь (но для таких больших exe.., 12 потоков, около 50-100 (может быть больше) классов и т. Д., И т. Д.)
Это C++
приложение, работающее на RHEL5U3.
4 ответа
Откройте этот coredump в шестнадцатеричном формате (как байты / слова / слова / слова). Начиная с середины файла, попробуйте заметить любой повторяющийся узор. Если что-то найдено, попробуйте определить начальный адрес и длину некоторой возможной структуры данных. Используя длину и содержание этой структуры, попытайтесь угадать, что это может быть. Используя адрес, попробуйте найти какой-нибудь указатель на эту структуру. Повторяйте, пока не дойдете до стека или какой-либо глобальной переменной. В случае переменной стека вы легко узнаете, в какой функции запускается эта цепочка. В случае глобальной переменной вы знаете, по крайней мере, ее тип.
Если вы не можете найти какой-либо шаблон в coredump, есть вероятность, что структура утечки очень велика. Просто сравните то, что вы видите в файле, с возможным содержанием всех больших структур в программе.
Обновить
Если ваш coredump имеет действительный стек вызовов, вы можете начать с проверки его функций. Ищите что-нибудь необычное. Проверьте, не выделяет ли слишком много памяти память в верхней части стека вызовов. Проверьте возможные бесконечные циклы в функциях стека вызовов.
Слова "используютсятолько умные указатели" пугают меня. Если значительная часть этих интеллектуальных указателей является общими указателями (shared_ptr, intrusive_ptr, ...), вместо поиска огромных контейнеров стоит искать циклы общего указателя.
Обновление 2
Попробуй определить, где твоя куча заканчивается в corefile (brk
значение). Запустите coredumped процесс под GDB и используйте pmap
команда (с другого терминала). GDB также должен знать это значение, но я не знаю, как его спросить... Если большая часть памяти процесса выше brk
Вы можете ограничить свой поиск большими выделениями памяти (скорее всего, std::vector).
Чтобы повысить вероятность обнаружения утечек в области кучи существующего coredump, можно использовать некоторое кодирование (я сам этого не делал, просто теория):
- Прочитайте файл coredump, интерпретируя каждое значение как указатель (игнорируйте сегмент кода, невыровненные значения и указатели на область без кучи). Сортировка списка, вычисление различий соседних элементов.
- На этом этапе вся память разбита на множество возможных структур. Вычислить гистограмму размеров структуры, отбросить любые незначительные значения.
- Рассчитайте разницу адресов указателей и структур, к которым относятся эти указатели. Для каждого размера структуры вычислите гистограмму смещения указателей, снова отбросьте все незначительные значения.
- Теперь у вас достаточно информации, чтобы угадать типы структур или построить ориентированный граф структур. Найти исходные узлы и циклы этого графа. Вы даже можете визуализировать этот график как "список" холодных "областей памяти".
Файл Coredump находится в elf
формат. От заголовка требуется только начало и размер сегмента данных. Чтобы упростить процесс, просто прочитайте его как линейный файл, игнорируя структуру.
Однажды я увидел, что он поглощает память - с использованием памяти около 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()
ошибочно возвращает ложь при некоторых условиях, ваш процесс будет быстро расти, чтобы использовать всю доступную память.
используются только умные указатели
Если вы не контролируете весь код, связанный с процессом, приведенное выше утверждение ложно. Цикл ошибки может быть внутри libc
или любую другую библиотеку, которой вы не владеете.
только догадки могут помочь
Вот и все. Ответ Евгения имеет хорошие отправные точки, которые помогут вам угадать.
Нормальные распределители памяти не отслеживают, какой части процесса выделена память - в конце концов, память все равно будет освобождена, а указатели удерживаются клиентским кодом. Если память действительно просочилась (т. Е. Нет указателей на нее), вы в значительной степени потеряли и смотрите на огромный блок неструктурированной памяти.
Valgrind
вероятно найдет несколько возможных ошибок, и стоит проанализировать все из них. Вам нужно создать файл подавления и использовать его следующим образом --suppressions=/path/to/file.supp
, За каждую возможную ошибку, valgrind
flags, либо добавьте предложение в файл подавления, либо измените вашу программу.
Ваша программа будет работать медленнее в Valgrind
и поэтому время событий будет другим, поэтому вы не можете быть уверены, что ваша ошибка произошла.
Существует графический интерфейс для Valgrind под названием Alleyoop, но я не использовал его много.