master
c 305 lines 10.4 KB
Raw
1 // SPDX-License-Identifier: GPL-3.0-or-later
2
3 #define PULSE_INTERNALS 1
4 #include "pulse-daemon-memory.h"
5 #include "streaming/stream-replication-sender.h"
6
7 #if defined(HAVE_C_MALLOC_INFO) || defined(HAVE_C_MALLINFO2)
8 #include <malloc.h>
9 #endif
10
11 #ifdef HAVE_C_MALLOC_INFO
12 // Helper function to find the last occurrence of a substring in a string
13 static char *find_last(const char *haystack, const char *needle, size_t *found) {
14 *found = 0;
15
16 char *last = NULL;
17 char *current = strstr(haystack, needle);
18 while (current) {
19 (*found)++;
20 last = current;
21 current = strstr(current + 1, needle);
22 }
23 return last;
24 }
25
26 static bool parse_malloc_info(size_t *arenas, size_t *allocated_arena, size_t *unused_fast, size_t *unused_rest, size_t *allocated_mmap) {
27 int found = 0;
28
29 *arenas = 0;
30 *allocated_arena = 0;
31 *unused_fast = 0;
32 *unused_rest = 0;
33 *allocated_mmap = 0;
34
35 // Buffer for malloc_info XML
36 char *buffer = NULL;
37 size_t size = 0;
38 FILE *meminfo = open_memstream(&buffer, &size);
39 if (!meminfo)
40 goto cleanup;
41
42 char *t = malloc(1024);
43
44 // Generate malloc_info XML
45 if(malloc_info(0, meminfo) != 0) {
46 free(t);
47 goto cleanup;
48 }
49
50 fflush(meminfo);
51 fclose(meminfo);
52 meminfo = NULL;
53
54 free(t);
55
56 if(!size || !buffer)
57 goto cleanup;
58
59 // make sure it is terminated
60 buffer[size - 1] = '\0';
61
62 // Find the last </heap>
63 char *last_heap_end = find_last(buffer, "</heap>", arenas);
64 if (!last_heap_end)
65 goto cleanup;
66
67 // Move past the last </heap>
68 char *summary_section = last_heap_end + strlen("</heap>");
69
70 // Parse the summary section using strstr
71 const char *fast_key = "<total type=\"fast\"";
72 const char *rest_key = "<total type=\"rest\"";
73 const char *mmap_key = "<total type=\"mmap\"";
74 const char *system_key = "<system type=\"current\"";
75 char *size_pos;
76
77 char *fast_pos = strstr(summary_section, fast_key);
78 if(!fast_pos || !(size_pos = strstr(fast_pos, "size=\"")))
79 goto cleanup;
80
81 *unused_fast = strtoull(size_pos + 6, NULL, 10);
82 found++;
83
84 char *rest_pos = strstr(summary_section, rest_key);
85 if (!rest_pos || !(size_pos = strstr(rest_pos, "size=\"")))
86 goto cleanup;
87
88 *unused_rest = strtoull(size_pos + 6, NULL, 10);
89 found++;
90
91 char *mmap_pos = strstr(summary_section, mmap_key);
92 if (!mmap_pos || !(size_pos = strstr(mmap_pos, "size=\"")))
93 goto cleanup;
94
95 *allocated_mmap = strtoull(size_pos + 6, NULL, 10);
96 found++;
97
98 char *system_pos = strstr(summary_section, system_key);
99 if (!system_pos || !(size_pos = strstr(system_pos, "size=\"")))
100 goto cleanup;
101
102 *allocated_arena = strtoull(size_pos + 6, NULL, 10);
103 found++;
104
105 cleanup:
106 if(meminfo) fclose(meminfo);
107 if(buffer) free(buffer);
108 return found == 4;
109 }
110 #endif // HAVE_C_MALLOC_INFO
111
112 void pulse_daemon_memory_system_do(bool extended) {
113 if(!extended) return;
114
115 size_t glibc_mmaps = 0;
116 bool have_mallinfo = false; (void)have_mallinfo;
117
118 #ifdef HAVE_C_MALLINFO2
119 struct mallinfo2 mi = mallinfo2();
120 glibc_mmaps = mi.hblks;
121 if(mi.hblkhd || mi.fordblks) {
122 static RRDSET *st_mallinfo = NULL;
123 static RRDDIM *rd_used_mmap = NULL;
124 static RRDDIM *rd_used_arena = NULL;
125 static RRDDIM *rd_unused_fragments = NULL;
126 static RRDDIM *rd_unused_releasable = NULL;
127
128 if (unlikely(!st_mallinfo)) {
129 st_mallinfo = rrdset_create_localhost(
130 "netdata",
131 "glibc_mallinfo2",
132 NULL,
133 "Memory Usage",
134 NULL,
135 "Glibc Mallinfo2 Memory Distribution",
136 "bytes",
137 "netdata",
138 "pulse",
139 130130,
140 localhost->rrd_update_every,
141 RRDSET_TYPE_STACKED);
142
143 rd_unused_releasable = rrddim_add(st_mallinfo, "unused releasable", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
144 rd_unused_fragments = rrddim_add(st_mallinfo, "unused fragments", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
145 rd_used_arena = rrddim_add(st_mallinfo, "used arena", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
146 rd_used_mmap = rrddim_add(st_mallinfo, "used mmap", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
147 }
148
149 // size_t total = mi.uordblks;
150 size_t used_mmap = mi.hblkhd;
151 size_t used_arena = (mi.arena > mi.fordblks) ? mi.arena - mi.fordblks : 0;
152
153 size_t unused_total = mi.fordblks;
154 size_t unused_releasable = mi.keepcost;
155 // size_t unused_fast = mi.fsmblks;
156 size_t unused_fragments = (unused_total > unused_releasable) ? unused_total - unused_releasable : 0;
157
158 rrddim_set_by_pointer(st_mallinfo, rd_unused_releasable, (collected_number)unused_releasable);
159 rrddim_set_by_pointer(st_mallinfo, rd_unused_fragments, (collected_number)unused_fragments);
160 rrddim_set_by_pointer(st_mallinfo, rd_used_arena, (collected_number)used_arena);
161 rrddim_set_by_pointer(st_mallinfo, rd_used_mmap, (collected_number)used_mmap);
162
163 rrdset_done(st_mallinfo);
164 have_mallinfo = true;
165 }
166 #endif // HAVE_C_MALLINFO2
167
168 #ifdef HAVE_C_MALLOC_INFO
169 size_t glibc_arenas, glibc_allocated_arenas, glibc_unused_fast, glibc_unused_rest, glibc_allocated_mmap;
170 if(!have_mallinfo && parse_malloc_info(&glibc_arenas, &glibc_allocated_arenas, &glibc_unused_fast, &glibc_unused_rest, &glibc_allocated_mmap)) {
171 if (glibc_arenas) {
172 static RRDSET *st_arenas = NULL;
173 static RRDDIM *rd_arenas = NULL;
174
175 if (unlikely(!st_arenas)) {
176 st_arenas = rrdset_create_localhost(
177 "netdata",
178 "glibc_arenas",
179 NULL,
180 "Memory Usage",
181 NULL,
182 "Glibc Memory Arenas",
183 "arenas",
184 "netdata",
185 "pulse",
186 130120,
187 localhost->rrd_update_every,
188 RRDSET_TYPE_LINE);
189
190 rd_arenas = rrddim_add(st_arenas, "arenas", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
191 }
192
193 rrddim_set_by_pointer(st_arenas, rd_arenas, (collected_number)glibc_arenas);
194 rrdset_done(st_arenas);
195 }
196
197 if (glibc_allocated_arenas || glibc_allocated_mmap) {
198 static RRDSET *st_malloc = NULL;
199 static RRDDIM *rd_unused_fast = NULL;
200 static RRDDIM *rd_unused_rest = NULL;
201 static RRDDIM *rd_used_arena = NULL;
202 static RRDDIM *rd_used_mmap = NULL;
203
204 if (unlikely(!st_malloc)) {
205 st_malloc = rrdset_create_localhost(
206 "netdata",
207 "glibc_malloc_info",
208 NULL,
209 "Memory Usage",
210 NULL,
211 "Glibc Malloc Info",
212 "bytes",
213 "netdata",
214 "pulse",
215 130121,
216 localhost->rrd_update_every,
217 RRDSET_TYPE_STACKED);
218
219 rd_unused_fast = rrddim_add(st_malloc, "unused fast", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
220 rd_unused_rest = rrddim_add(st_malloc, "unused rest", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
221 rd_used_arena = rrddim_add(st_malloc, "used arena", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
222 rd_used_mmap = rrddim_add(st_malloc, "used mmap", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
223 }
224
225 size_t unused = glibc_unused_fast + glibc_unused_rest;
226 size_t used_arena = (glibc_allocated_arenas > unused) ? glibc_allocated_arenas - unused : 0;
227
228 rrddim_set_by_pointer(st_malloc, rd_unused_fast, (collected_number)glibc_unused_fast);
229 rrddim_set_by_pointer(st_malloc, rd_unused_rest, (collected_number)glibc_unused_rest);
230 rrddim_set_by_pointer(st_malloc, rd_used_arena, (collected_number)used_arena);
231 rrddim_set_by_pointer(st_malloc, rd_used_mmap, (collected_number)glibc_allocated_mmap);
232 rrdset_done(st_malloc);
233 }
234 }
235 #endif
236
237 size_t netdata_mmaps = __atomic_load_n(&nd_mmap_count, __ATOMIC_RELAXED);
238 size_t total_mmaps = netdata_mmaps + glibc_mmaps;
239 {
240 static RRDSET *st_maps = NULL;
241 static RRDDIM *rd_netdata = NULL;
242 static RRDDIM *rd_glibc = NULL;
243
244 if (unlikely(!st_maps)) {
245 st_maps = rrdset_create_localhost(
246 "netdata",
247 "memory_maps",
248 NULL,
249 "Memory Usage",
250 NULL,
251 "Netdata Memory Maps",
252 "maps",
253 "netdata",
254 "pulse",
255 130105,
256 localhost->rrd_update_every,
257 RRDSET_TYPE_LINE);
258
259 rd_netdata = rrddim_add(st_maps, "netdata", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
260 rd_glibc = rrddim_add(st_maps, "glibc", NULL, 1, 1, RRD_ALGORITHM_ABSOLUTE);
261 }
262
263 rrddim_set_by_pointer(st_maps, rd_glibc, (collected_number)glibc_mmaps);
264 rrddim_set_by_pointer(st_maps, rd_netdata, (collected_number)netdata_mmaps);
265
266 rrdset_done(st_maps);
267 }
268
269 static unsigned long long max_map_count = 0;
270 static usec_t last_read_ut = 0;
271 usec_t now_ut = now_monotonic_usec();
272 if(now_ut - last_read_ut >= 60 * USEC_PER_SEC) {
273 // read this file once per minute
274 read_single_number_file("/proc/sys/vm/max_map_count", &max_map_count);
275 last_read_ut = now_ut;
276 }
277
278 if (max_map_count && total_mmaps) {
279 static RRDSET *st_maps_percent = NULL;
280 static RRDDIM *rd_used = NULL;
281
282 if (unlikely(!st_maps_percent)) {
283 st_maps_percent = rrdset_create_localhost(
284 "netdata",
285 "memory_maps_limit",
286 NULL,
287 "Memory Usage",
288 NULL,
289 "Netdata Memory Maps Limit",
290 "%",
291 "netdata",
292 "pulse",
293 130106,
294 localhost->rrd_update_every,
295 RRDSET_TYPE_AREA);
296
297 rd_used = rrddim_add(st_maps_percent, "used", NULL, 1, 1000, RRD_ALGORITHM_ABSOLUTE);
298 }
299
300 double percent = (double)total_mmaps * 100.0 / (double)max_map_count;
301
302 rrddim_set_by_pointer(st_maps_percent, rd_used, (collected_number)round(percent * 1000.0));
303 rrdset_done(st_maps_percent);
304 }
305 }