MALT(1)
π MALT(1) π
okay bestie, welcome to malt, fresh out of section 1, General Commands Manual β and it's giving memory profiling main character energy. π
π NAME π
malt β a memory profiling tool. short, sweet, does exactly what it says on the tin β¨
π¦ SYNOPSIS π¦
malt [MALT_OPTIONS...] {YOUR_PROGRAM} [YOUR_OPTIONS]
so it's literally: your options first, then the program you're profiling, then whatever options that program wants. no cap. π
πΈ DESCRIPTION πΈ
okay so here's the tea: MALT is a memory profiling tool that wraps your system memory allocation and applies a backtrace on every single call. π«Ά
and what do you get for it? a whole memory allocation profile, which you can then go look at with malt-webview(1). iconic behavior honestly.
MALT works out of the box β like, no setup arc required. but if you actually want to profile your application properly, it might be better to compile it with debug symbols (-g) and also to disable inlining so you get more accurate stacks (-O0 or -fno-inline). trust me, your future self will thank you. π
β¨ OPTIONS β¨
-c,--configβ give it a config file as parameters, with the formats described in the next part of this manpage. you can also get a dump of the default configuration with-d. π-s,--stackβ pick which stack tracking mode to use. it can bebacktrace(the default) to use thebacktracefunction from glibc. if you compile with libunwind you can also use the libunwind function in place of glibcbacktrace. it fixes some bugs with programs compiled with the Intel compiler or using Intel libraries (like Intel OpenMP) β but plot twist, it has a side effect, because libunwind has an internal memory leak. π the other approach is to instrument your code with-finstrument-function, and in that case you need to useenter-exitto use it.-q,--quietβ make MALT silent so it doesn't mix with the default output of your program. super useful if you're using the output to feed a script. π€«-v,--verboseβ add more infos about MALT running. π£οΈ-d,--dumpconfigβ dump the current MALT configuration into a file with the same name as the profile file. it's a whole quick-start for configuring stuff, because you get all the options and their default value. π₯-oβ lets you pass some specific options from the config file on the command line. the value is a list of options separated by commas, formatted likeSECTION:NAME=VALUE. see the configuration file format (INI format) to find the available sections and names. π--mpiβ enable MPI support so the output files get named with rank IDs instead of process IDs. MALT willLD_PRELOADthe library bridge from~/.malt/libmaltmpi.so. π--prep-mpiβ build the MPI bridge library:~/.malt/libmaltmpi.so. MALT requires it to get access to theMPI_Comm_rankfunction, depending on your MPI implementation. you need to do this command each time you switch your MPI implementation. each time, bestie. π--wrapβ lets you wrap and capture a custom memory allocator. the wrap option expects a list of functions to wrap separated by commas (or you can call it multiple times). the values should give the type of function (malloc,free,realloc,calloc...) and the symbol to wrap, on the form:{type}:{symbol}. π§--wrap-prefixβ automatically wrap all the memory management functions under the given prefix. you can list several prefixes separated by comma. for example, givingje_will wrap all theje_malloc,je_free,je_realloc... functions. slay. π--gdbβ run the program inside GDB, and automatically configure GDB so it works properly with MALT. π-h,--helpβ display the help message with a list of available command line options. the classic. π
π USING ON MPI APPLICATIONS π
MALT natively provides a lightweight support of MPI applications. each process is tracked separately, but MALT can help by numbering the output files with the task rank instead of the process ID. to get this, you need to compile a small wrapper adapted to your current MPI implementation. you only need to do it once:
malt --prep-mpi [mpicxx]
then make sure to use the --mpi option on the profiling tool:
mpirun malt --mpi ./you_program
and that's the whole ritual. periodt. π«Ά
πΌοΈ GET THE GUI πΌοΈ
with MALT you can use kcachegrind directly β but only if you enable the valgrind compatible format in the configuration (output:callgrind). β
but by default? MALT generates a JSON file to be used with malt-webview(1). so yeah, the pretty pictures are one step away. πΈ
π CONFIGURATION FILE π
you can get an example of this config file with the -d option, and then set up the config file to use with the -c one. the config file uses INI format with sections and variables, as in this example:
[time]
enabled = true
points = 512
linear_index = false
[stack]
enabled = true
mode = backtrace
resolve = true
libunwind = false
skip = 4 sampling = false samplingBw = 4093
[output]
name = malt-%1-%2.%3
lua = false
json = true
callgrind = false
indent = true
config = true
verbosity = default
stack-tree = false
loop-suppress = false
[max-stack]
enabled = true
[distr]
alloc-size = true
realloc-jump = true
[trace]
enabled = false
[info]
hidden = false
[filter]
exec =
childs = true
enabled = true ranks =
[dump]
on-signal =
after-seconds = 0
on-sys-full-at =
on-app-using-rss =
on-app-using-virt =
on-app-using-req =
on-thread-stack-using = watch-dog = false
[python] instru = true stack = enter-exit mix = false obj = true mem = true raw = true
[tools] nm = true nmMaxSize = 50M
β° the time section supports:
enabledβ enable support of tracking state values over time, so you can build those cute time charts. πpointsβ define the number of points used to discretize the execution time of the application.linear_indexβ don't use time to index data, but a linear value increased on each call. it might be interesting so you don't shrink intensive allocation steps on a long program that mostly doesn't do allocation over the run. π¦
π₯ the stack section supports:
enabled: enable support of stack tracking.mode: overridden by the-soption from the command line β it sets the stack tracking method to use. see the-sdocumentation for more details. the available values areenter-exit,backtrace,python,none. by defaultbacktraceis used, because it works out of the box everywhere. it's slower thanenter-exit, but this last one needs recompilation of the code with-finstrument-functionsand only sees the libs recompiled with this option.pythonlets you project all the C allocation directly into the python stack domain, so it hides the C underwood β good to improve performance over python when you're not interested in the C details. πresolve: enable symbol resolution at the end of execution, to extract full names and source location if debug options are available. πlibunwind: use the linunwind backtrace method instead of the one from glibc.skip: inbacktracemode, the backtrace is called inside our instrumentation function, itself called bymalloc/free... in order to skip our internal code we need to remove them. but on some compilers or distros, inlining and LTO configuration make wrong detection. this can be fixed by playing with this value to keepmalloc/free/callocas leaf for every calltree extracted by MALT. if you don't seemallocas leaf, you should increase this value. decrease it if you start to see MALT internal functions inside. notice that in some cases (Gentoo, Gcc-8) we start to see LTO totally trashing themalloc/freecall inO3mode, normally seen via backtrace. there is currently no other solution except recompiling inO0/O1to avoid it, or maybe disabling LTO optimizations, or considering not having the exact location of themallocitself. πsampling: enable sampling mode for the stacks, by capturing only for a few mallocs. it's less accurate than a full profile, but it costs less in memory and CPU. in sampling mode, the stack is processed only when we've seen passed a few bytes allocated β otherwise we consider the last seen call stack. with python you should also enable thebacktracemode for solving stacks viapython:stack=backtrace. π―samplingBw: define the amount of data seen passed between two samples. ideally this should be a prime number to avoid some multiple base biases. π’
π¦ the output section supports:
name: define the name of the profile file.%1is replaced by the program name,%2by the PID or MPI rank, and%3by the extension. πlua: enable output in LUA format (same structure as JSON files, but in LUA).json: enable output of the default JSON file format.callgrind: enable output of the compatibility format with callgrind/kcachegrind. it cannot contain all the data, but it can be used with compatible existing tools.indent: enable indentations in the JSON/LUA files. useful for debugging, but it generates bigger files. π«§config: dump the config INI file.verbosity: set the verbosity mode of MALT. by default it prints at start and end. you can use silent mode to disable any output β for example if you instrument a shell script parsing the output of child processes. you can also use verbose to get more debugging infos in case it doesn't work as expected, mostly at the symbol extraction step while dumping outputs. π£οΈstack-tree: enable storage of the stacks as a tree inside the output file. it produces smaller files, but it requires conversion at storage time and loading time to stay compatible with the basic expected format. you can use this option to get smaller files β in one case it lowers a 600 MB file to 200 MB, just to give you an idea. π³loop-suppress: enable recursive loop calls to be removed, providing a more simplified equivalent call stack. it helps reduce the size of profiles from applications using intensively this kind of call chain. in one case it lowers the file from 200 MB to 85 MB. it can help if nodejs failed to load the file because of the size. this parameter can also provide more readable stacks, since you don't care too much about how many times you cycle to call loops β you just want to see one of them. π
π§± the max-stack section support:
Continuing in the config file β the stack section has one more key:
enabledβ flips tracking of stack size and memory used by functions on stacks on or off. Needs--finstrument-functionon your code to actually provide data, bestie. β¨
The distr section support
alloc-sizeβ generates a distribution of the allocated chunk size.realloc-jumpβ generates a distribution of thereallocsize jumps.
The trace section support
enabledβ turns tracing on or off. Currently not used by the GUI β work in progress, iykyk. π«
The info section support
enabledβ hides execution information. This removes some possibly sensitive information from the output file, like executable names, hostname and command options. Itβs still recommended to take a look at the file, for example to replace paths withsed(1), since those might also need removing. This option targets some companies that might want to hide their internal applications when exchanging with external partners. π΅οΈββοΈ
The filter section support
exeβ enables filtering of executables so MALT is enabled for the ones you want and ignored otherwise. By default, an empty value enables MALT on all executables.childsβ whether to instrument children processes or not. By default it instruments all.enabledβ enables profiling by default. You can disable it so you can activate it via a C function call in the app whenever you want.ranksβ when running in MPI mode, instrument only the given ranks. The list is provided under the form1,2-4,6.
The dump section β dump the profile whenever you want π«Ά
on-signalβ dumps on the given signal. Can beonor a comma-separated list of:SIGINT,SIGUSR1,SIGUSR2, ... Heads up: profiling will currently stop from this point, and the app will continue without profiling. To be fixed later (their βlatterβ, honestly same energy). You can get the list of available ones by usinghelporavailin place of the name.after-secondsβ dumps the profile after X seconds. Same caveat: profiling will currently stop from this point, and the app will continue without profiling. To be fixed later.on-sys-full-atβ dumps if the system memory is globally filled at x%. Use an empty string to disable. It considers free memory and cached memory as free. A good value in practice is more like 70% / 80% than going to 95%, because you need to leave room for cached memory and it will start swapping before that. Also consider that MALT itself adds memory on top of yours (and thatβs considered in the % here). Values can be in%,K,M,Gby ending with the corresponding character.on-app-using-rssβ dumps if the application reaches the given RSS limit. The value is given in % of the global memory available or inK,M,G. Empty to disable (default).on-app-using-virtβ dumps if the application reaches the given virtual memory limit. The value is given in % of the global memory available or inK,M,G. Empty to disable (default).on-app-using-reqβ dumps if the application reaches the given requested memory limit. The value is given in % of the global memory available or inK,M,G. Empty to disable (default).on-thread-stack-usingβ dumps if one of the thread stacks reaches the given limit. The value is given in % of the global memory available or inK,M,G. Empty to disable (default).watch-dogβ starts a thread that spies on the memory usage of the process and triggers the dump as soon as it sees it going high. This balances the fact that, normally, system and process memory is spied on only sometimes by MALT in normal conditions to keep the overhead low.
The python section β configure how to instrument Python π
Notice that you need to build MALT with --enable-python for this to be effective.
instruβ enable or disable Python instrumentation.stackβ select the stack instrumentation mode: eitherenter-exit,backtraceornone. By defaultenter-exitis faster, so you should use it. When enabling sampling, you need to usebacktraceβ the page saysbactrace, and we love that for them β if you donβt want to pay unneeded overhead. You can also disable Python stack checking withnone.mixβ by default, when disabled, the C and Python stacks are analysed independently, which means that if a Python function calls a C function youβll see only the C call stack.mixlets you merge the two layers so you see that Python calls C. But it adds overhead on the analysis, of course, because of the extra work.objβ analyse or ignore the object allocation domain of Python. This is interesting to ignore all the small allocs used by the language, as it would have been on the stack in C. It improves Python profiling performance a lot, but misses part of the memory consumption if your program stores lots of small objects for long times.memβ same but for the mem allocation domain of Python. In principle you should let it stay enabled.rawβ same but for the raw allocation domain of Python, which is backed by the standard Cmallocfunction. In principle you should let it stay enabled.
The tools section β configure some sub-tools MALT calls for analysis π§°
nm: use to extract the source location of the global variables. If true (default) it is used, otherwise it is skipped.nmMaxSize: by default it limits the size of the.sofiles on which to applyNMin order to keep a decent profile dumping time when running on large frameworks likePyTorch, which tends to load huge.sofiles in memory.
ENVIRONMENT π
You can also directly LD_PRELOAD the MALT library in your program if the wrapper script has some issues for you.
LD_PRELOAD={PREFIX}/lib/libmalt.so {YOUR_PROGRAM}
In that case you can set up some environment variables to configure MALT:
MALT_CONFIGβ define the configuration file to use (equivalent of-coption).MALT_STACKβ define the stack mode to use (equivalent of-soption).MALT_OPTIONSβ list of options to transmit to MALT (equivalent of-ooption, see its documentation for format).
ON SUB-PART OF YOUR PROGRAM π―
If you run on a really big program doing millions of allocations you might get a big overhead, and maybe youβre just interested in a sub-part of the program. You can do it by including malt/malt.h in your files and use maltEnable() and maltDisable() to control MALT on each thread. Itβs also a nice way to detect leaks of sub-parts of your code.
#include <malt/malt.h>
int main()
{
maltDisable();
//ignored
malloc(16);
maltEnable();
//tracked
malloc(16);
}
You will need to link the libmalt-controler.so to get the default fake symbols when not using MALT. You can also just provide the two empty functions in your own dynamic library (not static).
If you have some allocation not under your control before your first call, you can disable MALT by default on threads using the filter:enabled option, then enable it by hand.
WRAPPING A CUSTOM ALLOCATOR π§΅
If your application uses a custom allocator with a different namespace than the default malloc, free... you can use the --wrap or --wrap-prefix options.
You can select in detail the function by doing:
malt --wrap malloc:je_malloc ./prgm
malt --wrap malloc:je_malloc,free:je_free,calloc:je_calloc,malloc:another_custom_malloc ./prgm
You can also simply use a common prefix for all by using (typically useful if you embed jemalloc with a custom symbol prefix):
malt --wrap-prefix je_
malt --wrap-prefix je_,another_custom_
VERSION
This is the manpage of MALT version 1.4.1.
SEE ALSO
xoxo, bestie π β this page is MALT 1.4.1, dated October 2025, and the footer says MALT(1). slay π¦
π¦ where this page comes from
- nixos 26.05: malt 1.4.1
- nixpkgs unstable: malt 1.4.1
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms