0
0
Fork 0
mirror of https://github.com/netdata/netdata.git synced 2025-04-09 23:57:55 +00:00

Pulse extended memory statistics, now report glibc allocations ()

* add malloc_info() to netdata

* report number of arenas

* arenas line chart
This commit is contained in:
Costa Tsaousis 2025-01-11 18:23:11 +00:00 committed by GitHub
parent 70454a4454
commit c54c40605a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 187 additions and 0 deletions

View file

@ -484,6 +484,14 @@ int main() {
}
" HAVE_C_MALLOC_TRIM)
check_c_source_compiles("
#include <malloc.h>
int main() {
malloc_info(0, stdout);
return 0;
}
" HAVE_C_MALLOC_INFO)
check_c_source_compiles("
#define _GNU_SOURCE
#include <stdio.h>

View file

@ -98,6 +98,7 @@
#cmakedefine HAVE_C__GENERIC
#cmakedefine HAVE_C_MALLOPT
#cmakedefine HAVE_C_MALLOC_TRIM
#cmakedefine HAVE_C_MALLOC_INFO
#cmakedefine HAVE_SETNS
#cmakedefine HAVE_STRNDUP
#cmakedefine SSL_HAS_PENDING

View file

@ -9,6 +9,116 @@
struct netdata_buffers_statistics netdata_buffers_statistics = { 0 };
#ifdef HAVE_C_MALLOC_INFO
#include <malloc.h>
// 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_memory, size_t *used_fast, size_t *used_rest, size_t *used_mmap, size_t *unused_memory) {
int found = 0;
*arenas = 0;
*allocated_memory = 0;
*used_fast = 0;
*used_rest = 0;
*used_mmap = 0;
*unused_memory = 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;
// Extract Used Fast
char *fast_pos = strstr(summary_section, fast_key);
if(!fast_pos || !(size_pos = strstr(fast_pos, "size=\"")))
goto cleanup;
*used_fast = strtoull(size_pos + 6, NULL, 10);
found++;
// Extract Used Rest
char *rest_pos = strstr(summary_section, rest_key);
if (!rest_pos || !(size_pos = strstr(rest_pos, "size=\"")))
goto cleanup;
*used_rest = strtoull(size_pos + 6, NULL, 10);
found++;
// Extract Used Mmap
char *mmap_pos = strstr(summary_section, mmap_key);
if (!mmap_pos || !(size_pos = strstr(mmap_pos, "size=\"")))
goto cleanup;
*used_mmap = strtoull(size_pos + 6, NULL, 10);
found++;
// Extract Allocated Memory
char *system_pos = strstr(summary_section, system_key);
if (!system_pos || !(size_pos = strstr(system_pos, "size=\"")))
goto cleanup;
*allocated_memory = strtoull(size_pos + 6, NULL, 10);
found++;
// Calculate Unused Memory
*unused_memory = *allocated_memory > (*used_fast + *used_rest + *used_mmap) ?
*allocated_memory - (*used_fast + *used_rest + *used_mmap) : 0;
cleanup:
if(meminfo) fclose(meminfo);
if(buffer) free(buffer);
return found == 4;
}
#endif // HAVE_C_MALLOC_INFO
void pulse_daemon_memory_do(bool extended) {
{
static RRDSET *st_memory = NULL;
@ -285,4 +395,72 @@ void pulse_daemon_memory_do(bool extended) {
// ----------------------------------------------------------------------------------------------------------------
#ifdef HAVE_C_MALLOC_INFO
size_t glibc_arenas, glibc_allocated_memory, glibc_used_fast, glibc_used_rest, glibc_used_mmap, glibc_unused_memory;
if(parse_malloc_info(&glibc_arenas, &glibc_allocated_memory, &glibc_used_fast, &glibc_used_rest, &glibc_used_mmap, &glibc_unused_memory)) {
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",
"bytes",
"netdata",
"pulse",
130104,
localhost->rrd_update_every,
RRDSET_TYPE_LINE);
rd_arenas = rrddim_add(st_arenas, "arenas", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
}
// the sum of all these needs to be above at the total buffers calculation
rrddim_set_by_pointer(st_arenas, rd_arenas, (collected_number)glibc_arenas);
rrdset_done(st_arenas);
}
if (glibc_allocated_memory > 0 && glibc_used_fast + glibc_used_rest + glibc_used_mmap + glibc_unused_memory > 0) {
static RRDSET *st_malloc = NULL;
static RRDDIM *rd_unused = NULL;
static RRDDIM *rd_fast = NULL;
static RRDDIM *rd_rest = NULL;
static RRDDIM *rd_mmap = NULL;
if (unlikely(!st_malloc)) {
st_malloc = rrdset_create_localhost(
"netdata",
"glibc_memory",
NULL,
"Memory Usage",
NULL,
"Glibc Memory Usage",
"bytes",
"netdata",
"pulse",
130105,
localhost->rrd_update_every,
RRDSET_TYPE_STACKED);
rd_unused = rrddim_add(st_malloc, "unused", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
rd_fast = rrddim_add(st_malloc, "fast", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
rd_rest = rrddim_add(st_malloc, "rest", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
rd_mmap = rrddim_add(st_malloc, "mmap", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
}
// the sum of all these needs to be above at the total buffers calculation
rrddim_set_by_pointer(st_malloc, rd_unused, (collected_number)glibc_unused_memory);
rrddim_set_by_pointer(st_malloc, rd_fast, (collected_number)glibc_used_fast);
rrddim_set_by_pointer(st_malloc, rd_rest, (collected_number)glibc_used_rest);
rrddim_set_by_pointer(st_malloc, rd_mmap, (collected_number)glibc_used_mmap);
rrdset_done(st_malloc);
}
}
#endif
}