Профилируем использование памяти в программах на C/C++ при помощи Heaptrack

7 ноября 2016

Рассмотрим типичную задачу. Есть программа на C или C++ с исходниками. Известно, что при выполнении определенных условий программа начинает отжирать слишком много памяти. Нужно понять, почему это происходит, и по возможности исправить. Инструменты, которые мы рассматривали до этого, например, в заметке Профилирование кода на C/C++ в Linux и FreeBSD, для этого явно не подходят. Спрашивается, что же тогда делать? На помощь приходит Heaptrack!

Примечание: Heaptrack является аналогом Valgrind Massif, только, в отличие от него, работает намного быстрее. См также заметку Поиск ошибок работы с памятью в C/C++ при помощи Valgrind.

Для установки Heaptrack я воспользовался этим AUR (см заметку Управление пакетами в Arch Linux с помощью ABS и pacman). Также нам понадобятся Massif Visualizer и его зависимость KDiagram, которые также доступны в AUR. Если вы используете дистрибутив, отличный от Arch Linux, уверен, для него в каком-то виде данные пакеты тоже есть. В крайнем случае, можно посмотреть файлы PKGBUILD у Арча и собрать все из исходников таким же образом. Насколько мне известно, в настоящее время Heaptrack работает только под Linux. Поэтому, если вы используете другую ОС… ну что ж, хреново быть вами :)

Пользоваться Heaptrack очень просто. Для примера натравим его на программку из Не унылого поста о списках и деревьях поиска в языке C:

heaptrack ./test_rbtree
# или: heaptrack -p PID

heaptrack_print --print-leaks \
  --print-histogram histogram.data \
  --print-massif massif.data \
  --print-flamegraph flamegraph.data \
  --file ./heaptrack.test_rbtree.22023.gz > report.txt

Ключ --print-leaks включает вывод информации о потенциальных утечках памяти (отключено по умолчанию), --print-histogram включает запись статистики по размерам выделенной памяти, а --print-massif создает файл, пригодный для просмотра в Massif Visualizer. Что делат ключ --print-flamegraph попробуйте угадать сами :) Файл flamegraph.data будет использован чуть ниже.

Полный пример отчета можно посмотреть здесь. Наиболее интересная его часть:

PEAK MEMORY CONSUMERS

4.98MB peak memory consumed over 31111 calls from
tree_allocfunc
  at /home/eax/projects/c/c-algorithms/test/struct/test_rbtree.c:37
  in /home/eax/projects/c/c-algorithms/build/test/struct/test_rbtree
1.24MB consumed over 7777 calls from:
  rb_insert
    in /home/eax/projects/c/c-algorithms/build/test/struct/test_rbtree
  left_right_walk_test
    at /home/eax/projects/c/c-algorithms/test/struct/test_rbtree.c:166
    in /home/eax/projects/c/c-algorithms/build/test/struct/test_rbtree
  run_tests
    at /home/eax/projects/c/c-algorithms/test/struct/test_rbtree.c:430
    in /home/eax/projects/c/c-algorithms/build/test/struct/test_rbtree
  main
    at /home/eax/projects/c/c-algorithms/test/struct/test_rbtree.c:458
    in /home/eax/projects/c/c-algorithms/build/test/struct/test_rbtree

Вполне похоже на правду! Действительно, почти вся память должна выделяться во время вставки в RB-дерево.

Если текстовый отчет вам не кажется слишком наглядным, попробуйте запустить Massif Visualizer:

massif-visualizer massif.data

Выглядит он приблизительно так (кликабельно, PNG 1366x747, 224 Кб):

Massif Visualizer

Кроме того, можно построить флеймграф:

git clone https://github.com/brendangregg/FlameGraph
./FlameGraph/flamegraph.pl flamegraph.data > flamegraph.svg

На красивые флеймграфы мы смотрели уже тысячу раз, так что картинку здесь не привожу.

Собственно, это все! А чем вы профилируете использование оперативной памяти?

Метки: , , .


Вы можете прислать свой комментарий мне на почту, или воспользоваться комментариями в Telegram-группе.