Когда-то, когда активно изучал аллокаторы, обнаружил в недрах интернета такую замечательную штуку, как статический лист для сбора информаций об аллокациях. Давайте сначала посмотрим на то, как это выглядит, а потом почему это круто.
Заведём структурку, в которую будем складывать необходимую информацию:
struct AllocationInfo {
const char* function;
unsigned int line;
unsigned long count;
unsigned long bytes;
AllocationInfo* next;
};Это базовая нода листа, в которой будем хранить всю необходимую информацию об аллокации. Ну и заведём голову листа:
static AllocationInfo root;Теперь напишем макрос, который будет служить базой для функции аллокации:
#define MYALLOCATE(n, name) \
[name_data = name] (size_t bts) { \
static AllocationInfo here; \
static bool firstCall = [name_data]() { \
/*first call, initialize the struct*/ \
here.function = name_data; \
here.line = __LINE__; \
here.next = root.next; \
root.next = &here; \
return true; \
}(); \
/* Cool, now deposit info about calls */ \
++here.count; \
here.bytes += (bts); \
return malloc(bts); \
}(n)Создаём новую ноду листа, которую инициализируем при первом появлении в строчке, после чего обновляем инфу если надо. Ну и макрос-обёртка:
#define ALLOC(n) MYALLOCATE(n, __PRETTY_FUNCTION__)Теперь везде где хочется выделять память теперь будем использовать макрос
ALLOC. И в конце можем обойти лист, получим всю необходимую информацию:auto p = &root;
p = p->next;
while (p) {
std::cout << "Func: " << p->function << std::endl;
std::cout << "Line: " << p->line << std::endl;
std::cout << "Times: " << p->count << std::endl;
std::cout << "Bytes: " << p->bytes << std::endl;
p = p->next;
}Демо. И можем увидеть какие-то такие результаты:
Func: int main()
Line: 46
Times: 1
Bytes: 128
Func: void some_func(size_t)
Line: 36
Times: 2
Bytes: 48
Func: int main()
Line: 40
Times: 1
Bytes: 8Видим, что в
main в строке 46 за один раз было выделено 128 байт; в some_func в строке 36 за два раза 48 байт и в main в строке 40 за раз 8 байт. Количество раз для каждой строки мы считаем т.к. используются статические переменные для нод листа, из-за чего при нескольких обращениях ноды переиспользуются. Можно складывать сюда таймпоинты выделений памяти или что угодно ещё. Прикона короч.
И самый сок, что весь лист у вас
static, т.е. у нас нет динамических аллокаций на него. Только те, которые лежат под вызовом ALLOC. Т.е. информация категорически точная. Мне когда-то голову взорвало.
Можно немножко зарефакторить код, юзая
std::source_location. Оставим как упражнение пытливому читателю. ===================================
Что-то в последнее время начала ощущаться усталость от 2.5 лет нехождения в отпуск. Потому дольше обычного не было постов (ну и ещё из-за запойного просмотра Доктора Хауса). Но, думаю, скоро получится отдохнуть и всё вернётся в нормальный ритм.