summaryrefslogtreecommitdiff
path: root/src/tests/leak_check.c
blob: 06941195db57bd2e710c5f18678efc458fc6c840 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
/*
   SSSD

   Common utilities for check-based tests using talloc.

   Authors:
        Martin Nagy <mnagy@redhat.com>

   Copyright (C) Red Hat, Inc 2009

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include <stdio.h>
#include "tests/common.h"
#include "util/util.h"
#include "util/dlinklist.h"

TALLOC_CTX *global_talloc_context = NULL;
char leak_err_msg[256];

struct size_snapshot {
    struct size_snapshot *prev;
    struct size_snapshot *next;

    TALLOC_CTX *ctx;
    size_t bytes_allocated;
};

static struct size_snapshot *snapshot_stack;

#define _set_leak_err_msg(fmt, ...) do {              \
        snprintf(leak_err_msg, sizeof(leak_err_msg),  \
                 fmt, ##__VA_ARGS__);                 \
} while(0);

const char *check_leaks_err_msg(void)
{
    return leak_err_msg;
}

bool
_check_leaks(TALLOC_CTX *ctx, size_t bytes, const char *location)
{
    size_t bytes_allocated;

    bytes_allocated = talloc_total_size(ctx);
    if (bytes_allocated != bytes) {
        fprintf(stderr, "Leak report for %s:\n", location);
        talloc_report_full(ctx, stderr);
        _set_leak_err_msg("%s: memory leaks detected, %zd bytes still allocated",
                          location, bytes_allocated - bytes);
        return false;
    }

    return true;
}

void
check_leaks_push(TALLOC_CTX *ctx)
{
    struct size_snapshot *snapshot;

    snapshot = talloc(NULL, struct size_snapshot);
    snapshot->ctx = ctx;
    snapshot->bytes_allocated = talloc_total_size(ctx);
    DLIST_ADD(snapshot_stack, snapshot);
}

bool
_check_leaks_pop(TALLOC_CTX *ctx, const char *location)
{
    struct size_snapshot *snapshot;
    TALLOC_CTX *old_ctx;
    size_t bytes_allocated;

    if (snapshot_stack == NULL) {
        _set_leak_err_msg("%s: trying to pop an empty stack", location);
        return false;
    }

    snapshot = snapshot_stack;
    DLIST_REMOVE(snapshot_stack, snapshot);

    old_ctx = snapshot->ctx;
    bytes_allocated = snapshot->bytes_allocated;

    if (old_ctx != ctx) {
        _set_leak_err_msg("Bad push/pop order");
        return false;
    }

    talloc_zfree(snapshot);
    return _check_leaks(old_ctx, bytes_allocated, location);
}

bool
leak_check_setup(void)
{
    talloc_enable_null_tracking();
    global_talloc_context = talloc_new(NULL);
    if (global_talloc_context == NULL) {
        _set_leak_err_msg("talloc_new failed");
        return false;
    }

    check_leaks_push(global_talloc_context);
    return true;
}

bool
leak_check_teardown(void)
{
    check_leaks_pop(global_talloc_context);
    if (snapshot_stack != NULL) {
        _set_leak_err_msg("Exiting with a non-empty stack");
        return false;
    }
    return check_leaks(global_talloc_context, 0);
}