LIBMEMSTAT(3)
okay so bestie, welcome to libmemstat(3) ๐ โ yes that one, the page from the Library Functions Manual, and she is literally the group chat between your program and the kernel's memory allocators. no cap, this thing slaps. let's get into it โจ
โจ NAME โจ
libmemstat โ library interface to retrieve kernel memory allocator statistics ๐
like, that's her whole job. she goes and gets the numbers, you stay comfy. periodt.
๐ LIBRARY ๐
Kernel Memory Allocator Statistics Library (libmemstat, -lmemstat) ๐ฆ
yeah, that's the library name and the linker flag, they're matching, it's giving coordinated outfit ๐ฅ
๐ SYNOPSIS ๐
okay here's the doorway, bestie, you gotta walk through it first:
#include <sys/types.h>
#include <memstat.h>
both of them. yes both. don't skip the first one she gets moody ๐ธ
๐ซถ General Functions ๐ซถ
const char *
memstat_strerror(int error);
one function, one job: you hand her an int error, she hands you back a const char *. iconic, efficient, no notes ๐
๐ Memory Type List Management Functions ๐
this whole crew is about the list โ making it, strolling through it, finding stuff in it, yeeting it, and asking it how it's feeling:
struct memory_type_list *
memstat_mtl_alloc(void);
struct memory_type *
memstat_mtl_first(struct memory_type_list *list);
struct memory_type *
memstat_mtl_next(struct memory_type *mtp);
struct memory_type *
memstat_mtl_find(struct memory_type_list *list, int allocator,
const char *name);
void
memstat_mtl_free(struct memory_type_list *list);
int
memstat_mtl_geterror(struct memory_type_list *list);
see how memstat_mtl_alloc and memstat_mtl_first and memstat_mtl_next and memstat_mtl_find all return a struct memory_type * (well, struct memory_type_list * for the alloc one, she's special) โ and memstat_mtl_free returns void because she takes rather than gives ๐ซถ
also ngl memstat_mtl_find is the main character here โ she wants a list, an allocator, and a name, miss girl is thorough. ๐
๐ฅ Allocator Query Functions ๐ฅ
six functions, all returning int, and they come in two flavours โ the kvm girlies and the sysctl girlies:
int
memstat_kvm_all(struct memory_type_list *list, void *kvm_handle);
int
memstat_kvm_malloc(struct memory_type_list *list, void *kvm_handle);
int
memstat_kvm_uma(struct memory_type_list *list, void *kvm_handle);
int
memstat_sysctl_all(struct memory_type_list *list, int flags);
int
memstat_sysctl_malloc(struct memory_type_list *list, int flags);
int
memstat_sysctl_uma(struct memory_type_list *list, int flags);
notice the pattern bestie โ every single one wants a struct memory_type_list *list first. then the kvm ones want a void *kvm_handle and the sysctl ones want an int flags. the way that they all match is lowkey satisfying โจ and yes, all, malloc and uma each get their own version in both families, she's consistent ๐
๐ง Memory Type Accessor Methods ๐ง
okay this is the big one, hydrate before you read it ๐ literally the entire getter wardrobe:
const char *
memstat_get_name(const struct memory_type *mtp);
int
memstat_get_allocator(const struct memory_type *mtp);
uint64_t
memstat_get_countlimit(const struct memory_type *mtp);
uint64_t
memstat_get_byteslimit(const struct memory_type *mtp);
uint64_t
memstat_get_sizemask(const struct memory_type *mtp);
uint64_t
memstat_get_size(const struct memory_type *mtp);
uint64_t
memstat_get_rsize(const struct memory_type *mtp);
uint64_t
memstat_get_memalloced(const struct memory_type *mtp);
uint64_t
memstat_get_memfreed(const struct memory_type *mtp);
uint64_t
memstat_get_numallocs(const struct memory_type *mtp);
uint64_t
memstat_get_numfrees(const struct memory_type *mtp);
uint64_t
memstat_get_bytes(const struct memory_type *mtp);
uint64_t
memstat_get_count(const struct memory_type *mtp);
uint64_t
memstat_get_free(const struct memory_type *mtp);
uint64_t
memstat_get_failures(const struct memory_type *mtp);
void *
memstat_get_caller_pointer(const struct memory_type *mtp, int index);
void
memstat_set_caller_pointer(struct memory_type *mtp, int index,
void *value);
uint64_t
memstat_get_caller_uint64(const struct memory_type *mtp, int index);
void
memstat_set_caller_uint64(struct memory_type *mtp, int index,
uint64_t value);
uint64_t
memstat_get_zonefree(const struct memory_type *mtp);
uint64_t
memstat_get_kegfree(const struct memory_type *mtp);
uint64_t
memstat_get_percpu_memalloced(const struct memory_type *mtp, int cpu);
uint64_t
memstat_get_percpu_memfreed(const struct memory_type *mtp, int cpu);
uint64_t
memstat_get_percpu_numallocs(const struct memory_type *mtp, int cpu);
uint64_t
memstat_get_percpu_numfrees(const struct memory_type *mtp, int cpu);
uint64_t
memstat_get_percpu_sizemask(const struct memory_type *mtp, int cpu);
void *
memstat_get_percpu_caller_pointer(const struct memory_type *mtp,
int cpu, int index);
void
memstat_set_percpu_caller_pointer(struct memory_type *mtp, int cpu,
int index, void *value);
uint64_t
memstat_get_percpu_caller_uint64(const struct memory_type *mtp, int cpu,
int index);
void
memstat_set_percpu_caller_uint64(struct memory_type *mtp, int cpu,
int index, uint64_t value);
uint64_t
memstat_get_percpu_free(const struct memory_type *mtp, int cpu);
okay let's break down the lore ๐
- the plain getters all take just a
const struct memory_type *mtpand most of them returnuint64_t. the exceptions arememstat_get_name, which returnsconst char *, andmemstat_get_allocator, which returnsint. the rest of the fam:memstat_get_countlimit,memstat_get_byteslimit,memstat_get_sizemask,memstat_get_size,memstat_get_rsize,memstat_get_memalloced,memstat_get_memfreed,memstat_get_numallocs,memstat_get_numfrees,memstat_get_bytes,memstat_get_count,memstat_get_free,memstat_get_failures,memstat_get_zonefree,memstat_get_kegfreeโ alluint64_t, all justmtp. ๐ - the caller ones bring a plus-one: they're
memstat_get_caller_pointerandmemstat_get_caller_uint64taking(const struct memory_type *mtp, int index)โ and thevoid *return onmemstat_get_caller_pointeris a whole different vibe from theuint64_ton the other. ๐ - the setters exist too, bestie โ
memstat_set_caller_pointerandmemstat_set_caller_uint64, bothvoid, both taking a non-conststruct memory_type *mtp(she's mutable, she's getting written to, respect it) plusint indexand thevoid *value/uint64_t valuerespectively. ๐ญ - the per-cpu girlies are all of that again but with
int cpusqueezed in, and they're the ones with_percpu_right there in the name:memstat_get_percpu_memalloced,memstat_get_percpu_memfreed,memstat_get_percpu_numallocs,memstat_get_percpu_numfrees,memstat_get_percpu_sizemask,memstat_get_percpu_free(alluint64_t), plusmemstat_get_percpu_caller_pointerreturningvoid *and taking(const struct memory_type *mtp, int cpu, int index). and yes the setters are back:memstat_set_percpu_caller_pointerandmemstat_set_percpu_caller_uint64, bothvoid, both non-constmtp, both(struct memory_type *mtp, int cpu, int index, โฆ)withvoid *valueanduint64_t value๐
it's giving a lot of getters, and honestly? obsessed. every single stat you could want has her own little door. no cap. ๐ฅ
and that's the whole synopsis, bestie โ the actual prose is up next, so don't go anywhere ๐
๐ซ DESCRIPTION ๐ซ
okay bestie, so libmemstat is the library that gives you kernel memory allocator stats ๐ซถ. It exists for debugging and system monitoring, it keeps applications insulated from the implementation details of the allocators, and it lets a tool transparently support multiple allocators. No cap. It can grab a single statistics snapshot, and it can also incrementally update stats for long-term monitoring.
libmemstat describes each memory type with a struct memory_type, an opaque memory type that your app touches through accessor functions in the library. The library returns and updates chains of struct memory_type through a struct memory_type_list, which gets allocated by calling memstat_mtl_alloc() and freed when youโre done using memstat_mtl_free(). Lists of memory types get populated by calls that query the kernel for statistics information; currently: memstat_kvm_all(), memstat_kvm_malloc(), memstat_kvm_uma(), memstat_sysctl_all(), memstat_sysctl_uma(), and memstat_sysctl_malloc(). Repeated calls will incrementally update the list of memory types, so you can track stuff over time without recreating all list state. Periodt. If an error is detected during a query call, you can retrieve error condition information with memstat_mtl_geterror(), and convert it into a user-readable string with memstat_strerror().
๐ Freeing the list frees all memory type data in the list, and so invalidates any outstanding pointers to entries in the list. Do not be out here holding stale pointers, bestie.
struct memory_type entries in the list may be iterated over using memstat_mtl_first() and memstat_mtl_next(), which respectively return the first entry in a list and the next entry in a list. Then thereโs memstat_mtl_find(), which returns a pointer to the first entry matching the passed parameters. Slay.
A series of accessor methods is provided to access fields of the structure, including retrieving statistics and properties, as well as setting caller-owned fields. Direct application access to the data structure fields is not supported. So bestie, use the accessors โ do not rawdog the struct fields. ๐
๐ง Library memory_type Fields ๐ง
Each struct memory_type holds a description of the memory type, including its name and the allocator it is managed by, plus current statistics on use. Some statistics are directly measured, and others are derived from directly measured statistics. Certain high-level statistics are present across all available allocators, like the number of allocation and free operations; other measurements โ such as the quantity of free items in per-CPU caches, or the administrative limit on the number of allocations โ are available only for specific allocators.
๐ Caller memory_type Fields ๐
struct memory_type includes fields that let the application store data, in the form of pointers and 64-bit integers, with memory types. For example, the application author might use one of the caller pointers to reference a more complex data structure tracking long-term behavior of the memory type, or a window system object that is used to render the state of the memory type. General and per-CPU storage is provided with each struct memory_type in the form of an array of pointers and integers. The array entries are accessed via the index argument to the get and set accessor methods. Possible values of index range between 0 and MEMSTAT_MAXCALLER.
Caller-owned fields are initialized to 0 or NULL when a new struct memory_type is allocated and attached to a memory type list; these fields retain their values across queries that update library-owned fields. Iconic behavior, honestly. โจ
๐ Allocator Types ๐
Currently, libmemstat supports two kernel allocators: ALLOCATOR_UMA for uma(9), and ALLOCATOR_MALLOC for malloc(9). These values may be passed to memstat_mtl_find(), and will be returned by memstat_get_allocator(). Two additional constants in the allocator namespace are defined: ALLOCATOR_UNKNOWN, which will only be returned as a result of a library error, and ALLOCATOR_ANY, which can be used to specify that returning types matching any allocator is permittable from memstat_mtl_find(). Iykyk.
๐ฎ Access Method List ๐ฎ
The following accessor methods are defined, of which some will be valid for a given memory type:
memstat_get_name()โ Return a pointer to the name of the memory type. Memory for the name is owned bylibmemstatand will be valid through a call tomemstat_mtl_free(). Note that names will be unique with respect to a single allocator, but the same name might be used by different memory types owned by different memory allocators.memstat_get_allocator()โ Return an integer identifier for the memory allocator that owns the memory type.memstat_get_countlimit()โ If the memory type has an administrative limit on the number of simultaneous allocations, return it.memstat_get_byteslimit()โ If the memory type has an administrative limit on the number of bytes of memory that may be simultaneously allocated for the memory type, return it.memstat_get_sizemask()โ If the memory type supports variable allocation sizes, return a bitmask of sizes allocated for the memory type.memstat_get_size()โ If the memory type supports a fixed allocation size, return that size.memstat_get_rsize()โ If the memory type supports a fixed allocation size, return real size of an allocation. Real size can exceed requested size due to alignment constraints or implicit padding.memstat_get_memalloced()โ Return the total number of bytes allocated for the memory type over its lifetime.memstat_get_memfreed()โ Return the total number of bytes freed for the memory type over its lifetime.memstat_get_numallocs()โ Return the total number of allocations for the memory type over its lifetime.memstat_get_numfrees()โ Return the total number of frees for the memory type over its lifetime.memstat_get_bytes()โ Return the current number of bytes allocated to the memory type.memstat_get_count()โ Return the current number of allocations for the memory type.memstat_get_free()โ If the memory allocator supports a cache, return the number of items in the cache.memstat_get_failures()โ If the memory allocator and type permit allocation failures, return the number of allocation failures measured.memstat_get_caller_pointer()โ Return a caller-owned pointer for the memory type.memstat_set_caller_pointer()โ Set a caller-owned pointer for the memory type.memstat_get_caller_uint64()โ Return a caller-owned integer for the memory type.memstat_set_caller_uint64()โ Set a caller-owned integer for the memory type.memstat_get_zonefree()โ If the memory allocator supports a multi-level allocation structure, return the number of cached items in the zone. These items will be in a fully constructed state available for immediate use.memstat_get_kegfree()โ If the memory allocator supports a multi-level allocation structure, return the number of cached items in the keg. These items may be in a partially constructed state, and may require further processing before they can be made available for use.memstat_get_percpu_memalloced()โ If the memory allocator supports per-CPU statistics, return the number of bytes of memory allocated for the memory type on the CPU over its lifetime.memstat_get_percpu_memfreed()โ If the memory allocator supports per-CPU statistics, return the number of bytes of memory freed from the memory type on the CPU over its lifetime.memstat_get_percpu_numallocs()โ If the memory allocator supports per-CPU statistics, return the number of allocations for the memory type on the CPU over its lifetime.memstat_get_percpu_numfrees()โ If the memory allocator supports per-CPU statistics, return the number of frees for the memory type on the CPU over its lifetime.memstat_get_percpu_sizemask()โ If the memory allocator supports variable size memory allocation and per-CPU statistics, return the size bitmask for the memory type on the CPU.memstat_get_percpu_caller_pointer()โ Return a caller-owned per-CPU pointer for the memory type.memstat_set_percpu_caller_pointer()โ Set a caller-owned per-CPU pointer for the memory type.memstat_get_percpu_caller_uint64()โ Return a caller-owned per-CPU integer for the memory type.memstat_set_percpu_caller_uint64()โ Set a caller-owned per-CPU integer for the memory type.memstat_get_percpu_free()โ If the memory allocator supports a per-CPU cache, return the number of free items in the per-CPU cache of the designated CPU.
๐ RETURN VALUES ๐
So like, libmemstat functions are basically sorted into three whole categories, and honestly? Each one vibes differently:
- functions that return a pointer to an object
- functions that return an integer return value
- functions implementing accessor methods that hand you data out of a
struct memory_type
๐ The pointer-returning girlies
Functions that return a pointer to an object will generally return NULL on failure. But like, one specific case to know: memstat_mtl_alloc() will hand back an error value via errno, and that value will be ENOMEM. Noted. ๐
And then there's memstat_mtl_first(), memstat_mtl_next(), and memstat_mtl_find() โ these return NULL when there's no entry or no match in the list. But bestie, listen: this is NOT a failure mode. It's just... empty. No error value is available for it, because nothing went wrong. It's giving "no thoughts, head empty," not "crisis." ๐ฆ
โจ The integer-returning girlies
Functions returning an integer success value will return 0 on success, or -1 on failure. Simple. Iconic.
If a failure gets returned, you can hit up the list error access method, memstat_mtl_geterror(), to retrieve the error state. And the string representation of that error? That's memstat_strerror()'s whole job. ๐ซถ
Here are all the possible error values, no cap:
MEMSTAT_ERROR_UNDEFINEDโ undefined error. This one happens ifmemstat_mtl_geterror()gets called on a list before any error associated with that list has occurred. Like asking "what's wrong" before anything's wrong. ๐MEMSTAT_ERROR_NOMEMORYโ insufficient memory. Happens if library calls tomalloc(3)fail, or if a system call to retrieve kernel statistics fails withENOMEM. The budget is simply not budgeting. ๐MEMSTAT_ERROR_VERSIONโ returned if the current version of libmemstat is unable to interpret the statistics data returned by the kernel, either due to an explicit version mismatch, or to differences in data structures that cannot be reconciled. It's a compatibility situationship. ๐ญMEMSTAT_ERROR_PERMISSIONโ returned if a statistics source returnserrnovalues ofEACCESorEPERM. You don't have the keys to this club, bestie. ๐MEMSTAT_ERROR_DATAERRORโ returned if libmemstat is unable to interpret statistics data returned by the data source, even though there does not appear to be a version problem. So the data is just... weird. Not deprecated, just unhinged.MEMSTAT_ERROR_KVMโ returned if libmemstat experiences an error while usingkvm(3)interfaces to query statistics data. Hit upkvm_geterr(3)to retrieve the error. ๐ซกMEMSTAT_ERROR_KVM_NOSYMBOLโ returned if libmemstat is unable to read a required symbol from the kernel being operated on. The symbol said "not today." ๐ธMEMSTAT_ERROR_KVM_SHORTREADโ returned if libmemstat attempts to read data from a live memory image or kernel core dump and insufficient data is returned. Short read, shortened patience. โญ
๐ The accessor girlies
And finally: functions that return data from a struct memory_type pointer are not permitted to fail. They just directly return either a statistic or a pointer to a string. No drama, no -1, they simply ate and left no crumbs. ๐
๐ EXAMPLES ๐
Okay so here's the whole plan: create a memory type list, query the uma(9) memory allocator for available statistics, and print out the number of allocations performed by the mbuf zone. Watch and learn, bestie:
struct memory_type_list *mtlp;
struct memory_type *mtp;
uint64_t mbuf_count;
mtlp = memstat_mtl_alloc();
if (mtlp == NULL)
err(-1, "memstat_mtl_alloc");
if (memstat_sysctl_uma(mtlp, 0) < 0)
err(-1, "memstat_sysctl_uma");
mtp = memstat_mtl_find(mtlp, ALLOCATOR_UMA, "mbuf");
if (mtp == NULL)
errx(-1, "memstat_mtl_find: mbuf not found");
mbuf_count = memstat_get_count(mtp);
memstat_mtl_free(mtlp);
printf("mbufs: %llu\n", (unsigned long long)mbuf_count);
Like, the whole arc: allocate, query, find, count, free, print. Character development fr fr. ๐ง
๐ SEE ALSO ๐
๐ HISTORY ๐
The libmemstat library appeared in FreeBSD 6.0. A literal era. โจ
๐ซถ AUTHORS ๐ซถ
The kernel memory allocator changes necessary to support a general purpose monitoring library, along with the library itself, were written by Robert Watson <rwatson@FreeBSD.org>. We stan a legend. ๐
๐ BUGS ๐
Okay so there are some real ones, bestie, don't shoot the messenger:
- There are memory allocators in the kernel โ such as the VM page allocator and the
sf_bufallocator โ which are not currently supported by libmemstat. They're just not invited to the party yet. ๐ - Once a memory type is present on a memory type list, it will not be removed even if the kernel no longer presents information on the type via its monitoring interfaces. So if you need to flush removed memory types, it is necessary to free the entire list and allocate a new one. Like, the whole list. No shortcuts, no individual evictions. Periodt. ๐
Fr fr on that second one: stale entries stick around forever unless you nuke the list and rebuild it. Plan accordingly. ๐ฅ
And that's the whole page, bestie โ this one's from the Debian build, dated February 11, 2014, LIBMEMSTAT(3). Go forth and monitor some memory, you gorgeous thing. ๐ฆ๐โจ
๐ฆ where this page comes from
- freebsd 14.5: base 14.5
- freebsd 15.1: base 15.1
- Copyright (c) 2005 Robert N. M. Watson
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms