2016-03-13 3 views
0

Я создаю небольшое быстрое приложение, которое обнаруживает утечки памяти в других приложениях. Я использую LD_PRELOAD для переопределения по умолчанию malloc и используя это, хранящиеся в памяти, каждый вызов в malloc в вашей программе. Проблема в том, что некоторые библиотечные функции C также используют malloc. Кроме того, существуют библиотечные функции, которые не освобождают выделенную память. Продемонстрируем:
MyApp.cppПереопределение malloc с помощью LD_PRELOAD и вызов malloc в библиотечных функциях

#include <unistd.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <dlfcn.h> 

static void * (* LT_MALLOC)(size_t) = 0; 
static void (* LT_FREE)(void *) = 0; 

static void init_malloc() 
{ 
    char *error; 
    *(void **) (&LT_MALLOC) = dlsym (RTLD_NEXT, "malloc"); 
    dlerror(); 
    if ((error = dlerror()) != NULL) 
    { 
     fprintf (stderr, "%s\n", error); 
     _exit(1); 
    } 
} 

static void init_free() 
{ 
    char *error; 
    *(void **) (&LT_FREE) = dlsym (RTLD_NEXT, "free"); 
    dlerror(); 
    if ((error = dlerror()) != NULL) 
    { 
     fprintf (stderr, "%s\n", error); 
     _exit(1); 
    } 
} 

extern "C" 
void * malloc (size_t size) throw() 
{ 
    if (LT_MALLOC == 0) 
    init_malloc(); 
    printf ("malloc(%ld) ", size); 
    void *p = LT_MALLOC(size); 
    printf ("p = %p\n", p); 
    return p; 
} 

extern "C" 
void free (void *p) throw() 
{ 
    if (LT_FREE == 0) 
    init_free(); 
    printf ("free(%p)\n", p); 
    LT_FREE(p); 
} 

И test.c. (Предположим, что исходный код не доступен на начальном этапе, и у меня есть только программы.)

#include <stdlib.h> 
#include <stdio.h> 
#include <time.h> 

int main (void) 
{ 
    printf ("test start\n"); 
    int *a = (int *)malloc(3 * sizeof (int)); 
    if (a) 
    printf ("Allocated 3 int - %p\n", a); 
    time_t t = time(NULL); 
    a[0] = 1; 
    printf ("time - %s", ctime(&t)); 
    printf ("a[0] = %d\n", a[0]); 
    free (a); 
    printf ("CALL FREE(a)\n"); 
    printf ("test done\n"); 
    return 0; 
} 


$ g++ -g -fPIC -c MyApp.cpp -o MyApp.o 
$ g++ -g -shared MyApp.o -o MyApp.so -ldl 
$ gcc -g test.c -o test 
$ export LD_PRELOAD=./MyApp.so 

Запуск программы ./test и посмотреть:

malloc(72704) p = 0x8aa040 
test start 
malloc(12) p = 0x6a0c50 
Allocated 3 int - 0x6a0c50 
free((nil)) 
malloc(15) p = 0x6a0c70 
malloc(552) p = 0x6a0c90 
free((nil)) 
malloc(1014) p = 0x6a0ec0 
free(0x6a0c90) 
malloc(20) p = 0x6a0c90 
malloc(20) p = 0x6a0cb0 
malloc(20) p = 0x6a0cd0 
malloc(21) p = 0x6a0cf0 
malloc(20) p = 0x6a0d10 
malloc(20) p = 0x6a0d30 
malloc(20) p = 0x6a0d50 
malloc(20) p = 0x6a0d70 
malloc(21) p = 0x6a0d90 
free((nil)) 
time - Mon Mar 14 18:30:14 2016 
a[0] = 1 
free(0x6a0c50) 
CALL FREE(a) 
test done 

Но я хочу посмотреть:

test start 
malloc(12) p = 0x6a0c50 
Allocated 3 int - 0x6a0c50 
time - Mon Mar 14 18:30:14 2016 
a[0] = 1 
free(0x6a0c50) 
CALL FREE(a) 
test done 

Я хочу, чтобы мое приложение избегало скинов malloc в библиотечных функциях. Но у меня нет идей, как это сделать.
Может ли malloc узнать, какая функция называется: библиотека или ваша? Или мы можем сделать, чтобы вызывать по умолчанию не переопределять malloc в библиотечных функциях? Или что-то другое?
P.S. Прошу прощения за мой плохой английский.

+0

Почему вы не просто используете valgrind? – chqrlie

+0

Valgrind не так быстро. –

ответ

0

Возможно, API Dynamic Loader может вам помочь. Там вы найдете функцию dladdr(), которая возвращает модуль и имя символа для заданного адреса, если он доступен.

Подробную информацию см. На странице man dlopen.

+0

Как я могу использовать информацию из 'dladdr()'? –

0

У меня МНОГИЕ времена использовали технику компиляции моего кода с макросом, который заменяет malloc моей собственной версией. Я думал, что написал ответ, чтобы продемонстрировать это, но, видимо, нет. Что-то вроде этого:

В "debugmalloc.h" или некоторые такие:

#if DEBUG_MALLOC 
#define malloc(x) myTrackingMalloc(x, __FILE__, __LINE__) 
#define free(x) myTrackngFree(x, __FILE__, __LINE__) 

extern void* myTrackingMalloc(size_t size, const char* file, int line); 
extern void myTrackingFree(void* ptr, const char* file, int line); 
#endif 

Затем в исходном файле, например, "debugmalloc.c":

#if DEBUG_MALLOC 
void* myTrackingMalloc(size_t size, const char* file, int line) 
{ 
    void *p = malloc(size); 
    ... whatever extra stuff you need ... 
    return p; 
} 

void myTrackingFree(void* ptr, const char* file, int line) 
{ 
    ... some extra code here ... 
    free(ptr); 
} 
#endif 

[я выделил некоторые дополнительные байт, изменив size и добавив затем использованное смещение, чтобы вернуть соответствующий фактический указатель полезной нагрузки]

Это относительно легко реализовать и не имеет недостатков injec используя LD_PRELOAD - в частности, вам не нужно различать ваш код и библиотеки. И, конечно, вам не нужно реализовывать свой собственный malloc.

У него есть небольшой недостаток, что doens't покрытие new, delete, ни strdup и другие функции библиотеки, которые сами по себе распределение памяти - вы можете реализовать new и delete глобальный набор оператора [не забудьте массив версия!], но заменяя strdup и любую другую функцию, которая может выделять память, довольно много работы - и в этот момент valgrind, вероятно, будет быстрее, даже если он работает довольно медленно.

0

В приведенном коде вы не отслеживаете calloc, realloc и strdup. Было бы более последовательным отслеживать все связанные функции распределения malloc.

Вы также можете отслеживать fopen и fclose отключить malloc и free отслеживания во время выполнения тех. Это также позволит вам отслеживать отсутствующие вызовы fclose().

Вы можете сделать то же самое для других функций библиотеки C, которые используют malloc. Вряд ли printf использует malloc, но вы можете отслеживать vfprintf, чтобы отфильтровать любые такие звонки. Трудно обернуть вариативные функции, но семейство printf все вызовет vfprintf или немного близкого родственника, в зависимости от реализации библиотеки C. Будьте осторожны, чтобы отключить контроль над vfprintf, когда вы позвоните printf себе!