Skip to content

mhahnFr/LeakSanitizer

Repository files navigation

LeakSanitizer

Tool to track down memory leaks.

It can be used in conjunction with code written in almost any programming language that compiles down to native machine code.
Officially supported languages are currently:

  • C
  • C++
  • Objective-C
  • Swift

This tool is available for both Linux and macOS.

Quickstart

Use the LeakSanitizer for finding memory leaks in your own applications.

Download a release here and link your application with the prebuilt framework.

Alternatively, you can build it yourself.

Usage

Use the LeakSanitizer by linking with its framework. On Linux, link with the runtime library directly.

macOS

Either embed the framework into your application or add the directory where you have put the LeakSanitizer's framework to the framework search path and the runtime search path of your application.

Tip

Example of the necessary compiler flags for Apple's Clang compiler on macOS:
-F<path/to/LeakSanitizer> -rpath <path/to/LeakSanitizer> -framework LeakSanitizer

Linux

Add the runtime library of the LeakSanitizer to your linking arguments.

Tip

Example for standard C/C++ compilers:
-L<path/to/LeakSanitizer> -llsan


Preloading

You can also inject the runtime library into a process by adding it to the preload environment variable of your dynamic linker:

  • Linux: LD_PRELOAD=<path/to/LeakSanitizer>/liblsan.so
  • macOS: DYLD_INSERT_LIBRARIES=<path/to/LeakSanitizer>/LeakSanitizer.framework/LeakSanitizer

Leak detection

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

Example:

// test.c

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

char* global;

void foo2(void) {
    global = strdup("Global variable");
}

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

void foo(void) { bar2(); }
void bar(void) { foo();  }

int main(void) {
    bar();
}

Compiled and linked on macOS with the command

cc -g test.c -L<path/to/LeakSanitizer> -llsan

this example creates the following output:

Compiled and linked on Fedora with the command

gcc -g test.c -L<path/to/LeakSanitizer> -llsan

the example above creates the following output:

Tip

Indirect memory leaks can be displayed by setting LSAN_INDIRECT_LEAKS to true:

LSAN_INDIRECT_LEAKS=true

Reachable memory leaks (those to which a pointer was found) can be hidden by setting LSAN_REACHABLE_LEAKS to false:

LSAN_REACHABLE_LEAKS=false

Source file line numbers

To add source file line information to the output (as shown above), simply compile your target with debug symbols.

Tip

Usually, the appropriate compilation option 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 behavior of this sanitizer can be adjusted by setting certain environment variables.
The following variables are currently supported:

Name Description Since Type Default value
LSAN_HUMAN_PRINT Print human-readably formatted v1.6 Boolean true
LSAN_PRINT_COUT Print to the default output stream v1.6 Boolean false
LSAN_PRINT_FORMATTED Print using ANSI escape codes v1.6 Boolean true
LSAN_INDIRECT_LEAKS Whether to print indirectly leaked allocations v1.11 Boolean false
LSAN_REACHABLE_LEAKS Whether to print leaks to whose a pointer was found v1.11 Boolean true
LSAN_CALLSTACK_SIZE The amount of frames to be printed in a callstack v1.6 Number 20
LSAN_PRINT_EXIT_POINT Print the callstack of the exit point v1.7 Boolean false
LSAN_PRINT_BINARIES Print the binary file names v1.8 Boolean true
LSAN_PRINT_FUNCTIONS Always print the function names v1.8 Boolean true
LSAN_RELATIVE_PATHS Allow relative paths to be printed v1.8 Boolean true
LSAN_INVALID_CRASH Terminate if an invalid action is detected v1.6 Boolean true
LSAN_INVALID_FREE Detect invalid deallocations v1.6 Boolean true
LSAN_FREE_NULL Issue a warning if NULL is freed v1.6 Boolean false
LSAN_ZERO_ALLOCATION Issue a warning when 0 byte are allocated v1.8 Boolean false
LSAN_STATS_ACTIVE Enable the statistical bookkeeping v1.6 Boolean false
LSAN_AUTO_STATS Time interval between the automatic statistics printing v1.11 Time interval None
LSAN_SUPPRESSION_DEVELOPER Activates more suppression developer output v1.11 Boolean false
LSAN_SUPPRESSION_FILES List of additional suppression files to be considered v1.11 File list None
LSAN_SYSTEM_LIBRARY_FILES List of additional system library files to be considered v1.11 File list None

Tip

LSAN_AUTO_STATS should be assigned a number with a time unit directly after the number.
The following time units are available:

  • ns: nanoseconds
  • us: microseconds
  • ms: milliseconds
  • s: seconds
  • m: minutes
  • h: hours

The default unit when none is given is seconds.

More on the environment variables here.

Leak suppression system

Use the memory leak suppression system introduced in version 1.11 to "remove" internal memory leaks created by the runtime libraries you additionally use.

Add your additional suppression files to the environment variable LSAN_SUPPRESSION_FILES.

Tip

Example:

LSAN_SUPPRESSION_FILES='someLibrarySuppressions.json:otherIgnoredLeaks.json'

Follow the full documentation of the suppression system in order to write your own suppression files.

Signals

This sanitizer comes with handlers for the following signals:

Signal Action
SIGUSR1 Prints the statistics if enabled using LSAN_STATS_ACTIVE or LSAN_AUTO_STATS.
SIGUSR2 Prints the current callstack.
Any deadly signal is caught and the stacktrace of the crash is printed.

Your own signal handlers take precedence. More about the signal handlers here.

Statistics

The statistics of the tracked memory can be queried at runtime. To do so activate the statistical bookkeeping by either setting the environment variable LSAN_STATS_ACTIVE to true or by using LSAN_AUTO_STATS.
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 allocations that have been deallocated.
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.

Installation

Get started by downloading a release. Alternatively, you can build the LeakSanitizer from source.

macOS

Simply move the downloaded framework anywhere you like, for example into the directory /Library/Frameworks.

Linux

Simply move the headers and the library anywhere you like.
For example, the headers can be moved into the directory /usr/local/include and the library into the directory /usr/local/lib.

Building from source

Clone the repository:

git clone --recurse-submodules https://github.com/mhahnFr/LeakSanitizer.git && cd LeakSanitizer
macOS

Build the framework using the following command:

xcodebuild

Upon successfully building the framework, it can be found in the directory ./build/Release. You can also specify another build directory by passing the option -derivedDataPath <desiredBuildPath> to the preceding command.

Tip

Example:

xcodebuild -derivedDataPath <path/to/build/folder>

The framework LeakSanitizer.framework will then be located in the directory <path/to/build/folder>/Release.

Linux

Build the library by using GNU Make:

make

It is automatically installed in the directory where the LeakSanitizer was cloned in. To install it in a specific directory, 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 (just like the downloaded release), use the following command:

make release
Dependencies

The LeakSanitizer adheres to the standard of C++23.

Additionally, the following command line tools are necessary to successfully build the LeakSanitizer:

  • GNU compatible make command line tool
  • The cat command line tool (POSIX.2)

All dependencies introduced by the CallstackLibrary are needed as well.

Uninstallation

To uninstall the LeakSanitizer, on macOS, simply remove the framework from the installation directory.

Linux

On Linux, simply remove its headers and its library. This can be done by the following command:

make INSTALL_PATH=/usr/local uninstall

Adapt the value of the INSTALL_PATH argument to your needs.

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 deallocation is registered and a stacktrace is created for it.
The allocations caused by the LeakSanitizer itself are ignored.

The signal handlers and the wrapper functions are installed once the sanitizer has been loaded by the dynamic linker. Your own signal handlers take precedence.

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

Final notes

If you experience any problems with the LeakSanitizer or if you have ideas to further improve it do not 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 - 2026 mhahnFr and contributors