Skip to content

Small library showing information about lost memory.

License

Notifications You must be signed in to change notification settings

mhahnFr/LeakSanitizer

Repository files navigation

Welcome to the LeakSanitizer!

The LeakSanitizer is a tool designed to debug memory leaks.

It can be used with almost any programming language that compiles to native machine code.
Officially supported are currently: C, C++, Objective-C, Swift.

Important

Objective-C and Swift objects are never considered to become memory leaks - even in the case of strong reference cycles.

This sanitizer has been optimized for both macOS and Linux - all memory leaks are detected on both platforms.

Quickstart

Use the LeakSanitizer to check for memory leaks as follows:

  1. Clone the repository: git clone --recursive https://github.com/mhahnFr/LeakSanitizer.git
  2. Build it: cd LeakSanitizer && make -j
  3. Link your code with: -L<path/to/library> -llsan

Tip

Update the LeakSanitizer using:

make update

More explanation can be found in the wiki; the detailed explanation follows below.

Usage

Installation

Get started by either downloading a prebuilt version of this sanitizer here.

Alternatively you can also build it from source:

  1. Clone the repository: git clone --recursive https://github.com/mhahnFr/LeakSanitizer.git
  2. go into the cloned repository: cd LeakSanitizer
  3. and build the library: make -j

Or in one step:

git clone --recursive https://github.com/mhahnFr/LeakSanitizer.git && cd LeakSanitizer && make -j

The LeakSanitizer is automatically installed in the current directory.

To install it in a specific directory you can use the following command:

make INSTALL_PATH=/usr/local install

Adapt the value of the INSTALL_PATH argument to your needs.

Tip

To create a portable build use the following command:

make -j release

If you have downloaded a release or if you have created a portable build you can simply move the headers and the library anywhere you like.

Build dependencies

The LeakSanitizer adheres to the C++17 standard.

Additionally, the following command line tools are needed to successfully build the sanitizer:

  • GNU compatible make command line tool
  • The find command line tool (POSIX.1-2001)
  • The uname command line tool (POSIX.2)

All dependencies introduced by the CallstackLibrary are necessary as well (find them here).

Uninstallation

Uninstall the sanitizer by simply removing its library and its header files from the installation directory.
This can be done using the following command:

make INSTALL_PATH=/usr/local uninstall

Adapt the value of the INSTALL_PATH argument to your needs.

Updating

Update the LeakSanitizer by either downloading the new release or, when cloned from the repository, using:

make update

Install it again as described above.

How to use

To use this sanitizer simply link your application against it (recommended) or preload its library.

Linking (recommended)

  • Add -L<path/to/library> if this sanitizer has not been installed in one of the default directories.

Link with: -llsan

Tip

  • Example: -L<path/to/library> -llsan

Preloading

Add this sanitizer's library to the dynamic loader preload environment variable:

  • macOS: DYLD_INSERT_LIBRARIES=<path/to/library>
  • Linux: LD_PRELOAD=<path/to/library>

Tip

  • Example macOS:
DYLD_INSERT_LIBRARIES=/usr/local/lib/liblsan.dylib ./a.out
  • Example Linux:
LD_PRELOAD=/usr/local/lib/liblsan.so ./a.out

Leaks

Once this sanitizer is bundled with your application the detected memory leaks are printed upon termination.

// main.c

#include <string.h>
#include <stdlib.h>

int main(void) {
    void * a = malloc(1023);
    a = strdup("Hello World!");
    a = NULL;
    a = malloc(1000);
    free(a);
}

Compiled and linked on macOS with cc main.c -g -L<path/to/library> -llsan creates the following output:

Exiting

Leak of size 13 B                   
At: (/usr/lib/system/libsystem_c.dylib) strdup + 32
in: (a.out) main (main.c:8:9)
at: (/usr/lib/dyld) start + 1903

Leak of size 1023 B                 
In: (a.out) main (main.c:7:16)
at: (/usr/lib/dyld) start + 1903


Summary: 2 leaks, 1.01 KiB lost.

Compiled and linked on Debian with gcc main.c -g -L<path/to/library> -llsan creates the following output:

Exiting

Leak of size 1023 B                 
In: (a.out) main (main.c:7:16)
at: (/usr/lib/x86_64-linux-gnu/libc.so.6) << Unknown >>
at: (/usr/lib/x86_64-linux-gnu/libc.so.6) __libc_start_main + 133
at: (a.out) _start + 33

Leak of size 13 B                   
At: (/usr/lib/x86_64-linux-gnu/libc.so.6) __strdup + 26
in: (a.out) main (main.c:8:9)
at: (/usr/lib/x86_64-linux-gnu/libc.so.6) << Unknown >>
at: (/usr/lib/x86_64-linux-gnu/libc.so.6) __libc_start_main + 133
at: (a.out) _start + 33


Summary: 2 leaks, 1.01 KiB lost.

Line numbers

Simply compile your code with debug symbols to add line numbers to the output.

Tip

Usually, the appropriate flag is -g.

Currently, debug symbols in the following formats are supported:

  • DWARF in ELF binary files
  • DWARF in Mach-O debug maps (using Mach-O object files)
  • .dSYM Mach-O bundles

The DWARF parser supports DWARF in version 2, 3, 4 and 5.

Behaviour

Since version 1.6 the behaviour of this sanitizer can be adjusted by setting the following environment variables to their indicated values:

Name Description Values Default value
LSAN_HUMAN_PRINT Print human-readably formatted true, false true
LSAN_PRINT_COUT Print to the default output stream true, false false
LSAN_PRINT_FORMATTED Print using ANSI escape codes true, false true
LSAN_INVALID_CRASH Terminate if an invalid action is detected true, false true
LSAN_INVALID_FREE Detect invalid de-allocations true, false true
LSAN_FREE_NULL Issue a warning if NULL is freed true, false false
LSAN_STATS_ACTIVE Enable the statistical bookkeeping true, false false
LSAN_LEAK_COUNT The amount of leaks to be printed in detail 0 to SIZE_MAX 100
LSAN_CALLSTACK_SIZE The amount of frames to be printed in a callstack 0 to SIZE_MAX 20
LSAN_FIRST_PARTY_THRESHOLD Since v1.7: The amount of first party frames 0 to SIZE_MAX 3
LSAN_PRINT_EXIT_POINT Since v1.7: Print the callstack of the exit point true, false false
LSAN_PRINT_BINARIES Since v1.8: Print the binary file names true, false true
LSAN_PRINT_FUNCTIONS Since v1.8: Always print the function names true, false true
LSAN_RELATIVE_PATHS Since v1.8: Allow relative paths to be printed true, false true
LSAN_ZERO_ALLOCATION Since v1.8: Issue a warning when 0 byte are allocated true, false false
LSAN_FIRST_PARTY_REGEX Since v1.8: Binary files matching this regex are considered "first party". Any regex None

More on the environment variables here.

Signals

This sanitizer comes with handlers for the following signals:

Signal Action
SIGUSR1 Printing the statistics if enabled using LSAN_STATS_ACTIVE or __lsan_statsActive.
SIGUSR2 Printing the current callstack.
Deadly signal Printing the callstack of the crash.

More on the signal handlers here.

Statistics

The statistics of the tracked memory can be queried at runtime. To do so activate the statistical bookkeeping by setting either the environment variable LSAN_STATS_ACTIVE or the variable __lsan_statsActive to true.
The statistics then can be queried using the following API:

Function Description
__lsan_getTotalMallocs() Returns the total count of allocations registered.
__lsan_getTotalBytes() Returns the total count of allocated bytes.
__lsan_getTotalFrees() Returns the total count of registered and freed objects.
__lsan_getCurrentMallocCount() Returns the count of currently active allocations.
__lsan_getCurrentByteCount() Returns the amount of currently allocated bytes.
__lsan_getMallocPeek() Returns the highest amount of allocations at the same time.
__lsan_getBytePeek() Returns the highest amount of bytes allocated at the same time.
__lsan_printStats() Prints the statistics to the output stream specified by LSAN_PRINT_COUT.
__lsan_printFStats() Prints the fragmentation statistics to the output stream specified by LSAN_PRINT_COUT.

More on the statistics here.

Behind the scenes or: How does it work?

In order to track the memory allocations this sanitizer replaces the common allocation management functions such as malloc, calloc, realloc and free. Every allocation and de-allocation is registered and a backtrace is stored for it.
Its own allocations are not tracked.

The signal handlers and the wrapper functions are installed once the sanitizer has been loaded by the dynamic loader.

When the exit handler registered using atexit is invoked the allocated memory is examined and the detected memory leaks are printed.
The backtraces are translated using the CallstackLibrary.

Final notes

If you experience any problems with the LeakSanitizer or if you have ideas to further improve it don't hesitate to open an issue or to open a pull request.

This project is licensed under the terms of the GNU GPL in version 3 or later.

© Copyright 2022 - 2024 mhahnFr and contributors