0

I working on a multithreaded software that uses mysql to query database, I keep getting memory leaks after debugging and I have no clue what's causing them. I followed mysql dev guidelines and also this SO answer but the leaks are still there, I was able to produce the same error with this script:

#include <mysql/mysql.h>
#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>

#define DBHOST  "localhost"
#define DBUSER  "#####"
#define DBPSWD  "#######"
#define DBNAME  "testdb"

void die (MYSQL *conn)
{
    fprintf (stderr, "%s\n", mysql_error (conn));
    if (conn != NULL) mysql_close (conn);
    exit (1);
}

MYSQL *DBConnect()
{
    MYSQL *conn = NULL, *res;
    if ((conn = mysql_init (NULL)) == NULL) die (conn);
    res = mysql_real_connect (conn, DBHOST, DBUSER, DBPSWD, DBNAME, 0, NULL, 0);
    if (res == NULL) die (conn);
    return conn;
}

void DBDisconnect (MYSQL *conn)
{
    mysql_close (conn);
    mysql_thread_end();
}

MYSQL_RES *executeQuery (MYSQL *conn, char *query)
{
    MYSQL_RES *result;
    if (mysql_query (conn, query) != 0) die (conn);
    if ((result = mysql_store_result (conn)) == NULL) return NULL;
    return result;
}

int getRecordsCount (MYSQL *conn)
{
    char *query = "SELECT * FROM test";
    MYSQL_RES *results = executeQuery (conn, query);
    int count = mysql_num_rows (results);
    mysql_free_result (results);
    return count;
}

void *handler (void *arg)
{
    MYSQL *conn = DBConnect();
    int n = *((int *) arg);
    sleep (1);
    printf ("Thread %d: count = %d\n", n, getRecordsCount (conn));
    DBDisconnect (conn);
    return NULL;
}

int main (void)
{
    pthread_t threads[10];
    pthread_attr_t attr;
    int i, tCount = 0;

    mysql_library_init (0, NULL, NULL);
    pthread_attr_init (&attr);
    pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);

    for (i = 0; i < 10; i++)
    {
        pthread_create (&(threads[tCount++]), &attr, handler, (void *) &i);
    }
    while (i-- > 0) sleep (2);

    pthread_attr_destroy (&attr);
    pthread_exit (NULL);
    mysql_library_end();

    return 0;
}

Makefile:

CC = gcc
CFLAGS = -Wall -Wextra -pedantic-errors -O2
LIBS = -lmysqlclient -lpthread

all: test
test: test.c
    $(CC) $(CFLAGS) $< $(LIBS) -o $@

Valgrind:

--15058-- Discarding syms at 0x51cbad0-0x51d26bb in /lib/i386-linux-gnu/libnss_files-2.19.so due to munmap()
==15058== 
==15058== HEAP SUMMARY:
==15058==     in use at exit: 73,872 bytes in 21 blocks
==15058==   total heap usage: 239 allocs, 218 frees, 606,847 bytes allocated
==15058== 
==15058== Searching for pointers to 21 not-freed blocks
==15058== Checked 717,608 bytes
==15058== 
==15058== 16 bytes in 1 blocks are still reachable in loss record 1 of 5
==15058==    at 0x402A17C: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==15058==    by 0x4095357: my_malloc (in /usr/lib/i386-linux-gnu/libmysqlclient.so.18.0.0)
==15058==    by 0x40913FC: my_error_register (in /usr/lib/i386-linux-gnu/libmysqlclient.so.18.0.0)
==15058==    by 0x4071C8C: init_client_errs (in /usr/lib/i386-linux-gnu/libmysqlclient.so.18.0.0)
==15058==    by 0x406E044: mysql_server_init (in /usr/lib/i386-linux-gnu/libmysqlclient.so.18.0.0)
==15058==    by 0x80488EF: main (in /tmp/test)
==15058== 
==15058== 128 bytes in 1 blocks are definitely lost in loss record 2 of 5
==15058==    at 0x402C109: calloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==15058==    by 0x4096C03: my_thread_init (in /usr/lib/i386-linux-gnu/libmysqlclient.so.18.0.0)
==15058==    by 0x4096ECF: my_thread_global_init (in /usr/lib/i386-linux-gnu/libmysqlclient.so.18.0.0)
==15058==    by 0x4094AF7: my_init (in /usr/lib/i386-linux-gnu/libmysqlclient.so.18.0.0)
==15058==    by 0x406E014: mysql_server_init (in /usr/lib/i386-linux-gnu/libmysqlclient.so.18.0.0)
==15058==    by 0x80488EF: main (in /tmp/test)
==15058== 
==15058== 144 bytes in 1 blocks are still reachable in loss record 3 of 5
==15058==    at 0x402A17C: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==15058==    by 0x4095357: my_malloc (in /usr/lib/i386-linux-gnu/libmysqlclient.so.18.0.0)
==15058==    by 0x4090892: init_alloc_root (in /usr/lib/i386-linux-gnu/libmysqlclient.so.18.0.0)
==15058==    by 0x4079650: mysql_client_plugin_init (in /usr/lib/i386-linux-gnu/libmysqlclient.so.18.0.0)
==15058==    by 0x406E049: mysql_server_init (in /usr/lib/i386-linux-gnu/libmysqlclient.so.18.0.0)
==15058==    by 0x80488EF: main (in /tmp/test)
==15058== 
==15058== 36,792 bytes in 9 blocks are still reachable in loss record 4 of 5
==15058==    at 0x402A17C: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==15058==    by 0x4095702: my_once_alloc (in /usr/lib/i386-linux-gnu/libmysqlclient.so.18.0.0)
==15058==    by 0x408BF37: ??? (in /usr/lib/i386-linux-gnu/libmysqlclient.so.18.0.0)
==15058==    by 0x408CBA9: ??? (in /usr/lib/i386-linux-gnu/libmysqlclient.so.18.0.0)
==15058==    by 0x438736D: pthread_once (pthread_once.S:120)
==15058==    by 0x408D222: get_charset_by_csname (in /usr/lib/i386-linux-gnu/libmysqlclient.so.18.0.0)
==15058==    by 0x4074A39: mysql_init_character_set (in /usr/lib/i386-linux-gnu/libmysqlclient.so.18.0.0)
==15058==    by 0x40759B2: mysql_real_connect (in /usr/lib/i386-linux-gnu/libmysqlclient.so.18.0.0)
==15058==    by 0x8048B15: DBConnect (in /tmp/test)
==15058==    by 0x8048BE9: handler (in /tmp/test)
==15058==    by 0x4381F6F: start_thread (pthread_create.c:312)
==15058==    by 0x4482BED: clone (clone.S:129)
==15058== 
==15058== 36,792 bytes in 9 blocks are still reachable in loss record 5 of 5
==15058==    at 0x402A17C: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so)
==15058==    by 0x4095702: my_once_alloc (in /usr/lib/i386-linux-gnu/libmysqlclient.so.18.0.0)
==15058==    by 0x408BF56: ??? (in /usr/lib/i386-linux-gnu/libmysqlclient.so.18.0.0)
==15058==    by 0x408CBA9: ??? (in /usr/lib/i386-linux-gnu/libmysqlclient.so.18.0.0)
==15058==    by 0x438736D: pthread_once (pthread_once.S:120)
==15058==    by 0x408D222: get_charset_by_csname (in /usr/lib/i386-linux-gnu/libmysqlclient.so.18.0.0)
==15058==    by 0x4074A39: mysql_init_character_set (in /usr/lib/i386-linux-gnu/libmysqlclient.so.18.0.0)
==15058==    by 0x40759B2: mysql_real_connect (in /usr/lib/i386-linux-gnu/libmysqlclient.so.18.0.0)
==15058==    by 0x8048B15: DBConnect (in /tmp/test)
==15058==    by 0x8048BE9: handler (in /tmp/test)
==15058==    by 0x4381F6F: start_thread (pthread_create.c:312)
==15058==    by 0x4482BED: clone (clone.S:129)
==15058== 
==15058== LEAK SUMMARY:
==15058==    definitely lost: 128 bytes in 1 blocks
==15058==    indirectly lost: 0 bytes in 0 blocks
==15058==      possibly lost: 0 bytes in 0 blocks
==15058==    still reachable: 73,744 bytes in 20 blocks
==15058==         suppressed: 0 bytes in 0 blocks
==15058== 
==15058== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
==15058== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

How do i fix those memory leaks? I'm using mysql v 5.5 if that would help.

Community
  • 1
  • 1
razz
  • 9,770
  • 7
  • 50
  • 68
  • You have only one memory leak, detailed in loss record #2. The others describe allocated memory that is not freed but is *still reachable* at program exit. – John Bollinger Feb 09 '16 at 19:25
  • @JohnBollinger What's causing the error? and the "difinitely lost" memory leak? – razz Feb 09 '16 at 19:31
  • 2
    Also, your main thread probably should not call `pthread_exit()`. That's an analog of the `exit()` function, and it kills the thread that calls it. That means `mysql_library_end()` never gets called, which might explain your leak. – John Bollinger Feb 09 '16 at 19:31
  • @JohnBollinger I tip my hat for you sir, all leaks and errors are gone now :), if you would like to put your comments in an answer i would be happy to accept it. – razz Feb 09 '16 at 19:34
  • In that case, I will write it as an answer. I did not do so before because although I knew it was funky, I didn't know whether it explained the problem. – John Bollinger Feb 09 '16 at 19:37

1 Answers1

1

The pthread_exit() function is an analog of the standard library's exit() function: it kills the thread that calls it. Its argument is the thread's return value.

The main thread of your program is, in fact, a thread, and it can be killed by pthread_exit(). It is unusual to kill the main thread that way, however, and it is surely wrong to expect that function to return. In your particular case, your call to mysql_library_end() appears after your call to pthread_exit(), so it will never be executed. As you discovered, this prevents the mysql library from cleaning up after itself.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157