Разбираемся с утечками
Oct. 13th, 2021 04:29 pm![[personal profile]](https://www.dreamwidth.org/img/silk/identity/user.png)
Занялся полным рефакторингом кода системы управления оптоволоконным спектрографом. А глаз уже "замыленный": ну вот не могу сходу обнаружить, где я забываю free() сделать (тем более, что у меня там аллокаторы многоуровневые: вызываешь одну функцию, она — другую, и лишь в третьей вызывается аллокатор, поди-как обнаружь косяк). Вот и решил накидать макросов для логгирования проблемных мест.
Сделал надстройку над макросами аллокаторов и free, debug.h:
И вот, что находится в debug.c:
Переопределил еще макрос FNAME, да добавил макросы _LOG и DBGLOG, чтобы можно было еще дополнительно что-нибудь в этот файл писать, да при необходимости помечать функции, в которые заходит в ходе выполнения.
Для анализа выхлопа DEBUG.log написал простой скриптик:
Запустил немного погонять, и что я вижу:
Рукожопие мое зело велико! На 4080 аллокаторов всего лишь 3906 освобождений памяти! И за несколько минут натекло аж 28.5МБ памяти! То-то оно и дохло через несколько часов работы… Интересно, что "потеряшек" всего с полпроцента от общей выделяемой памяти, т.е. теряется где-то в мелочевке (скажем, выделил память под объект, выделил память под его содержимое; содержимое освободил, а объект - нет). Ну и нужно проверить, успевают ли логгироваться все выделения/освобождения памяти в потоках openmp (дело в том, что у меня для записи лога файл открывается и блокируется, однако, если другой процесс/поток захочет туда же записать, он ждет не более 0.1с; теоретически, этого времени на 8 потоков должно хватить, но кто знает?).
Буду искать, где ж собака порылась-то!
Сделал надстройку над макросами аллокаторов и free, debug.h:
#include <usefull_macros.h>
#ifdef EBUG
extern sl_log *debuglog;
void makedebuglog();
void *my_malloc(size_t N, size_t S);
void my_free(void *ptr);
#undef FNAME
#undef ALLOC
#undef MALLOC
#undef FREE
#define _LOG(...) do{if(!debuglog) makedebuglog(); sl_putlogt(1, debuglog, LOGLEVEL_ERR, __VA_ARGS__);}while(0)
#define DBGLOG(...) do{_LOG("%s (%s, line %d)", __func__, __FILE__, __LINE__); \
sl_putlogt(0, debuglog, LOGLEVEL_ERR, __VA_ARGS__);}while(0)
#define FNAME() _LOG("%s (%s, line %d)", __func__, __FILE__, __LINE__)
#define _str(x) #x
#define ALLOC(type, var, size) DBGLOG("%s *%s = ALLOC(%d)", _str(type), _str(var), size*sizeof(type)); \
type * var = ((type *)my_malloc(size, sizeof(type)))
#define MALLOC(type, size) ((type *)my_malloc(size, sizeof(type))); DBGLOG("ALLOC()")
#define FREE(ptr) do{if(ptr){DBGLOG("FREE(%s)", _str(ptr)); my_free(ptr); ptr = NULL;}}while(0);
#else
#define DBGLOG(...)
#endif
И вот, что находится в debug.c:
#ifdef EBUG
#include <string.h>
#include <unistd.h>
#include "debug.h"
#define DEBUGLOG "DEBUG.log"
sl_log *debuglog = NULL;
void makedebuglog(){
unlink(DEBUGLOG);
debuglog = sl_createlog(DEBUGLOG, LOGLEVEL_ANY, 0);
}
/**
* @brief my_malloc - memory allocator for logger
* @param N - number of elements to allocate
* @param S - size of single element (typically sizeof)
* @return pointer to allocated memory area
*/
void *my_malloc(size_t N, size_t S){
size_t NS = N*S + sizeof(size_t);
sl_putlogt(0, debuglog, LOGLEVEL_ERR, "ALLOCSZ(%zd)", N*S);
void *p = malloc(NS);
if(!p) ERR("malloc");
memset(p, 0, NS);
*((size_t*)p) = N*S;
return (p + sizeof(size_t));
}
void my_free(void *ptr){
void *orig = ptr - sizeof(size_t);
sl_putlogt(0, debuglog, LOGLEVEL_ERR, "FREESZ(%zd)", *(size_t*)orig);
free(orig);
}
#endif
Переопределил еще макрос FNAME, да добавил макросы _LOG и DBGLOG, чтобы можно было еще дополнительно что-нибудь в этот файл писать, да при необходимости помечать функции, в которые заходит в ходе выполнения.
Для анализа выхлопа DEBUG.log написал простой скриптик:
#!/bin/sh
ALLOC=$(grep ALLOCSZ DEBUG.log)
FREE=$(grep FREESZ DEBUG.log)
echo "Total: $(echo "$ALLOC" | wc -l) allocations and $(echo "$FREE" | wc -l) frees"
AL=$(echo "$ALLOC" | sed 's/ALLOCSZ(\([[:digit:]]*\))/\1/' | awk 'BEGIN{x=0;} {x=x+$1;} END{print x;}')
FR=$(echo "$FREE" | sed 's/FREESZ(\([[:digit:]]*\))/\1/' | awk 'BEGIN{x=0;} {x=x+$1;} END{print x;}')
echo "Allocated: $AL bytes, Freed: $FR bytes, difference: $((AL - FR)) bytes"
Запустил немного погонять, и что я вижу:
./DEBUG.log.analyze
Total: 4080 allocations and 3906 frees
Allocated: 5965517012 bytes, Freed: 5935556848 bytes, difference: 29960164 bytes
Рукожопие мое зело велико! На 4080 аллокаторов всего лишь 3906 освобождений памяти! И за несколько минут натекло аж 28.5МБ памяти! То-то оно и дохло через несколько часов работы… Интересно, что "потеряшек" всего с полпроцента от общей выделяемой памяти, т.е. теряется где-то в мелочевке (скажем, выделил память под объект, выделил память под его содержимое; содержимое освободил, а объект - нет). Ну и нужно проверить, успевают ли логгироваться все выделения/освобождения памяти в потоках openmp (дело в том, что у меня для записи лога файл открывается и блокируется, однако, если другой процесс/поток захочет туда же записать, он ждет не более 0.1с; теоретически, этого времени на 8 потоков должно хватить, но кто знает?).
Буду искать, где ж собака порылась-то!