Я системный администратор, мое хобби разработка. В качестве инструментов я использую языки C, PHP, JS.
Речь пойдет о проекте на языке C (окружение POSIX).
Имеем классическую архитектуру — микроядро и модули. Ядро обладает базовым функционалом — серверная часть, клиентская часть, загрузка и выгрузка модулей, добавление и удаление команд, чтение и анализ конфигурационных файлов, динамические строки, динамические списки, цепочки. Модуль должен обеспечить прикладные возможности программы. В качестве исключения существуют модули-библиотеки, которые содержат наборы функций, расширяя возможности ядра (в дальнейшем если я говорю ядро, я подразумевая ядро программы).
Загрузка модуля, может быть осуществлена пользователем, через командную строку, или по команде в конфигурационном файле. При загрузке модуля, модуль сам регистрирует себя в списке модулей, добавляет набор команд командной строки, регистрирует глобальные функции. В зависимости от назначения модуля, модуль может создавать множество потоков, устанавливать сетевые соединения, переходить в режим прослушивания сокетов и так далее. При выгрузке, включается глобальный флаг останова модуля, модуль переходит в режим ожидания завершения потоков (pthread_join), после чего освобождается память, удаляются зарегистрированные ранее команды, модуль убирает себя из списка модулей.
Несмотря на то, что программа хорошо разделена на логические составляющие, отладка даже одного модуля, с множеством потоков, динамических объектов, асинхронными событиями, может быть затруднена. До последнего момента у меня не было отладочных инструментов внутри программы.
Процесс разработки примерно такой: я разрабатываю какую-то часть кода в отдельной песочнице, затем интегрирую ее в какой-то модуль. В песочнице отладка проходит довольно просто. Можно выводить промежуточные значения прямо в консоль. Утечки памяти можно тестировать утилитой valgrind (если ей можно тестировать многопоточные приложения научите меня как).
Приятно, когда три месяца работы оканчиваются работающей программой. Но мое счастье длилось не долго. Программа падала, при выгрузке модуля. На тот момент, я совершенно не контролировал память, я не знал сколько ее выделено и кем, я не знал сколько памяти остается неутилизированной. Вышеупамянутая утилита valgrind мне ничем помочь не могла. Отладчиком, когда ты не знаешь, что ищешь, проблему тоже решить сложно. Я понял, что мне нужно вести учет памяти.
Не долго думая, я обратился к исходному коду Asterisk, я исследовал как обстоят у них дела в этом направлении. Ниже идет описание отладки памяти в астериске, в свою программу я перенес ее практически без изменения.
Управление памятью (memory managment) в астериске заключено в файлах main/utils.c и main/astmm.c. Которое можно разделить на две ветки — управление в обычном режиме, и в режиме отладки. В обычном режиме, все довольно просто, стандартные обертки над malloc, calloc, etc с проверкой на ошибку. А вот в режиме отладки, механизм немного другой.
После внедрения себе этого механизма, я понял, что это крайне удобно. В реальном времени, всегда можно посмотреть не увеличивается ли память, не остается ли память после загрузки выгрузки объектов. Если память подтекает, можно посмотреть какая функция генерирует утечку.
В тот же день, я обнаружил у себя утечку, через два дня я смог ее устранить.
В первый раз пишу, что-то подобное, надеюсь не в последний.
Спасибо за внимание, все замечания приветствуются.
Речь пойдет о проекте на языке C (окружение POSIX).
Имеем классическую архитектуру — микроядро и модули. Ядро обладает базовым функционалом — серверная часть, клиентская часть, загрузка и выгрузка модулей, добавление и удаление команд, чтение и анализ конфигурационных файлов, динамические строки, динамические списки, цепочки. Модуль должен обеспечить прикладные возможности программы. В качестве исключения существуют модули-библиотеки, которые содержат наборы функций, расширяя возможности ядра (в дальнейшем если я говорю ядро, я подразумевая ядро программы).
Загрузка модуля, может быть осуществлена пользователем, через командную строку, или по команде в конфигурационном файле. При загрузке модуля, модуль сам регистрирует себя в списке модулей, добавляет набор команд командной строки, регистрирует глобальные функции. В зависимости от назначения модуля, модуль может создавать множество потоков, устанавливать сетевые соединения, переходить в режим прослушивания сокетов и так далее. При выгрузке, включается глобальный флаг останова модуля, модуль переходит в режим ожидания завершения потоков (pthread_join), после чего освобождается память, удаляются зарегистрированные ранее команды, модуль убирает себя из списка модулей.
Несмотря на то, что программа хорошо разделена на логические составляющие, отладка даже одного модуля, с множеством потоков, динамических объектов, асинхронными событиями, может быть затруднена. До последнего момента у меня не было отладочных инструментов внутри программы.
Процесс разработки примерно такой: я разрабатываю какую-то часть кода в отдельной песочнице, затем интегрирую ее в какой-то модуль. В песочнице отладка проходит довольно просто. Можно выводить промежуточные значения прямо в консоль. Утечки памяти можно тестировать утилитой valgrind (если ей можно тестировать многопоточные приложения научите меня как).
Приятно, когда три месяца работы оканчиваются работающей программой. Но мое счастье длилось не долго. Программа падала, при выгрузке модуля. На тот момент, я совершенно не контролировал память, я не знал сколько ее выделено и кем, я не знал сколько памяти остается неутилизированной. Вышеупамянутая утилита valgrind мне ничем помочь не могла. Отладчиком, когда ты не знаешь, что ищешь, проблему тоже решить сложно. Я понял, что мне нужно вести учет памяти.
Здесь у меня будут слова благодарности. Я хочу поблагодарить сообщество asterisk.org, эти парни пишут потрясающую программу, я многому у них научился, и я взял у них много кода. Спасибо!
Не долго думая, я обратился к исходному коду Asterisk, я исследовал как обстоят у них дела в этом направлении. Ниже идет описание отладки памяти в астериске, в свою программу я перенес ее практически без изменения.
Управление памятью (memory managment) в астериске заключено в файлах main/utils.c и main/astmm.c. Которое можно разделить на две ветки — управление в обычном режиме, и в режиме отладки. В обычном режиме, все довольно просто, стандартные обертки над malloc, calloc, etc с проверкой на ошибку. А вот в режиме отладки, механизм немного другой.
- создаются макросы (обертки) для всех стандартных функций управления памятью
- при каждом выделении памяти создается заголовочный блок, где указывается размер, из какого файла вызвали функцию, какую функцию и какую строку
- весь выделенный блок обносится забором (fence) — магические числа в начале и в конце данных, для выявления переполнения буфера
- информация о блоке помещается в хеш-таблицу, чтобы не создавать больших задержек при работе с памятью в режиме отладки
- в консоль добавляется две команды memory show и memory summary. Первая показывает информацию обо всех блоках. Вторая показывает суммарную информацию по файлам
После внедрения себе этого механизма, я понял, что это крайне удобно. В реальном времени, всегда можно посмотреть не увеличивается ли память, не остается ли память после загрузки выгрузки объектов. Если память подтекает, можно посмотреть какая функция генерирует утечку.
В тот же день, я обнаружил у себя утечку, через два дня я смог ее устранить.
В первый раз пишу, что-то подобное, надеюсь не в последний.
Спасибо за внимание, все замечания приветствуются.