OAH
OAH

Reputation: 11

Where is the memory leak when freeing this nested struct?

I'm trying to implement a Barnes-Hutt tree in C and I'm having an issue when freeing the tree.

The nodes in the tree are as follows:

struct node {
    int is_external;
    quad q;
    body *b;

    node *nw;
    node *ne;
    node *sw;
    node *se;
};

and the function I use to free them is

void free_node(node *n) {
    // free subnodes
    if (n->nw != NULL) {
        free_node(n->nw);
    }
    if (n->ne != NULL) {
        free_node(n->ne);
    }
    if (n->sw != NULL) {
        free_node(n->sw);
    }
    if (n->se != NULL) {
        free_node(n->se);
    }

    // free this node's body
    if (!n->is_external) {
        free(n->b);
    }
    free(n);
}

(I'm not freeing the bodies of external nodes because they are kept for the next iteration of the simulation).

When I check for memory leaks however, I get

72 bytes in 1 blocks are definitely lost in loss record 24 of 51
   at 0x100111CF5: malloc (in /usr/local/Cellar/valgrind/HEAD-9f4f524/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
   by 0x100001B95: subnode (bh_tree.c:70)
   by 0x100002413: insert_in_node (bh_tree.c:165)
   by 0x100001693: main (sim.c:115)

72 bytes in 1 blocks are definitely lost in loss record 25 of 51
   at 0x100111CF5: malloc (in /usr/local/Cellar/valgrind/HEAD-9f4f524/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
   by 0x100001B95: subnode (bh_tree.c:70)
   by 0x100001E64: insert_in_node (bh_tree.c:108)
   by 0x100002675: insert_in_node (bh_tree.c:188)
   by 0x100001693: main (sim.c:115)

72 bytes in 1 blocks are definitely lost in loss record 26 of 51
   at 0x100111CF5: malloc (in /usr/local/Cellar/valgrind/HEAD-9f4f524/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
   by 0x100001B95: subnode (bh_tree.c:70)
   by 0x10000251D: insert_in_node (bh_tree.c:173)
   by 0x100001F97: insert_in_node (bh_tree.c:118)
   by 0x100001693: main (sim.c:115)

72 bytes in 1 blocks are definitely lost in loss record 27 of 51
   at 0x100111CF5: malloc (in /usr/local/Cellar/valgrind/HEAD-9f4f524/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
   by 0x100001B95: subnode (bh_tree.c:70)
   by 0x100002627: insert_in_node (bh_tree.c:181)
   by 0x10000209B: insert_in_node (bh_tree.c:126)
   by 0x100002675: insert_in_node (bh_tree.c:188)
   by 0x100001F97: insert_in_node (bh_tree.c:118)
   by 0x100001693: main (sim.c:115)

72 bytes in 1 blocks are definitely lost in loss record 28 of 51
   at 0x100111CF5: malloc (in /usr/local/Cellar/valgrind/HEAD-9f4f524/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
   by 0x100001B95: subnode (bh_tree.c:70)
   by 0x10000206C: insert_in_node (bh_tree.c:124)
   by 0x100002675: insert_in_node (bh_tree.c:188)
   by 0x10000209B: insert_in_node (bh_tree.c:126)
   by 0x100002675: insert_in_node (bh_tree.c:188)
   by 0x100001F97: insert_in_node (bh_tree.c:118)
   by 0x100001693: main (sim.c:115)

72 bytes in 1 blocks are definitely lost in loss record 29 of 51
   at 0x100111CF5: malloc (in /usr/local/Cellar/valgrind/HEAD-9f4f524/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
   by 0x100001B95: subnode (bh_tree.c:70)
   by 0x100002170: insert_in_node (bh_tree.c:132)
   by 0x100001693: main (sim.c:115)

72 bytes in 1 blocks are definitely lost in loss record 30 of 51
   at 0x100111CF5: malloc (in /usr/local/Cellar/valgrind/HEAD-9f4f524/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
   by 0x100001B95: subnode (bh_tree.c:70)
   by 0x100002627: insert_in_node (bh_tree.c:181)
   by 0x100001E93: insert_in_node (bh_tree.c:110)
   by 0x100001693: main (sim.c:115)

72 bytes in 1 blocks are definitely lost in loss record 31 of 51
   at 0x100111CF5: malloc (in /usr/local/Cellar/valgrind/HEAD-9f4f524/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
   by 0x100001B95: subnode (bh_tree.c:70)
   by 0x100002627: insert_in_node (bh_tree.c:181)
   by 0x10000219F: insert_in_node (bh_tree.c:134)
   by 0x100002675: insert_in_node (bh_tree.c:188)
   by 0x100001E93: insert_in_node (bh_tree.c:110)
   by 0x100001693: main (sim.c:115)

72 bytes in 1 blocks are definitely lost in loss record 32 of 51
   at 0x100111CF5: malloc (in /usr/local/Cellar/valgrind/HEAD-9f4f524/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
   by 0x100001B95: subnode (bh_tree.c:70)
   by 0x100002309: insert_in_node (bh_tree.c:157)
   by 0x10000219F: insert_in_node (bh_tree.c:134)
   by 0x100002675: insert_in_node (bh_tree.c:188)
   by 0x10000219F: insert_in_node (bh_tree.c:134)
   by 0x100002675: insert_in_node (bh_tree.c:188)
   by 0x100001E93: insert_in_node (bh_tree.c:110)
   by 0x100001693: main (sim.c:115)

72 bytes in 1 blocks are definitely lost in loss record 33 of 51
   at 0x100111CF5: malloc (in /usr/local/Cellar/valgrind/HEAD-9f4f524/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
   by 0x100001B95: subnode (bh_tree.c:70)
   by 0x10000206C: insert_in_node (bh_tree.c:124)
   by 0x100002675: insert_in_node (bh_tree.c:188)
   by 0x10000219F: insert_in_node (bh_tree.c:134)
   by 0x100002675: insert_in_node (bh_tree.c:188)
   by 0x10000219F: insert_in_node (bh_tree.c:134)
   by 0x100002675: insert_in_node (bh_tree.c:188)
   by 0x100001E93: insert_in_node (bh_tree.c:110)
   by 0x100001693: main (sim.c:115)

all of which point to the following malloc

    node n_sub;
    n_sub.is_external = 1;
    n_sub.q = subquad(n.q, k);
    n_sub.nw = NULL;
    n_sub.ne = NULL;
    n_sub.sw = NULL;
    n_sub.se = NULL;
    n_sub.b = malloc(sizeof(body));
    *n_sub.b = (body) {.id=EMPTY};

    return n_sub;
}

but I'm fairly certain I was freeing all of these correctly. Any ideas where it could be going wrong?

Upvotes: 0

Views: 102

Answers (1)

jxh
jxh

Reputation: 70442

Apparently valgrind is always complaining about the same malloc:

at 0x100111CF5: malloc (...)
by 0x100001B95: subnode (bh_tree.c:70)

And the only malloc you point to in your code is at a guess the implementation of subnode:

node n_sub;
n_sub.is_external = 1;
//...
n_sub.b = malloc(sizeof(body));

But, in your free_node function you only call free if is_external is false:

if (!n->is_external) {
    free(n->b);
}

So, you need to suspect whatever code path you think is setting is_external to false is not always (or perhaps is never) called.

Upvotes: 2

Related Questions