Хуки памяти gcc иногда обходятся? - PullRequest
1 голос
/ 15 ноября 2010

Для приложения c ++ arm мне нужно отслеживать распределение памяти. Для этого я использую хуки памяти gcc. Пока я печатаю распределения и освобождения, см. Код ниже.

Однако malloc и free не складываются. Иногда я вижу free в блоке памяти, который проходил раньше, не перехватывал malloc раньше. Или память освобождается дважды. Конечно, это может быть ошибка в моем коде, хотя я не получаю segfault. Но я также вижу, что malloc иногда возвращает указатель, который он возвращал раньше, и в то же время не было free (по крайней мере, мой бесплатный хук не был вызван).

Так что я предполагаю, что некоторые malloc и free' не передаются через мои хуки. Обратите внимание, что, когда я отслеживаю только распределение c ++, все складывается хорошо.

У кого-нибудь есть идеи?

#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
#include <new>
#include <unistd.h>
#include <string.h>
#include <malloc.h>

pthread_mutex_t lock = PTHREAD_RECURSIVE_MUTEX_INITIALIZER_NP;

static void push_memhooks();
static void pop_memhooks();

static void *malloc_hook(size_t size, const void *ret)
{

    pthread_mutex_lock(&lock);

    pop_memhooks();

    void *mem = malloc(size);

    if (mem) {
        printf("malloc %p\n", mem);
    }

    push_memhooks();

    pthread_mutex_unlock(&lock);

    return mem;
}

static void *realloc_hook(void* ptr, size_t size, const void *ret)
{
    pthread_mutex_lock(&lock);

    pop_memhooks();

    void* mem = realloc(ptr, size);

    if (mem) {
        printf("realloc %p -> %p\n", ptr, mem);
    }

    push_memhooks();

    pthread_mutex_unlock(&lock);

    return mem;
}

static void* memalign_hook(size_t boundary, size_t size, const void *ret)
{
    pthread_mutex_lock(&lock);
    pop_memhooks();

    void* mem = memalign(boundary, size);

    if (mem) {
        printf("memalign %p\n", mem);
    }

    push_memhooks();

    pthread_mutex_unlock(&lock);

    return mem;
}

static void free_hook(void *mem, const void *ret)
{
    pthread_mutex_lock(&lock);

    pop_memhooks();

    free(mem);

    printf("free %p\n", mem);

    push_memhooks();

    pthread_mutex_unlock(&lock);
}

void *operator new(size_t size)
{
    void* mem = malloc(size);

    if (!mem) {
        throw std::bad_alloc();
    }

    return mem;
}

void operator delete(void* mem)
{
    free(mem);
}

void *operator new[](size_t size)
{       
    void* mem = malloc(size);

    if (!mem) {
        throw std::bad_alloc();
    }

    return mem;
}

void operator delete[](void* mem)
{
    free(mem);
}

static int memhooks = 0;

static void push_memhooks()
{
    if (++memhooks == 1) {
        __malloc_hook = malloc_hook;
        __realloc_hook = realloc_hook;
        __free_hook = free_hook;
        __memalign_hook = memalign_hook;
    }
}

static void pop_memhooks()
{
    if (--memhooks == 0) {
        __malloc_hook = NULL;
        __realloc_hook = NULL;
        __free_hook = NULL;
        __memalign_hook = NULL;
    }
}

static void install_memhooks ()
{
    push_memhooks();
}

void (*__malloc_initialize_hook)(void) = install_memhooks;

Например, я получаю следующий вывод, когда извлекаю трассировку для указателя, который показывает странное поведение.

<snip>
malloc 0x8234818
free 0x8234818
malloc 0x8234818
malloc 0x8234818
free 0x8234818
<snip>

Обратите внимание на два последовательных malloc.

Решение : Как упоминал Крис в своем ответе, в приведенном выше коде есть условие гонки. К сожалению, перехватчики malloc не могут безопасно использоваться в многопоточной среде при удалении и переустановке перехватчиков, как я. По той же причине mcheck нельзя использовать в многопоточных приложениях (http://sources.redhat.com/bugzilla/show_bug.cgi?id=9939).

Реализация malloc / realloc / free и вызов версий libc с использованием dlsym(RTLD_NEXT, "malloc") также не работали. Во-первых, dlsym вызывает calloc, поэтому для предотвращения бесконечной рекурсии здесь требуется особая осторожность. Во-вторых, при вызове libc malloc процесс зависает. Кроме того, я вижу, что мой __malloc_initialize_hook не вызывается. Поэтому я предполагаю, что, предоставляя мою собственную malloc реализацию, libc malloc не инициализируется должным образом.

Мое текущее решение имеет встроенную реализацию dlmalloc для удаления зависимости от malloc libc. Теперь мне не нужно постоянно удалять / переустанавливать крючки malloc. Я устанавливаю ловушки один раз, и мои ловушки распределяют память, используя dlmalloc.

Ответы [ 2 ]

1 голос
/ 15 ноября 2010

Если вы работаете в многопоточной среде, у вас есть состояние гонки, из-за которого вы можете пропустить вызовы malloc / free. Когда вызывается ваша функция malloc_hook, она отцепляет все хуки, вызывает malloc и затем перехватывает хуки. Если какой-то другой поток вызывает malloc / free во время отсоединения перехватчиков, вы не увидите этот вызов. Ваш мьютекс не помогает, так как при отключенном вызове malloc / free не будет вызывать вашу функцию перехвата, поэтому не будет ждать мьютекса.

редактировать

Мой предпочтительный способ перехватить / перехватить malloc в программе - просто перехватывать вызовы из моей программы с помощью макросов, не беспокоясь о вызовах из stdlib. Создайте файл wrap_malloc с:

#define malloc(sz)     wrap_malloc(sz, __FILE__, __LINE__)
#define free(p)        wrap_free(p, __FILE__, __LINE__)
#define realloc(p, sz) wrap_realloc(p, sz, __FILE__, __LINE__)
#define calloc(s1, s2) wrap_calloc(s1, s2, __FILE__, __LINE__)

затем скомпилируйте весь мой код с помощью -imacros wrap_malloc. Файл, который определяет wrap_malloc и friends, просто нуждается в соответствующих #undef s, но никаких других изменений в коде не требуется.

0 голосов
/ 15 ноября 2010

Я не знаю, в какой степени вы контролируете этот код и как вы пытаетесь определить, совпадает ли malloc/free.Но может случиться и то, что любой из них замаскирован под realloc.Это может с некоторыми типами аргументов иметь тот же эффект, что и malloc (начальный указатель равен 0) или как free (новый размер равен 0), IIRC.

Редактировать: Я вижу, что вы используете pthreads?Другая возможность состоит в том, что ваш malloc/free происходит по порядку, но ваш printf для выходных данных отладки выходит из строя.Доступ к переменным FILE* обычно осуществляется взаимно.Попробуйте поставить префикс в результатах отладки с отметкой времени и сначала отсортировать по этой отметке времени.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...