forked from netdata/netdata
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
added mmap count charts (netdata#19384)
* added mmap count charts * buffers should not be extended * read max_map_count once per minute
- Loading branch information
Showing
5 changed files
with
311 additions
and
234 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,300 @@ | ||
// SPDX-License-Identifier: GPL-3.0-or-later | ||
|
||
#define PULSE_INTERNALS 1 | ||
#include "pulse-daemon-memory.h" | ||
#include "streaming/stream-replication-sender.h" | ||
|
||
#if defined(HAVE_C_MALLOC_INFO) || defined(HAVE_C_MALLINFO2) | ||
#include <malloc.h> | ||
#endif | ||
|
||
#ifdef HAVE_C_MALLOC_INFO | ||
// Helper function to find the last occurrence of a substring in a string | ||
static char *find_last(const char *haystack, const char *needle, size_t *found) { | ||
*found = 0; | ||
|
||
char *last = NULL; | ||
char *current = strstr(haystack, needle); | ||
while (current) { | ||
(*found)++; | ||
last = current; | ||
current = strstr(current + 1, needle); | ||
} | ||
return last; | ||
} | ||
|
||
static bool parse_malloc_info(size_t *arenas, size_t *allocated_arena, size_t *unused_fast, size_t *unused_rest, size_t *allocated_mmap) { | ||
int found = 0; | ||
|
||
*arenas = 0; | ||
*allocated_arena = 0; | ||
*unused_fast = 0; | ||
*unused_rest = 0; | ||
*allocated_mmap = 0; | ||
|
||
// Buffer for malloc_info XML | ||
char *buffer = NULL; | ||
size_t size = 0; | ||
FILE *meminfo = open_memstream(&buffer, &size); | ||
if (!meminfo) | ||
goto cleanup; | ||
|
||
char *t = malloc(1024); | ||
|
||
// Generate malloc_info XML | ||
if(malloc_info(0, meminfo) != 0) | ||
goto cleanup; | ||
|
||
fflush(meminfo); | ||
fclose(meminfo); | ||
meminfo = NULL; | ||
|
||
free(t); | ||
|
||
if(!size || !buffer) | ||
goto cleanup; | ||
|
||
// make sure it is terminated | ||
buffer[size - 1] = '\0'; | ||
|
||
// Find the last </heap> | ||
char *last_heap_end = find_last(buffer, "</heap>", arenas); | ||
if (!last_heap_end) | ||
goto cleanup; | ||
|
||
// Move past the last </heap> | ||
char *summary_section = last_heap_end + strlen("</heap>"); | ||
|
||
// Parse the summary section using strstr | ||
const char *fast_key = "<total type=\"fast\""; | ||
const char *rest_key = "<total type=\"rest\""; | ||
const char *mmap_key = "<total type=\"mmap\""; | ||
const char *system_key = "<system type=\"current\""; | ||
char *size_pos; | ||
|
||
char *fast_pos = strstr(summary_section, fast_key); | ||
if(!fast_pos || !(size_pos = strstr(fast_pos, "size=\""))) | ||
goto cleanup; | ||
|
||
*unused_fast = strtoull(size_pos + 6, NULL, 10); | ||
found++; | ||
|
||
char *rest_pos = strstr(summary_section, rest_key); | ||
if (!rest_pos || !(size_pos = strstr(rest_pos, "size=\""))) | ||
goto cleanup; | ||
|
||
*unused_rest = strtoull(size_pos + 6, NULL, 10); | ||
found++; | ||
|
||
char *mmap_pos = strstr(summary_section, mmap_key); | ||
if (!mmap_pos || !(size_pos = strstr(mmap_pos, "size=\""))) | ||
goto cleanup; | ||
|
||
*allocated_mmap = strtoull(size_pos + 6, NULL, 10); | ||
found++; | ||
|
||
char *system_pos = strstr(summary_section, system_key); | ||
if (!system_pos || !(size_pos = strstr(system_pos, "size=\""))) | ||
goto cleanup; | ||
|
||
*allocated_arena = strtoull(size_pos + 6, NULL, 10); | ||
found++; | ||
|
||
cleanup: | ||
if(meminfo) fclose(meminfo); | ||
if(buffer) free(buffer); | ||
return found == 4; | ||
} | ||
#endif // HAVE_C_MALLOC_INFO | ||
|
||
void pulse_daemon_memory_system_do(bool extended) { | ||
|
||
#ifdef HAVE_C_MALLOC_INFO | ||
size_t glibc_arenas, glibc_allocated_arenas, glibc_unused_fast, glibc_unused_rest, glibc_allocated_mmap; | ||
if(extended && parse_malloc_info(&glibc_arenas, &glibc_allocated_arenas, &glibc_unused_fast, &glibc_unused_rest, &glibc_allocated_mmap)) { | ||
if (glibc_arenas) { | ||
static RRDSET *st_arenas = NULL; | ||
static RRDDIM *rd_arenas = NULL; | ||
|
||
if (unlikely(!st_arenas)) { | ||
st_arenas = rrdset_create_localhost( | ||
"netdata", | ||
"glibc_arenas", | ||
NULL, | ||
"Memory Usage", | ||
NULL, | ||
"Glibc Memory Arenas", | ||
"arenas", | ||
"netdata", | ||
"pulse", | ||
130120, | ||
localhost->rrd_update_every, | ||
RRDSET_TYPE_LINE); | ||
|
||
rd_arenas = rrddim_add(st_arenas, "arenas", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); | ||
} | ||
|
||
rrddim_set_by_pointer(st_arenas, rd_arenas, (collected_number)glibc_arenas); | ||
rrdset_done(st_arenas); | ||
} | ||
|
||
if (glibc_allocated_arenas || glibc_allocated_mmap) { | ||
static RRDSET *st_malloc = NULL; | ||
static RRDDIM *rd_unused_fast = NULL; | ||
static RRDDIM *rd_unused_rest = NULL; | ||
static RRDDIM *rd_used_arena = NULL; | ||
static RRDDIM *rd_used_mmap = NULL; | ||
|
||
if (unlikely(!st_malloc)) { | ||
st_malloc = rrdset_create_localhost( | ||
"netdata", | ||
"glibc_malloc_info", | ||
NULL, | ||
"Memory Usage", | ||
NULL, | ||
"Glibc Malloc Info", | ||
"bytes", | ||
"netdata", | ||
"pulse", | ||
130121, | ||
localhost->rrd_update_every, | ||
RRDSET_TYPE_STACKED); | ||
|
||
rd_unused_fast = rrddim_add(st_malloc, "unused fast", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); | ||
rd_unused_rest = rrddim_add(st_malloc, "unused rest", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); | ||
rd_used_arena = rrddim_add(st_malloc, "used arena", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); | ||
rd_used_mmap = rrddim_add(st_malloc, "used mmap", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); | ||
} | ||
|
||
size_t unused = glibc_unused_fast + glibc_unused_rest; | ||
size_t used_arena = (glibc_allocated_arenas > unused) ? glibc_allocated_arenas - unused : 0; | ||
|
||
rrddim_set_by_pointer(st_malloc, rd_unused_fast, (collected_number)glibc_unused_fast); | ||
rrddim_set_by_pointer(st_malloc, rd_unused_rest, (collected_number)glibc_unused_rest); | ||
rrddim_set_by_pointer(st_malloc, rd_used_arena, (collected_number)used_arena); | ||
rrddim_set_by_pointer(st_malloc, rd_used_mmap, (collected_number)glibc_allocated_mmap); | ||
rrdset_done(st_malloc); | ||
} | ||
} | ||
#endif | ||
|
||
size_t glibc_mmaps = 0; | ||
|
||
#ifdef HAVE_C_MALLINFO2 | ||
struct mallinfo2 mi = mallinfo2(); | ||
glibc_mmaps = mi.hblks; | ||
if(extended && (mi.hblkhd || mi.fordblks)) { | ||
static RRDSET *st_mallinfo = NULL; | ||
static RRDDIM *rd_used_mmap = NULL; | ||
static RRDDIM *rd_used_arena = NULL; | ||
static RRDDIM *rd_unused_fragments = NULL; | ||
static RRDDIM *rd_unused_releasable = NULL; | ||
|
||
if (unlikely(!st_mallinfo)) { | ||
st_mallinfo = rrdset_create_localhost( | ||
"netdata", | ||
"glibc_mallinfo2", | ||
NULL, | ||
"Memory Usage", | ||
NULL, | ||
"Glibc Mallinfo2 Memory Distribution", | ||
"bytes", | ||
"netdata", | ||
"pulse", | ||
130130, | ||
localhost->rrd_update_every, | ||
RRDSET_TYPE_STACKED); | ||
|
||
rd_unused_releasable = rrddim_add(st_mallinfo, "unused releasable", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); | ||
rd_unused_fragments = rrddim_add(st_mallinfo, "unused fragments", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); | ||
rd_used_arena = rrddim_add(st_mallinfo, "used arena", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); | ||
rd_used_mmap = rrddim_add(st_mallinfo, "used mmap", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); | ||
} | ||
|
||
// size_t total = mi.uordblks; | ||
size_t used_mmap = mi.hblkhd; | ||
size_t used_arena = (mi.arena > mi.fordblks) ? mi.arena - mi.fordblks : 0; | ||
|
||
size_t unused_total = mi.fordblks; | ||
size_t unused_releasable = mi.keepcost; | ||
// size_t unused_fast = mi.fsmblks; | ||
size_t unused_fragments = (unused_total > unused_releasable) ? unused_total - unused_releasable : 0; | ||
|
||
rrddim_set_by_pointer(st_mallinfo, rd_unused_releasable, (collected_number)unused_releasable); | ||
rrddim_set_by_pointer(st_mallinfo, rd_unused_fragments, (collected_number)unused_fragments); | ||
rrddim_set_by_pointer(st_mallinfo, rd_used_arena, (collected_number)used_arena); | ||
rrddim_set_by_pointer(st_mallinfo, rd_used_mmap, (collected_number)used_mmap); | ||
|
||
rrdset_done(st_mallinfo); | ||
} | ||
#endif // HAVE_C_MALLINFO2 | ||
|
||
size_t netdata_mmaps = __atomic_load_n(&netdata_mmap_count, __ATOMIC_RELAXED); | ||
size_t total_mmaps = netdata_mmaps + glibc_mmaps; | ||
{ | ||
static RRDSET *st_maps = NULL; | ||
static RRDDIM *rd_netdata = NULL; | ||
static RRDDIM *rd_glibc = NULL; | ||
|
||
if (unlikely(!st_maps)) { | ||
st_maps = rrdset_create_localhost( | ||
"netdata", | ||
"memory_maps", | ||
NULL, | ||
"Memory Usage", | ||
NULL, | ||
"Netdata Memory Maps", | ||
"maps", | ||
"netdata", | ||
"pulse", | ||
130105, | ||
localhost->rrd_update_every, | ||
RRDSET_TYPE_LINE); | ||
|
||
rd_netdata = rrddim_add(st_maps, "netdata", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); | ||
rd_glibc = rrddim_add(st_maps, "glibc", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE); | ||
} | ||
|
||
rrddim_set_by_pointer(st_maps, rd_glibc, (collected_number)netdata_mmaps); | ||
rrddim_set_by_pointer(st_maps, rd_netdata, (collected_number)glibc_mmaps); | ||
|
||
rrdset_done(st_maps); | ||
} | ||
|
||
static unsigned long long max_map_count = 0; | ||
static usec_t last_read_ut = 0; | ||
usec_t now_ut = now_monotonic_usec(); | ||
if(now_ut - last_read_ut >= 60 * USEC_PER_SEC) { | ||
// read this file once per minute | ||
read_single_number_file("/proc/sys/vm/max_map_count", &max_map_count); | ||
last_read_ut = now_ut; | ||
} | ||
|
||
if (max_map_count && total_mmaps) { | ||
static RRDSET *st_maps_percent = NULL; | ||
static RRDDIM *rd_used = NULL; | ||
|
||
if (unlikely(!st_maps_percent)) { | ||
st_maps_percent = rrdset_create_localhost( | ||
"netdata", | ||
"memory_maps_limit", | ||
NULL, | ||
"Memory Usage", | ||
NULL, | ||
"Netdata Memory Maps Limit", | ||
"%", | ||
"netdata", | ||
"pulse", | ||
130106, | ||
localhost->rrd_update_every, | ||
RRDSET_TYPE_AREA); | ||
|
||
rd_used = rrddim_add(st_maps_percent, "used", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE); | ||
} | ||
|
||
double percent = (double)total_mmaps * 100.0 / (double)max_map_count; | ||
|
||
rrddim_set_by_pointer(st_maps_percent, rd_used, (collected_number)round(percent * 1000.0)); | ||
rrdset_done(st_maps_percent); | ||
} | ||
} |
Oops, something went wrong.