Skip to content

Commit

Permalink
libbpf-tools: Add new feature lsan on libbpf-tools
Browse files Browse the repository at this point in the history
Add leaksanitizer(lsan) feature on libbpf-tools
lsan feature originally comes from llvm-project
https://github.com/llvm/llvm-project
cvector.h comes from c-vector project
commit d3f3156373b0587336ac7ee1568755d6cf93dd39
https://github.com/eteran/c-vector
uthash.h comes from uthash project
commit bf15263081be6229be31addd48566df93921cb46
https://github.com/troydhanson/uthash
This tool detect and report dangling pointers periodically

Usage: lsan [OPTION...]
Detect memory leak resulting from dangling pointers.

Either -c or -p is a mandatory option
EXAMPLES:
    lsan -p 1234             # Detect leaks on process id 1234
    lsan -c a.out            # Detect leaks on a.out
    lsan -c 'a.out arg'      # Detect leaks on a.out with argument
    lsan -c "a.out arg"      # Detect leaks on a.out with argument

  -c, --command=COMMAND      Execute and trace the specified command
  -i, --interval=INTERVAL    Set interval in second to detect leak
  -p, --pid=PID              Set pid
  -s, --suppressions=SUPPRESSIONS
                             Suppressions file name
  -T, --top=TOP              Report only specified amount of backtraces
  -v, --verbose              Verbose debug output
  -w, --stop-the-world       Stop the world during tracing
  -?, --help                 Give this help list
      --usage                Give a short usage message
  -V, --version              Print program version

Mandatory or optional arguments to long options are also mandatory or optional
for any corresponding short options.

Report example:
$ sudo ./lsan -c a.out -s suppr.txt
Info: Execute child process: a.out
Info: execute command: a.out(pid 8335)

[2022-07-19 16:07:26] Print leaks:
Could not open [uprobes]
4 bytes direct leak found in 1 allocations from stack id(19863)
        iovisor#1 0x00564465ed71bf foo
        iovisor#2 0x00564465ed721b main
        iovisor#3 0x007fc0a33f7d90 __libc_init_first

[2022-07-19 16:07:36] Print leaks:
8 bytes direct leak found in 2 allocations from stack id(19863)
        iovisor#1 0x00564465ed71bf foo
        iovisor#2 0x00564465ed721b main
        iovisor#3 0x007fc0a33f7d90 __libc_init_first

[2022-07-19 16:07:46] Print leaks:
12 bytes direct leak found in 3 allocations from stack id(19863)
        iovisor#1 0x00564465ed71bf foo
        iovisor#2 0x00564465ed721b main
        iovisor#3 0x007fc0a33f7d90 __libc_init_first

Source code of test program:
\#include <stdio.h>
\#include <stdlib.h>
\#include <unistd.h>

int* foo() {
        int *tmp = malloc(sizeof(int));
        *tmp = 99;
        return tmp;
}

int* bar() {
        int *tmp = malloc(sizeof(int));
        *tmp = 22;
        return tmp;
}

int main() {
        int *a = NULL;
        while (1) {
                a = foo();
                printf("%d\n", *a);
                a = bar();
                free(a);
                sleep(10);
        }
}
  • Loading branch information
Bojun-Seo committed Nov 2, 2023
1 parent b8b943a commit 27564f6
Show file tree
Hide file tree
Showing 7 changed files with 2,833 additions and 0 deletions.
1 change: 1 addition & 0 deletions libbpf-tools/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@
/klockstat
/ksnoop
/llcstat
/lsan
/memleak
/mdflush
/mountsnoop
Expand Down
1 change: 1 addition & 0 deletions libbpf-tools/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ APPS = \
klockstat \
ksnoop \
llcstat \
lsan \
mdflush \
mountsnoop \
numamove \
Expand Down
294 changes: 294 additions & 0 deletions libbpf-tools/cvector.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,294 @@
/*
* Copyright (c) 2015 Evan Teran
*
* License: The MIT License (MIT)
*/

#ifndef CVECTOR_H_
#define CVECTOR_H_

#include <assert.h> /* for assert */
#include <stdlib.h> /* for malloc/realloc/free */
#include <string.h> /* for memcpy/memmove */

/* cvector heap implemented using C library malloc() */

/* in case C library malloc() needs extra protection,
* allow these defines to be overridden.
*/
#ifndef cvector_clib_free
#define cvector_clib_free free
#endif
#ifndef cvector_clib_malloc
#define cvector_clib_malloc malloc
#endif
#ifndef cvector_clib_calloc
#define cvector_clib_calloc calloc
#endif
#ifndef cvector_clib_realloc
#define cvector_clib_realloc realloc
#endif

typedef void (*cvector_elem_destructor_t)(void *elem);

/**
* @brief cvector_vector_type - The vector type used in this library
*/
#define cvector_vector_type(type) type *

/**
* @brief cvector_capacity - gets the current capacity of the vector
* @param vec - the vector
* @return the capacity as a size_t
*/
#define cvector_capacity(vec) \
((vec) ? ((size_t *)(vec))[-1] : (size_t)0)

/**
* @brief cvector_size - gets the current size of the vector
* @param vec - the vector
* @return the size as a size_t
*/
#define cvector_size(vec) \
((vec) ? ((size_t *)(vec))[-2] : (size_t)0)

/**
* @brief cvector_set_elem_destructor - set the element destructor function
* used to clean up removed elements
* @param vec - the vector
* @return elem_destructor_fn - function pointer of type cvector_elem_destructor_t
* @return the function pointer elem_destructor_fn or NULL on error
*/
#define cvector_set_elem_destructor(vec, elem_destructor_fn) \
do { \
if (!(vec)) { \
cvector_grow((vec), 0); \
} \
((cvector_elem_destructor_t *)&(((size_t *)(vec))[-2]))[-1] = (elem_destructor_fn); \
} while (0)

/**
* @brief cvector_elem_destructor - get the element destructor function used
* to clean up elements
* @param vec - the vector
* @return the function pointer as cvector_elem_destructor_t
*/
#define cvector_elem_destructor(vec) \
((vec) ? (((cvector_elem_destructor_t *)&(((size_t *)(vec))[-2]))[-1]) : NULL)

/**
* @brief cvector_empty - returns non-zero if the vector is empty
* @param vec - the vector
* @return non-zero if empty, zero if non-empty
*/
#define cvector_empty(vec) \
(cvector_size(vec) == 0)

/**
* @brief cvector_reserve - Requests that the vector capacity be at least enough
* to contain n elements. If n is greater than the current vector capacity, the
* function causes the container to reallocate its storage increasing its
* capacity to n (or greater).
* @param vec - the vector
* @param n - Minimum capacity for the vector.
* @return void
*/
#define cvector_reserve(vec, capacity) \
do { \
size_t cv_cap__ = cvector_capacity(vec); \
if (cv_cap__ < (capacity)) { \
cvector_grow((vec), (capacity)); \
} \
} while (0)

/**
* @brief cvector_erase - removes the element at index i from the vector
* @param vec - the vector
* @param i - index of element to remove
* @return void
*/
#define cvector_erase(vec, i) \
do { \
if ((vec)) { \
const size_t cv_sz__ = cvector_size(vec); \
if ((i) < cv_sz__) { \
cvector_set_size((vec), cv_sz__ - 1); \
memmove((vec) + (i), (vec) + (i) + 1, sizeof(*(vec)) * (cv_sz__ - 1 - (i))); \
} \
} \
} while (0)

/**
* @brief cvector_free - frees all memory associated with the vector
* @param vec - the vector
* @return void
*/
#define cvector_free(vec) \
do { \
if ((vec)) { \
size_t *p1__ = (size_t *)&(((cvector_elem_destructor_t *)&(((size_t *)(vec))[-2]))[-1]); \
cvector_elem_destructor_t elem_destructor__ = cvector_elem_destructor((vec)); \
if (elem_destructor__) { \
size_t i__; \
for (i__ = 0; i__ < cvector_size(vec); ++i__) \
elem_destructor__(&vec[i__]); \
} \
cvector_clib_free(p1__); \
} \
} while (0)

/**
* @brief cvector_begin - returns an iterator to first element of the vector
* @param vec - the vector
* @return a pointer to the first element (or NULL)
*/
#define cvector_begin(vec) \
(vec)

/**
* @brief cvector_end - returns an iterator to one past the last element of the vector
* @param vec - the vector
* @return a pointer to one past the last element (or NULL)
*/
#define cvector_end(vec) \
((vec) ? &((vec)[cvector_size(vec)]) : NULL)

/* user request to use logarithmic growth algorithm */
#ifdef CVECTOR_LOGARITHMIC_GROWTH

/**
* @brief cvector_compute_next_grow - returns an the computed size in next vector grow
* size is increased by multiplication of 2
* @param size - current size
* @return size after next vector grow
*/
#define cvector_compute_next_grow(size) \
((size) ? ((size) << 1) : 1)

#else

/**
* @brief cvector_compute_next_grow - returns an the computed size in next vector grow
* size is increased by 1
* @param size - current size
* @return size after next vector grow
*/
#define cvector_compute_next_grow(size) \
((size) + 1)

#endif /* CVECTOR_LOGARITHMIC_GROWTH */

/**
* @brief cvector_push_back - adds an element to the end of the vector
* @param vec - the vector
* @param value - the value to add
* @return void
*/
#define cvector_push_back(vec, value) \
do { \
size_t cv_cap__ = cvector_capacity(vec); \
if (cv_cap__ <= cvector_size(vec)) { \
cvector_grow((vec), cvector_compute_next_grow(cv_cap__)); \
} \
(vec)[cvector_size(vec)] = (value); \
cvector_set_size((vec), cvector_size(vec) + 1); \
} while (0)

/**
* @brief cvector_insert - insert element at position pos to the vector
* @param vec - the vector
* @param pos - position in the vector where the new elements are inserted.
* @param val - value to be copied (or moved) to the inserted elements.
* @return void
*/
#define cvector_insert(vec, pos, val) \
do { \
if (cvector_capacity(vec) <= cvector_size(vec) + 1) { \
cvector_grow((vec), cvector_compute_next_grow(cvector_capacity((vec)))); \
} \
if ((pos) < cvector_size(vec)) { \
memmove((vec) + (pos) + 1, (vec) + (pos), sizeof(*(vec)) * ((cvector_size(vec) + 1) - (pos))); \
} \
(vec)[(pos)] = (val); \
cvector_set_size((vec), cvector_size(vec) + 1); \
} while (0)

/**
* @brief cvector_pop_back - removes the last element from the vector
* @param vec - the vector
* @return void
*/
#define cvector_pop_back(vec) \
do { \
cvector_elem_destructor_t elem_destructor__ = cvector_elem_destructor((vec)); \
if (elem_destructor__) \
elem_destructor__(&(vec)[cvector_size(vec) - 1]); \
cvector_set_size((vec), cvector_size(vec) - 1); \
} while (0)

/**
* @brief cvector_copy - copy a vector
* @param from - the original vector
* @param to - destination to which the function copy to
* @return void
*/
#define cvector_copy(from, to) \
do { \
if ((from)) { \
cvector_grow(to, cvector_size(from)); \
cvector_set_size(to, cvector_size(from)); \
memcpy((to), (from), cvector_size(from) * sizeof(*(from))); \
} \
} while (0)

/**
* @brief cvector_set_capacity - For internal use, sets the capacity variable of the vector
* @param vec - the vector
* @param size - the new capacity to set
* @return void
*/
#define cvector_set_capacity(vec, size) \
do { \
if ((vec)) { \
((size_t *)(vec))[-1] = (size); \
} \
} while (0)

/**
* @brief cvector_set_size - For internal use, sets the size variable of the vector
* @param vec - the vector
* @param size - the new capacity to set
* @return void
*/
#define cvector_set_size(vec, size) \
do { \
if ((vec)) { \
((size_t *)(vec))[-2] = (size); \
} \
} while (0)

/**
* @brief cvector_grow - For internal use, ensures that the vector is at least <count> elements big
* @param vec - the vector
* @param count - the new capacity to set
* @return void
*/
#define cvector_grow(vec, count) \
do { \
const size_t cv_sz__ = (count) * sizeof(*(vec)) + sizeof(size_t) * 2 + sizeof(cvector_elem_destructor_t); \
if ((vec)) { \
cvector_elem_destructor_t *cv_p1__ = &((cvector_elem_destructor_t *)&((size_t *)(vec))[-2])[-1]; \
cvector_elem_destructor_t *cv_p2__ = cvector_clib_realloc(cv_p1__, cv_sz__); \
assert(cv_p2__); \
(vec) = (void *)&((size_t *)&cv_p2__[1])[2]; \
} else { \
cvector_elem_destructor_t *cv_p__ = cvector_clib_malloc(cv_sz__); \
assert(cv_p__); \
(vec) = (void *)&((size_t *)&cv_p__[1])[2]; \
cvector_set_size((vec), 0); \
((cvector_elem_destructor_t *)&(((size_t *)(vec))[-2]))[-1] = NULL; \
} \
cvector_set_capacity((vec), (count)); \
} while (0)

#endif /* CVECTOR_H_ */
Loading

0 comments on commit 27564f6

Please sign in to comment.