Комментарии 7

В коде malloc увидел следующее:
RestoreHooks
Malloc
Sethooks


Выглядит адски потоконебезопасно. Будет работать так:
Первый поток входит в ownmalloc, отрубает хук, собирается выделить память.
Второй поток пытается саллоцировать память, хук отключен, юзает malloc напрямую, нарушая консистентность следилки
Первый поток аллоцирует память и включает хук обратно.


Звучит так себе, особенно когда оно будет регистрировать таким образом free, который пролетел мимо хука.


Мы используем у себя два метода:


  1. Своя стандартная библиотека ( https://github.com/codemeow/bixi/tree/master/code/libbixi ), которая имеет возможность установить кастомный аллокатор, через который идут все функции (stdlib не используется).
  2. Ld_preload с библиотекой, реализующей кастомные аллокаторы, позволяет перехватывать даже stdlib.

Описанный в статье метод нееффективен и непотокобезопасен.

Действительно представленное описание было ориентировано на использование средств встроенного контроля в контексте однопоточного исполнения. Но ничто не ограничивает в указанных функциях установить блокировки вида:

#include
#include
#include
#include
….
std::mutex g_lock;
void ProtectedFunction()
{
g_lock.lock ();
//делаем в защищенном режиме.
g_lock.unlock();
}
… и обрабатывать запросы к памяти в защищенном режиме.
В отношении кастомного аллокатора памяти – нет вопросов, если выполняется разработка нового программного обеспечения. В тех же случаях, когда сопровождается и развивается приложение с глубокой “историей” и большим контингентом пользователей на такой риск вряд ли кто решится.
В отношении высказанного (правда ничем не подтвержденного) утверждения о неэффективности описанного подхода можно лишь заметить, что последняя картинка в публикации относится к профилированию динамической памяти в очень сложном приложении, в котором на пятые сутки непрерывного исполнения обнаруживалась ситуация исчерпания ресурсов памяти с последующим аварийным завершением. Это простенькое средство контроля позволило выявить причины.
Так что, не претендуя на универсальность это средство приносит пользу.
Но ничто не ограничивает в указанных функциях установить блокировки вида:

Вот только скорость работы такой штуки будет низкой. Выделение памяти — очень небыстрая операция, а вы это еще и только в одном потоке собираетесь делать.

В тех же случаях, когда сопровождается и развивается приложение с глубокой “историей” и большим контингентом пользователей на такой риск вряд ли кто решится.

В таких случаях используется LD_PRELOAD и код «монстра» никак не модифицируется. Простейший пример можно посмотреть например тут: github.com/codemeow/c-heetah#call-as-library
Возможно я не все понял, но два вопроса все-таки возникают.
1) при выявлении причин нехватки памяти время исполнения не столь критично в этом режиме (собственно алгоритмы берут на 2-3 порядка больше времени). Важны результаты, а именно: какая память в приложении не освобождается и накапливается.
2) как я понимаю предлагаемый аллокатор контролирует и включает в отчет утечки памяти и пр.? Если это так и он внешний, то какой размер отчета я получаю по всему приложению? А это не требуется. В подавляющем большинстве случаев известен один (или несколько) исполняемых потоков операций (not threading), где необходимо искать проблему. Поэтому локальные вставки макросов и дают урезанный лог. Конечно, вопрос спорный. Но вроде как такая практика приносит свои положительные результаты наряду с высказанными вами негативными сторонами: замедление, инструментовка исходников для извлечения данных о памяти
а можно же просто переопределить глобальные operator new/delete...?

По идее, в с++ коде не должно быть выделений памяти через malloc
В C++ коде не должно, а в статических библиотеках new/delete уже не переопределишь. Ну и если часть кода — древнее legacy, то ему может быть не так уж и важно, кто кому чего должен.
Если вернуться к показанным в публикации фрагментам кода из исходника memorysupervisor.cpp можно увидеть, что показанные там new операторы перехватываются. Как я понимаю это результат того, что их реализация выполнена через malloc/calloc. К сожалению не представил исполнение delete оператора. Но имею подозрения, что его реализация базируется на использовании вызова free(). Поэтому установление крючков — перехватчиков к malloc/calloc/realloc/free обеспечивает перехват и запросов по new/delete.
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.