Click here to Skip to main content
15,891,431 members
Articles / Programming Languages / C

C++ Memory Leak Finder

Rate me:
Please Sign up or sign in to vote.
4.96/5 (37 votes)
6 Jun 2012CPOL12 min read 150K   2.4K   124  
How to write a memory leak detection program using library injection
leakfinder/allocation_info.hpp0000664000175000017500000000144211740401715016674 0ustar  fredrikfredrik#ifndef __allocation_info
#define __allocation_info

#include <vector>
#include <string>

#include <pthread.h>

namespace bornander
{
    namespace memory
    {
        class allocation_info
        {
        public:
            typedef long long address_type;

        private:
            allocation_info::address_type address;
            size_t size;
            std::vector<std::string> stacktrace;
            pthread_t thread_id;
        public:
            allocation_info(void* address, size_t size, char** stacktrace, size_t depth, pthread_t thread_id);

            allocation_info::address_type get_address() const;
            size_t get_size() const;
            std::vector<std::string> get_stacktrace() const;
            pthread_t get_thread_id() const;
        };
    }
}
    
#endif
leakfinder/c_example.c0000664000175000017500000000106411740035522015123 0ustar  fredrikfredrik#include <stdlib.h>
#include <stdio.h>

void foo(int size)
{
    int* data = malloc(sizeof(int) * size);

    // Uncomment this to stop leak    
    //free(data);
}

void bar(int size)
{
    char *data = malloc(sizeof(char) * size);
    foo(size);

    // Uncomment this to stop leak
    //free(data);
}

void foobar(int size)
{
    bar(size);
}

int main(void)
{
    printf("leakfinder C example app\n");
    printf("This application is expected to leak\n");
    
    foobar(8);
    foobar(16);

    printf("leakfinder C example app all done\n");
    return 0;
}
leakfinder/cpp_example.cpp0000664000175000017500000000153511753520221016025 0ustar  fredrikfredrik#include<iostream>

class my_class_a
{
private:
    int* leak;
    int* no_leak;
public:
    my_class_a()
    {
        leak = new int;
        no_leak = new int;
    }

    ~my_class_a()
    {
        delete no_leak;
    }
};

class my_class_b
{
private:
    my_class_a a_instance;
public:
    void foo()
    {
        int* leak = new int; 
    }

    void bar()
    {
        foo();
    }

    void foobar()
    {
       bar();
    }
};

void cpp_function()
{
    char* leak = new char; 
}

extern "C" void c_function()
{
    cpp_function();
}

int main(void)
{
    std::cout << "leakfinder C++ thread example app" << std::endl;
    std::cout << "This application is expected to leak" << std::endl;
    my_class_b b;
    b.foobar();
    b.foo();

    c_function();

    std::cout << "leakfinder C++ thread example app all done" << std::endl;
    return 0;
}

leakfinder/c_thread_example.c0000664000175000017500000000202211753517742016462 0ustar  fredrikfredrik#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>

void foo(int size)
{
    int* data = malloc(sizeof(int) * size);

    // Uncomment this to stop leak    
    //free(data);
}

void bar(int size)
{
    char *data = malloc(sizeof(char) * size);
    foo(size);

    // Uncomment this to stop leak
    //free(data);
}

void foobar(int size)
{
    bar(size);
}

void* thread_run(void* ptr)
{
    char* message = (char*)ptr;
    printf("Caller %s with id %ld\n", message, pthread_self());

    foobar(32);    
}

int main(void)
{
    printf("leakfinder C thread example app\n");
    printf("This application is expected to leak on multiple threads\n");
    
    pthread_t thread1, thread2;
    char *message1 = "Thread 1";
    char *message2 = "Thread 2";
   
    pthread_create(&thread1, NULL, thread_run, (void*)message1);
    pthread_create(&thread2, NULL, thread_run, (void*)message2);
    
    pthread_join(thread1, NULL);
    pthread_join(thread2, NULL);
 
    printf("leakfinder C thread example app all done\n");
    return 0;
}
leakfinder/leakfinder.c0000664000175000017500000000233511737567756015325 0ustar  fredrikfredrik#include <stdio.h>
#include <execinfo.h>
#include <stdlib.h>
     
#define __USE_GNU
#include <dlfcn.h>

class allocation_info
{
private:
 
public:
};

static bool isExternalSource=true;
static void* (*real_malloc)(size_t)=0;
static void (*real_free)(void*)=0;

static void __mtrace_init(void)
{
    real_malloc = (void*(*)(size_t))dlsym(RTLD_NEXT, "malloc");
    if (real_malloc == 0) {
        fprintf(stderr, "Error in `dlsym`: %s\n", dlerror());
        return;
    }

    real_free = (void(*)(void*))dlsym(RTLD_NEXT, "free");
    if (real_free == 0) {
        fprintf(stderr, "Error in `dlsym`: %s\n", dlerror());
        return;
    }


}

extern "C" void *malloc(size_t size)
{
    if(real_malloc==0)
        __mtrace_init();

    void *p = real_malloc(size);

    if (isExternalSource)
    {
        isExternalSource = false;

        printf("Allocating %d bytes\n", size);        

        isExternalSource = true;
    }	

    return p;
}

extern "C" void free(void* ptr) 
{
    if(real_free==0)
        __mtrace_init();

    int address = (int)ptr;
    real_free(ptr);

    if (isExternalSource)
    {
        isExternalSource = false;
        printf("Freed memory at %d \n", address);        
        isExternalSource = true;
    }
}
leakfinder/leakfinder.cpp0000664000175000017500000000673011753521446015650 0ustar  fredrikfredrik#include <vector>
#include <string>
#include <iostream>
#include <iomanip>
#include <execinfo.h>
#include <pthread.h>
     
#ifndef __USE_GNU
#define __USE_GNU
#endif
#include <dlfcn.h>

#include "allocation_info.hpp"

using namespace bornander::memory;
using namespace std;

static void compile_allocation() __attribute__((destructor));

static pthread_mutex_t cs_mutex = PTHREAD_MUTEX_INITIALIZER;

static size_t allocation_count = 0;
static vector<allocation_info> allocations;

static const size_t max_frame_depth = 128;
static bool isExternalSource = true;
static void* (*sys_malloc)(size_t) = 0;
static void (*sys_free)(void*) = 0;

static void initialize_functions(void)
{
    sys_malloc = reinterpret_cast<void*(*)(size_t)>(dlsym(RTLD_NEXT, "malloc"));
    if (sys_malloc == 0) 
        cerr << "leakfinder failed to read malloc function; " << dlerror() << endl; 

    sys_free = reinterpret_cast<void(*)(void*)>(dlsym(RTLD_NEXT, "free"));
    if (sys_free == 0) 
        cerr << "leakfinder failed to read free function; " << dlerror() << endl; 
}

extern "C" void* malloc(size_t size)
{
    if (sys_malloc == 0)
        initialize_functions();

    void* ptr = sys_malloc(size);

    if (isExternalSource)
    {
        pthread_t thread_id = pthread_self();
        pthread_mutex_lock(&cs_mutex);
        isExternalSource = false;
        
        ++allocation_count;
        
        void* frames[max_frame_depth];
        size_t stack_size = backtrace(frames, max_frame_depth);
        char** stacktrace = backtrace_symbols(frames, stack_size);
        allocation_info allocation(ptr, size, stacktrace, stack_size, thread_id);

        allocations.push_back(allocation);
     
        sys_free(stacktrace);

        isExternalSource = true;

        pthread_mutex_unlock(&cs_mutex);
    }	

    return ptr;
}

extern "C" void free(void* ptr) 
{
    if (sys_free == 0)
        initialize_functions();

    allocation_info::address_type address = reinterpret_cast<allocation_info::address_type>(ptr);
    sys_free(ptr);

    if (isExternalSource)
    {
        pthread_mutex_lock(&cs_mutex);

        isExternalSource = false;
        for (int i = 0; i < allocations.size(); ++i)
        {
            allocation_info allocation = allocations[i];
            if (allocation.get_address() == address)
            {
                allocations.erase(allocations.begin() + i);
                break;
            }        
        }
        isExternalSource = true;

        pthread_mutex_unlock(&cs_mutex);
    }
}

void compile_allocation()
{
    isExternalSource = false; 
    if (allocations.empty())
    {
        cout << "leakfinder found no leaks, not one of the " << allocation_count;
        cout << " allocations was not released." << endl;
    }
    else
    {
        cout << "leakfinder detected that " << allocations.size();
        cout << " out of " << allocation_count << " allocations was not released." << endl;
        for (int i = 0; i < allocations.size(); ++i)
        {
            allocation_info allocation = allocations[i];
            cout << "Leak " << (i+1) << "@0x" << hex << allocation.get_thread_id() << dec;
            cout << "; leaked " << allocation.get_size() << " bytes at position 0x"; 
            cout << hex << allocation.get_address() << dec << endl; 
            
            vector<string> stacktrace = allocation.get_stacktrace();
            for (int j = 0; j < stacktrace.size(); ++j)
            {
                cout << "\t" << stacktrace[j] << endl;
            } 
        } 
    }
}
leakfinder/makefile0000664000175000017500000000102011760654461014525 0ustar  fredrikfredrikall: leakfinder.so c_example c_thread_example cpp_example
	
leakfinder.so: allocation_info.hpp allocation_info.cpp leakfinder.cpp
	g++ -shared -fPIC allocation_info.cpp leakfinder.cpp -o leakfinder.so -lpthread -ldl

c_example: c_example.c
	cc -rdynamic c_example.c -o c_example 

c_thread_example: c_thread_example.c
	cc -rdynamic c_thread_example.c -lpthread -o c_thread_example 

cpp_example: cpp_example.cpp
	g++ -rdynamic cpp_example.cpp -o cpp_example 

clean:
	rm -rf leakfinder.so c_example c_thread_example cpp_example

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Sweden Sweden
Article videos
Oakmead Apps Android Games

21 Feb 2014: Best VB.NET Article of January 2014 - Second Prize
18 Oct 2013: Best VB.NET article of September 2013
23 Jun 2012: Best C++ article of May 2012
20 Apr 2012: Best VB.NET article of March 2012
22 Feb 2010: Best overall article of January 2010
22 Feb 2010: Best C# article of January 2010

Comments and Discussions