Я работаю над инструментом отслеживания блокировок, предназначенным для подключения к приложениям на основе Pthreads с помощью LD_PRELOAD, и столкнулся со странной проблемой. Когда тестовое приложение запускается под управлением valgrind с подключенным моим трассировщиком, оно сообщает о нескольких утечках памяти, происходящих в pthread_cond_signal()/wait()
libpthread (мой инструмент скрывает эти функции для реализации функции трассировки). Эти утечки не происходят, когда мой инструмент не прикреплен. Образец отчета об утечке:
==12993== 48 bytes in 1 blocks are definitely lost in loss record 1 of 6
==12993== at 0x483DD99: calloc (vg_replace_malloc.c:762)
==12993== by 0x48C8629: pthread_cond_wait@GLIBC_2.2.5 (old_pthread_cond_wait.c:34)
==12993== by 0x48775EF: pthread_cond_wait (pthread_trace.cpp:39)
==12993== by 0x10C060: shard_get (shard.c:68)
==12993== by 0x10BC38: resolver_thread (req_res.c:74)
==12993== by 0x487789A: inject_thread_registration(void*) (pthread_trace.cpp:85)
==12993== by 0x48C0608: start_thread (pthread_create.c:477)
==12993== by 0x49FC292: clone (clone.S:95)
Я понятия не имею, почему это происходит, потому что мой код вообще не взаимодействует с объектами Pthreads, за исключением получения их адреса для регистрации. Вот код для моих функций-оболочек:
int pthread_cond_wait(pthread_cond_t* cond, pthread_mutex_t* lk) {
// log arrival at wait
the_tracer.add_event(lktrace::event::COND_WAIT, (size_t) cond);
// run pthreads function
GET_REAL_FN(pthread_cond_wait, int, pthread_cond_t*, pthread_mutex_t*);
int e = REAL_FN(cond, lk);
if (e == 0) the_tracer.add_event(lktrace::event::COND_LEAVE, (size_t) cond);
else the_tracer.add_event(lktrace::event::COND_ERR, (size_t) cond);
return e;
}
int pthread_cond_signal(pthread_cond_t* cond) {
// log cond signal
the_tracer.add_event(lktrace::event::COND_SIGNAL, (size_t) cond);
// run pthreads function
GET_REAL_FN(pthread_cond_signal, int, pthread_cond_t*);
return REAL_FN(cond);
}
// GET_REAL_FN definition:
#define GET_REAL_FN(name, rtn, params...) \
typedef rtn (*real_fn_t)(params); \
static const real_fn_t REAL_FN = (real_fn_t) dlsym(RTLD_NEXT, #name); \
assert(REAL_FN != NULL) // semicolon absence intentional
И, для полноты, вот соответствующий код glibc для pthread_cond_signal (идентичный pthread_cond_wait, за исключением вызова функции возврата):
int
__pthread_cond_signal_2_0 (pthread_cond_2_0_t *cond)
{
if (cond->cond == NULL)
{
pthread_cond_t *newcond;
newcond = (pthread_cond_t *) calloc (sizeof (pthread_cond_t), 1); // leak alloc'd here
if (newcond == NULL)
return ENOMEM;
if (atomic_compare_and_exchange_bool_acq (&cond->cond, newcond, NULL))
/* Somebody else just initialized the condvar. */
free (newcond);
}
return __pthread_cond_signal (cond->cond);
}
Тестовая программа (к которой подключается мой инструмент) очищает свои условные переменные перед выходом (и, как уже упоминалось, не имеет утечек памяти при запуске без инструмента). Я довольно озадачен этим, у вас есть идеи? Я уверен, что это что-то простое, что смотрит мне в лицо, это всегда так...