1 #define JEMALLOC_STATS_C_
2 #include "jemalloc/internal/jemalloc_preamble.h"
3 #include "jemalloc/internal/jemalloc_internal_includes.h"
4
5 #include "jemalloc/internal/assert.h"
6 #include "jemalloc/internal/ctl.h"
7 #include "jemalloc/internal/emitter.h"
8 #include "jemalloc/internal/mutex.h"
9 #include "jemalloc/internal/mutex_prof.h"
10
11 const char *global_mutex_names[mutex_prof_num_global_mutexes] = {
12 #define OP(mtx) #mtx,
13 MUTEX_PROF_GLOBAL_MUTEXES
14 #undef OP
15 };
16
17 const char *arena_mutex_names[mutex_prof_num_arena_mutexes] = {
18 #define OP(mtx) #mtx,
19 MUTEX_PROF_ARENA_MUTEXES
20 #undef OP
21 };
22
23 #define CTL_GET(n, v, t) do { \
24 size_t sz = sizeof(t); \
25 xmallctl(n, (void *)v, &sz, NULL, 0); \
26 } while (0)
27
28 #define CTL_M2_GET(n, i, v, t) do { \
29 size_t mib[CTL_MAX_DEPTH]; \
30 size_t miblen = sizeof(mib) / sizeof(size_t); \
31 size_t sz = sizeof(t); \
32 xmallctlnametomib(n, mib, &miblen); \
33 mib[2] = (i); \
34 xmallctlbymib(mib, miblen, (void *)v, &sz, NULL, 0); \
35 } while (0)
36
37 #define CTL_M2_M4_GET(n, i, j, v, t) do { \
38 size_t mib[CTL_MAX_DEPTH]; \
39 size_t miblen = sizeof(mib) / sizeof(size_t); \
40 size_t sz = sizeof(t); \
41 xmallctlnametomib(n, mib, &miblen); \
42 mib[2] = (i); \
43 mib[4] = (j); \
44 xmallctlbymib(mib, miblen, (void *)v, &sz, NULL, 0); \
45 } while (0)
46
47 /******************************************************************************/
48 /* Data. */
49
50 bool opt_stats_print = false;
51 char opt_stats_print_opts[stats_print_tot_num_options+1] = "";
52
53 /******************************************************************************/
54
55 /* Calculate x.yyy and output a string (takes a fixed sized char array). */
56 static bool
get_rate_str(uint64_t dividend,uint64_t divisor,char str[6])57 get_rate_str(uint64_t dividend, uint64_t divisor, char str[6]) {
58 if (divisor == 0 || dividend > divisor) {
59 /* The rate is not supposed to be greater than 1. */
60 return true;
61 }
62 if (dividend > 0) {
63 assert(UINT64_MAX / dividend >= 1000);
64 }
65
66 unsigned n = (unsigned)((dividend * 1000) / divisor);
67 if (n < 10) {
68 malloc_snprintf(str, 6, "0.00%u", n);
69 } else if (n < 100) {
70 malloc_snprintf(str, 6, "0.0%u", n);
71 } else if (n < 1000) {
72 malloc_snprintf(str, 6, "0.%u", n);
73 } else {
74 malloc_snprintf(str, 6, "1");
75 }
76
77 return false;
78 }
79
80 #define MUTEX_CTL_STR_MAX_LENGTH 128
81 static void
gen_mutex_ctl_str(char * str,size_t buf_len,const char * prefix,const char * mutex,const char * counter)82 gen_mutex_ctl_str(char *str, size_t buf_len, const char *prefix,
83 const char *mutex, const char *counter) {
84 malloc_snprintf(str, buf_len, "stats.%s.%s.%s", prefix, mutex, counter);
85 }
86
87 static void
mutex_stats_init_cols(emitter_row_t * row,const char * table_name,emitter_col_t * name,emitter_col_t col_uint64_t[mutex_prof_num_uint64_t_counters],emitter_col_t col_uint32_t[mutex_prof_num_uint32_t_counters])88 mutex_stats_init_cols(emitter_row_t *row, const char *table_name,
89 emitter_col_t *name,
90 emitter_col_t col_uint64_t[mutex_prof_num_uint64_t_counters],
91 emitter_col_t col_uint32_t[mutex_prof_num_uint32_t_counters]) {
92 mutex_prof_uint64_t_counter_ind_t k_uint64_t = 0;
93 mutex_prof_uint32_t_counter_ind_t k_uint32_t = 0;
94
95 emitter_col_t *col;
96
97 if (name != NULL) {
98 emitter_col_init(name, row);
99 name->justify = emitter_justify_left;
100 name->width = 21;
101 name->type = emitter_type_title;
102 name->str_val = table_name;
103 }
104
105 #define WIDTH_uint32_t 12
106 #define WIDTH_uint64_t 16
107 #define OP(counter, counter_type, human) \
108 col = &col_##counter_type[k_##counter_type]; \
109 ++k_##counter_type; \
110 emitter_col_init(col, row); \
111 col->justify = emitter_justify_right; \
112 col->width = WIDTH_##counter_type; \
113 col->type = emitter_type_title; \
114 col->str_val = human;
115 MUTEX_PROF_COUNTERS
116 #undef OP
117 #undef WIDTH_uint32_t
118 #undef WIDTH_uint64_t
119 }
120
121 static void
mutex_stats_read_global(const char * name,emitter_col_t * col_name,emitter_col_t col_uint64_t[mutex_prof_num_uint64_t_counters],emitter_col_t col_uint32_t[mutex_prof_num_uint32_t_counters])122 mutex_stats_read_global(const char *name, emitter_col_t *col_name,
123 emitter_col_t col_uint64_t[mutex_prof_num_uint64_t_counters],
124 emitter_col_t col_uint32_t[mutex_prof_num_uint32_t_counters]) {
125 char cmd[MUTEX_CTL_STR_MAX_LENGTH];
126
127 col_name->str_val = name;
128
129 emitter_col_t *dst;
130 #define EMITTER_TYPE_uint32_t emitter_type_uint32
131 #define EMITTER_TYPE_uint64_t emitter_type_uint64
132 #define OP(counter, counter_type, human) \
133 dst = &col_##counter_type[mutex_counter_##counter]; \
134 dst->type = EMITTER_TYPE_##counter_type; \
135 gen_mutex_ctl_str(cmd, MUTEX_CTL_STR_MAX_LENGTH, \
136 "mutexes", name, #counter); \
137 CTL_GET(cmd, (counter_type *)&dst->bool_val, counter_type);
138 MUTEX_PROF_COUNTERS
139 #undef OP
140 #undef EMITTER_TYPE_uint32_t
141 #undef EMITTER_TYPE_uint64_t
142 }
143
144 static void
mutex_stats_read_arena(unsigned arena_ind,mutex_prof_arena_ind_t mutex_ind,const char * name,emitter_col_t * col_name,emitter_col_t col_uint64_t[mutex_prof_num_uint64_t_counters],emitter_col_t col_uint32_t[mutex_prof_num_uint32_t_counters])145 mutex_stats_read_arena(unsigned arena_ind, mutex_prof_arena_ind_t mutex_ind,
146 const char *name, emitter_col_t *col_name,
147 emitter_col_t col_uint64_t[mutex_prof_num_uint64_t_counters],
148 emitter_col_t col_uint32_t[mutex_prof_num_uint32_t_counters]) {
149 char cmd[MUTEX_CTL_STR_MAX_LENGTH];
150
151 col_name->str_val = name;
152
153 emitter_col_t *dst;
154 #define EMITTER_TYPE_uint32_t emitter_type_uint32
155 #define EMITTER_TYPE_uint64_t emitter_type_uint64
156 #define OP(counter, counter_type, human) \
157 dst = &col_##counter_type[mutex_counter_##counter]; \
158 dst->type = EMITTER_TYPE_##counter_type; \
159 gen_mutex_ctl_str(cmd, MUTEX_CTL_STR_MAX_LENGTH, \
160 "arenas.0.mutexes", arena_mutex_names[mutex_ind], #counter);\
161 CTL_M2_GET(cmd, arena_ind, \
162 (counter_type *)&dst->bool_val, counter_type);
163 MUTEX_PROF_COUNTERS
164 #undef OP
165 #undef EMITTER_TYPE_uint32_t
166 #undef EMITTER_TYPE_uint64_t
167 }
168
169 static void
mutex_stats_read_arena_bin(unsigned arena_ind,unsigned bin_ind,emitter_col_t col_uint64_t[mutex_prof_num_uint64_t_counters],emitter_col_t col_uint32_t[mutex_prof_num_uint32_t_counters])170 mutex_stats_read_arena_bin(unsigned arena_ind, unsigned bin_ind,
171 emitter_col_t col_uint64_t[mutex_prof_num_uint64_t_counters],
172 emitter_col_t col_uint32_t[mutex_prof_num_uint32_t_counters]) {
173 char cmd[MUTEX_CTL_STR_MAX_LENGTH];
174 emitter_col_t *dst;
175
176 #define EMITTER_TYPE_uint32_t emitter_type_uint32
177 #define EMITTER_TYPE_uint64_t emitter_type_uint64
178 #define OP(counter, counter_type, human) \
179 dst = &col_##counter_type[mutex_counter_##counter]; \
180 dst->type = EMITTER_TYPE_##counter_type; \
181 gen_mutex_ctl_str(cmd, MUTEX_CTL_STR_MAX_LENGTH, \
182 "arenas.0.bins.0","mutex", #counter); \
183 CTL_M2_M4_GET(cmd, arena_ind, bin_ind, \
184 (counter_type *)&dst->bool_val, counter_type);
185 MUTEX_PROF_COUNTERS
186 #undef OP
187 #undef EMITTER_TYPE_uint32_t
188 #undef EMITTER_TYPE_uint64_t
189 }
190
191 /* "row" can be NULL to avoid emitting in table mode. */
192 static void
mutex_stats_emit(emitter_t * emitter,emitter_row_t * row,emitter_col_t col_uint64_t[mutex_prof_num_uint64_t_counters],emitter_col_t col_uint32_t[mutex_prof_num_uint32_t_counters])193 mutex_stats_emit(emitter_t *emitter, emitter_row_t *row,
194 emitter_col_t col_uint64_t[mutex_prof_num_uint64_t_counters],
195 emitter_col_t col_uint32_t[mutex_prof_num_uint32_t_counters]) {
196 if (row != NULL) {
197 emitter_table_row(emitter, row);
198 }
199
200 mutex_prof_uint64_t_counter_ind_t k_uint64_t = 0;
201 mutex_prof_uint32_t_counter_ind_t k_uint32_t = 0;
202
203 emitter_col_t *col;
204
205 #define EMITTER_TYPE_uint32_t emitter_type_uint32
206 #define EMITTER_TYPE_uint64_t emitter_type_uint64
207 #define OP(counter, type, human) \
208 col = &col_##type[k_##type]; \
209 ++k_##type; \
210 emitter_json_kv(emitter, #counter, EMITTER_TYPE_##type, \
211 (const void *)&col->bool_val);
212 MUTEX_PROF_COUNTERS;
213 #undef OP
214 #undef EMITTER_TYPE_uint32_t
215 #undef EMITTER_TYPE_uint64_t
216 }
217
218 static void
stats_arena_bins_print(emitter_t * emitter,bool mutex,unsigned i)219 stats_arena_bins_print(emitter_t *emitter, bool mutex, unsigned i) {
220 size_t page;
221 bool in_gap, in_gap_prev;
222 unsigned nbins, j;
223
224 CTL_GET("arenas.page", &page, size_t);
225
226 CTL_GET("arenas.nbins", &nbins, unsigned);
227
228 emitter_row_t header_row;
229 emitter_row_init(&header_row);
230
231 emitter_row_t row;
232 emitter_row_init(&row);
233 #define COL(name, left_or_right, col_width, etype) \
234 emitter_col_t col_##name; \
235 emitter_col_init(&col_##name, &row); \
236 col_##name.justify = emitter_justify_##left_or_right; \
237 col_##name.width = col_width; \
238 col_##name.type = emitter_type_##etype; \
239 emitter_col_t header_col_##name; \
240 emitter_col_init(&header_col_##name, &header_row); \
241 header_col_##name.justify = emitter_justify_##left_or_right; \
242 header_col_##name.width = col_width; \
243 header_col_##name.type = emitter_type_title; \
244 header_col_##name.str_val = #name;
245
246 COL(size, right, 20, size)
247 COL(ind, right, 4, unsigned)
248 COL(allocated, right, 13, uint64)
249 COL(nmalloc, right, 13, uint64)
250 COL(ndalloc, right, 13, uint64)
251 COL(nrequests, right, 13, uint64)
252 COL(curregs, right, 13, size)
253 COL(curslabs, right, 13, size)
254 COL(regs, right, 5, unsigned)
255 COL(pgs, right, 4, size)
256 /* To buffer a right- and left-justified column. */
257 COL(justify_spacer, right, 1, title)
258 COL(util, right, 6, title)
259 COL(nfills, right, 13, uint64)
260 COL(nflushes, right, 13, uint64)
261 COL(nslabs, right, 13, uint64)
262 COL(nreslabs, right, 13, uint64)
263 #undef COL
264
265 /* Don't want to actually print the name. */
266 header_col_justify_spacer.str_val = " ";
267 col_justify_spacer.str_val = " ";
268
269
270 emitter_col_t col_mutex64[mutex_prof_num_uint64_t_counters];
271 emitter_col_t col_mutex32[mutex_prof_num_uint32_t_counters];
272
273 emitter_col_t header_mutex64[mutex_prof_num_uint64_t_counters];
274 emitter_col_t header_mutex32[mutex_prof_num_uint32_t_counters];
275
276 if (mutex) {
277 mutex_stats_init_cols(&row, NULL, NULL, col_mutex64,
278 col_mutex32);
279 mutex_stats_init_cols(&header_row, NULL, NULL, header_mutex64,
280 header_mutex32);
281 }
282
283 /*
284 * We print a "bins:" header as part of the table row; we need to adjust
285 * the header size column to compensate.
286 */
287 header_col_size.width -=5;
288 emitter_table_printf(emitter, "bins:");
289 emitter_table_row(emitter, &header_row);
290 emitter_json_arr_begin(emitter, "bins");
291
292 for (j = 0, in_gap = false; j < nbins; j++) {
293 uint64_t nslabs;
294 size_t reg_size, slab_size, curregs;
295 size_t curslabs;
296 uint32_t nregs;
297 uint64_t nmalloc, ndalloc, nrequests, nfills, nflushes;
298 uint64_t nreslabs;
299
300 CTL_M2_M4_GET("stats.arenas.0.bins.0.nslabs", i, j, &nslabs,
301 uint64_t);
302 in_gap_prev = in_gap;
303 in_gap = (nslabs == 0);
304
305 if (in_gap_prev && !in_gap) {
306 emitter_table_printf(emitter,
307 " ---\n");
308 }
309
310 CTL_M2_GET("arenas.bin.0.size", j, ®_size, size_t);
311 CTL_M2_GET("arenas.bin.0.nregs", j, &nregs, uint32_t);
312 CTL_M2_GET("arenas.bin.0.slab_size", j, &slab_size, size_t);
313
314 CTL_M2_M4_GET("stats.arenas.0.bins.0.nmalloc", i, j, &nmalloc,
315 uint64_t);
316 CTL_M2_M4_GET("stats.arenas.0.bins.0.ndalloc", i, j, &ndalloc,
317 uint64_t);
318 CTL_M2_M4_GET("stats.arenas.0.bins.0.curregs", i, j, &curregs,
319 size_t);
320 CTL_M2_M4_GET("stats.arenas.0.bins.0.nrequests", i, j,
321 &nrequests, uint64_t);
322 CTL_M2_M4_GET("stats.arenas.0.bins.0.nfills", i, j, &nfills,
323 uint64_t);
324 CTL_M2_M4_GET("stats.arenas.0.bins.0.nflushes", i, j, &nflushes,
325 uint64_t);
326 CTL_M2_M4_GET("stats.arenas.0.bins.0.nreslabs", i, j, &nreslabs,
327 uint64_t);
328 CTL_M2_M4_GET("stats.arenas.0.bins.0.curslabs", i, j, &curslabs,
329 size_t);
330
331 if (mutex) {
332 mutex_stats_read_arena_bin(i, j, col_mutex64,
333 col_mutex32);
334 }
335
336 emitter_json_arr_obj_begin(emitter);
337 emitter_json_kv(emitter, "nmalloc", emitter_type_uint64,
338 &nmalloc);
339 emitter_json_kv(emitter, "ndalloc", emitter_type_uint64,
340 &ndalloc);
341 emitter_json_kv(emitter, "curregs", emitter_type_size,
342 &curregs);
343 emitter_json_kv(emitter, "nrequests", emitter_type_uint64,
344 &nrequests);
345 emitter_json_kv(emitter, "nfills", emitter_type_uint64,
346 &nfills);
347 emitter_json_kv(emitter, "nflushes", emitter_type_uint64,
348 &nflushes);
349 emitter_json_kv(emitter, "nreslabs", emitter_type_uint64,
350 &nreslabs);
351 emitter_json_kv(emitter, "curslabs", emitter_type_size,
352 &curslabs);
353 if (mutex) {
354 emitter_json_dict_begin(emitter, "mutex");
355 mutex_stats_emit(emitter, NULL, col_mutex64,
356 col_mutex32);
357 emitter_json_dict_end(emitter);
358 }
359 emitter_json_arr_obj_end(emitter);
360
361 size_t availregs = nregs * curslabs;
362 char util[6];
363 if (get_rate_str((uint64_t)curregs, (uint64_t)availregs, util))
364 {
365 if (availregs == 0) {
366 malloc_snprintf(util, sizeof(util), "1");
367 } else if (curregs > availregs) {
368 /*
369 * Race detected: the counters were read in
370 * separate mallctl calls and concurrent
371 * operations happened in between. In this case
372 * no meaningful utilization can be computed.
373 */
374 malloc_snprintf(util, sizeof(util), " race");
375 } else {
376 not_reached();
377 }
378 }
379
380 col_size.size_val = reg_size;
381 col_ind.unsigned_val = j;
382 col_allocated.size_val = curregs * reg_size;
383 col_nmalloc.uint64_val = nmalloc;
384 col_ndalloc.uint64_val = ndalloc;
385 col_nrequests.uint64_val = nrequests;
386 col_curregs.size_val = curregs;
387 col_curslabs.size_val = curslabs;
388 col_regs.unsigned_val = nregs;
389 col_pgs.size_val = slab_size / page;
390 col_util.str_val = util;
391 col_nfills.uint64_val = nfills;
392 col_nflushes.uint64_val = nflushes;
393 col_nslabs.uint64_val = nslabs;
394 col_nreslabs.uint64_val = nreslabs;
395
396 /*
397 * Note that mutex columns were initialized above, if mutex ==
398 * true.
399 */
400
401 emitter_table_row(emitter, &row);
402 }
403 emitter_json_arr_end(emitter); /* Close "bins". */
404
405 if (in_gap) {
406 emitter_table_printf(emitter, " ---\n");
407 }
408 }
409
410 static void
stats_arena_lextents_print(emitter_t * emitter,unsigned i)411 stats_arena_lextents_print(emitter_t *emitter, unsigned i) {
412 unsigned nbins, nlextents, j;
413 bool in_gap, in_gap_prev;
414
415 CTL_GET("arenas.nbins", &nbins, unsigned);
416 CTL_GET("arenas.nlextents", &nlextents, unsigned);
417
418 emitter_row_t header_row;
419 emitter_row_init(&header_row);
420 emitter_row_t row;
421 emitter_row_init(&row);
422
423 #define COL(name, left_or_right, col_width, etype) \
424 emitter_col_t header_##name; \
425 emitter_col_init(&header_##name, &header_row); \
426 header_##name.justify = emitter_justify_##left_or_right; \
427 header_##name.width = col_width; \
428 header_##name.type = emitter_type_title; \
429 header_##name.str_val = #name; \
430 \
431 emitter_col_t col_##name; \
432 emitter_col_init(&col_##name, &row); \
433 col_##name.justify = emitter_justify_##left_or_right; \
434 col_##name.width = col_width; \
435 col_##name.type = emitter_type_##etype;
436
437 COL(size, right, 20, size)
438 COL(ind, right, 4, unsigned)
439 COL(allocated, right, 13, size)
440 COL(nmalloc, right, 13, uint64)
441 COL(ndalloc, right, 13, uint64)
442 COL(nrequests, right, 13, uint64)
443 COL(curlextents, right, 13, size)
444 #undef COL
445
446 /* As with bins, we label the large extents table. */
447 header_size.width -= 6;
448 emitter_table_printf(emitter, "large:");
449 emitter_table_row(emitter, &header_row);
450 emitter_json_arr_begin(emitter, "lextents");
451
452 for (j = 0, in_gap = false; j < nlextents; j++) {
453 uint64_t nmalloc, ndalloc, nrequests;
454 size_t lextent_size, curlextents;
455
456 CTL_M2_M4_GET("stats.arenas.0.lextents.0.nmalloc", i, j,
457 &nmalloc, uint64_t);
458 CTL_M2_M4_GET("stats.arenas.0.lextents.0.ndalloc", i, j,
459 &ndalloc, uint64_t);
460 CTL_M2_M4_GET("stats.arenas.0.lextents.0.nrequests", i, j,
461 &nrequests, uint64_t);
462 in_gap_prev = in_gap;
463 in_gap = (nrequests == 0);
464
465 if (in_gap_prev && !in_gap) {
466 emitter_table_printf(emitter,
467 " ---\n");
468 }
469
470 CTL_M2_GET("arenas.lextent.0.size", j, &lextent_size, size_t);
471 CTL_M2_M4_GET("stats.arenas.0.lextents.0.curlextents", i, j,
472 &curlextents, size_t);
473
474 emitter_json_arr_obj_begin(emitter);
475 emitter_json_kv(emitter, "curlextents", emitter_type_size,
476 &curlextents);
477 emitter_json_arr_obj_end(emitter);
478
479 col_size.size_val = lextent_size;
480 col_ind.unsigned_val = nbins + j;
481 col_allocated.size_val = curlextents * lextent_size;
482 col_nmalloc.uint64_val = nmalloc;
483 col_ndalloc.uint64_val = ndalloc;
484 col_nrequests.uint64_val = nrequests;
485 col_curlextents.size_val = curlextents;
486
487 if (!in_gap) {
488 emitter_table_row(emitter, &row);
489 }
490 }
491 emitter_json_arr_end(emitter); /* Close "lextents". */
492 if (in_gap) {
493 emitter_table_printf(emitter, " ---\n");
494 }
495 }
496
497 static void
stats_arena_mutexes_print(emitter_t * emitter,unsigned arena_ind)498 stats_arena_mutexes_print(emitter_t *emitter, unsigned arena_ind) {
499 emitter_row_t row;
500 emitter_col_t col_name;
501 emitter_col_t col64[mutex_prof_num_uint64_t_counters];
502 emitter_col_t col32[mutex_prof_num_uint32_t_counters];
503
504 emitter_row_init(&row);
505 mutex_stats_init_cols(&row, "", &col_name, col64, col32);
506
507 emitter_json_dict_begin(emitter, "mutexes");
508 emitter_table_row(emitter, &row);
509
510 for (mutex_prof_arena_ind_t i = 0; i < mutex_prof_num_arena_mutexes;
511 i++) {
512 const char *name = arena_mutex_names[i];
513 emitter_json_dict_begin(emitter, name);
514 mutex_stats_read_arena(arena_ind, i, name, &col_name, col64,
515 col32);
516 mutex_stats_emit(emitter, &row, col64, col32);
517 emitter_json_dict_end(emitter); /* Close the mutex dict. */
518 }
519 emitter_json_dict_end(emitter); /* End "mutexes". */
520 }
521
522 static void
stats_arena_print(emitter_t * emitter,unsigned i,bool bins,bool large,bool mutex)523 stats_arena_print(emitter_t *emitter, unsigned i, bool bins, bool large,
524 bool mutex) {
525 unsigned nthreads;
526 const char *dss;
527 ssize_t dirty_decay_ms, muzzy_decay_ms;
528 size_t page, pactive, pdirty, pmuzzy, mapped, retained;
529 size_t base, internal, resident, metadata_thp;
530 uint64_t dirty_npurge, dirty_nmadvise, dirty_purged;
531 uint64_t muzzy_npurge, muzzy_nmadvise, muzzy_purged;
532 size_t small_allocated;
533 uint64_t small_nmalloc, small_ndalloc, small_nrequests;
534 size_t large_allocated;
535 uint64_t large_nmalloc, large_ndalloc, large_nrequests;
536 size_t tcache_bytes;
537 uint64_t uptime;
538
539 CTL_GET("arenas.page", &page, size_t);
540
541 CTL_M2_GET("stats.arenas.0.nthreads", i, &nthreads, unsigned);
542 emitter_kv(emitter, "nthreads", "assigned threads",
543 emitter_type_unsigned, &nthreads);
544
545 CTL_M2_GET("stats.arenas.0.uptime", i, &uptime, uint64_t);
546 emitter_kv(emitter, "uptime_ns", "uptime", emitter_type_uint64,
547 &uptime);
548
549 CTL_M2_GET("stats.arenas.0.dss", i, &dss, const char *);
550 emitter_kv(emitter, "dss", "dss allocation precedence",
551 emitter_type_string, &dss);
552
553 CTL_M2_GET("stats.arenas.0.dirty_decay_ms", i, &dirty_decay_ms,
554 ssize_t);
555 CTL_M2_GET("stats.arenas.0.muzzy_decay_ms", i, &muzzy_decay_ms,
556 ssize_t);
557 CTL_M2_GET("stats.arenas.0.pactive", i, &pactive, size_t);
558 CTL_M2_GET("stats.arenas.0.pdirty", i, &pdirty, size_t);
559 CTL_M2_GET("stats.arenas.0.pmuzzy", i, &pmuzzy, size_t);
560 CTL_M2_GET("stats.arenas.0.dirty_npurge", i, &dirty_npurge, uint64_t);
561 CTL_M2_GET("stats.arenas.0.dirty_nmadvise", i, &dirty_nmadvise,
562 uint64_t);
563 CTL_M2_GET("stats.arenas.0.dirty_purged", i, &dirty_purged, uint64_t);
564 CTL_M2_GET("stats.arenas.0.muzzy_npurge", i, &muzzy_npurge, uint64_t);
565 CTL_M2_GET("stats.arenas.0.muzzy_nmadvise", i, &muzzy_nmadvise,
566 uint64_t);
567 CTL_M2_GET("stats.arenas.0.muzzy_purged", i, &muzzy_purged, uint64_t);
568
569 emitter_row_t decay_row;
570 emitter_row_init(&decay_row);
571
572 /* JSON-style emission. */
573 emitter_json_kv(emitter, "dirty_decay_ms", emitter_type_ssize,
574 &dirty_decay_ms);
575 emitter_json_kv(emitter, "muzzy_decay_ms", emitter_type_ssize,
576 &muzzy_decay_ms);
577
578 emitter_json_kv(emitter, "pactive", emitter_type_size, &pactive);
579 emitter_json_kv(emitter, "pdirty", emitter_type_size, &pdirty);
580 emitter_json_kv(emitter, "pmuzzy", emitter_type_size, &pmuzzy);
581
582 emitter_json_kv(emitter, "dirty_npurge", emitter_type_uint64,
583 &dirty_npurge);
584 emitter_json_kv(emitter, "dirty_nmadvise", emitter_type_uint64,
585 &dirty_nmadvise);
586 emitter_json_kv(emitter, "dirty_purged", emitter_type_uint64,
587 &dirty_purged);
588
589 emitter_json_kv(emitter, "muzzy_npurge", emitter_type_uint64,
590 &muzzy_npurge);
591 emitter_json_kv(emitter, "muzzy_nmadvise", emitter_type_uint64,
592 &muzzy_nmadvise);
593 emitter_json_kv(emitter, "muzzy_purged", emitter_type_uint64,
594 &muzzy_purged);
595
596 /* Table-style emission. */
597 emitter_col_t decay_type;
598 emitter_col_init(&decay_type, &decay_row);
599 decay_type.justify = emitter_justify_right;
600 decay_type.width = 9;
601 decay_type.type = emitter_type_title;
602 decay_type.str_val = "decaying:";
603
604 emitter_col_t decay_time;
605 emitter_col_init(&decay_time, &decay_row);
606 decay_time.justify = emitter_justify_right;
607 decay_time.width = 6;
608 decay_time.type = emitter_type_title;
609 decay_time.str_val = "time";
610
611 emitter_col_t decay_npages;
612 emitter_col_init(&decay_npages, &decay_row);
613 decay_npages.justify = emitter_justify_right;
614 decay_npages.width = 13;
615 decay_npages.type = emitter_type_title;
616 decay_npages.str_val = "npages";
617
618 emitter_col_t decay_sweeps;
619 emitter_col_init(&decay_sweeps, &decay_row);
620 decay_sweeps.justify = emitter_justify_right;
621 decay_sweeps.width = 13;
622 decay_sweeps.type = emitter_type_title;
623 decay_sweeps.str_val = "sweeps";
624
625 emitter_col_t decay_madvises;
626 emitter_col_init(&decay_madvises, &decay_row);
627 decay_madvises.justify = emitter_justify_right;
628 decay_madvises.width = 13;
629 decay_madvises.type = emitter_type_title;
630 decay_madvises.str_val = "madvises";
631
632 emitter_col_t decay_purged;
633 emitter_col_init(&decay_purged, &decay_row);
634 decay_purged.justify = emitter_justify_right;
635 decay_purged.width = 13;
636 decay_purged.type = emitter_type_title;
637 decay_purged.str_val = "purged";
638
639 /* Title row. */
640 emitter_table_row(emitter, &decay_row);
641
642 /* Dirty row. */
643 decay_type.str_val = "dirty:";
644
645 if (dirty_decay_ms >= 0) {
646 decay_time.type = emitter_type_ssize;
647 decay_time.ssize_val = dirty_decay_ms;
648 } else {
649 decay_time.type = emitter_type_title;
650 decay_time.str_val = "N/A";
651 }
652
653 decay_npages.type = emitter_type_size;
654 decay_npages.size_val = pdirty;
655
656 decay_sweeps.type = emitter_type_uint64;
657 decay_sweeps.uint64_val = dirty_npurge;
658
659 decay_madvises.type = emitter_type_uint64;
660 decay_madvises.uint64_val = dirty_nmadvise;
661
662 decay_purged.type = emitter_type_uint64;
663 decay_purged.uint64_val = dirty_purged;
664
665 emitter_table_row(emitter, &decay_row);
666
667 /* Muzzy row. */
668 decay_type.str_val = "muzzy:";
669
670 if (muzzy_decay_ms >= 0) {
671 decay_time.type = emitter_type_ssize;
672 decay_time.ssize_val = muzzy_decay_ms;
673 } else {
674 decay_time.type = emitter_type_title;
675 decay_time.str_val = "N/A";
676 }
677
678 decay_npages.type = emitter_type_size;
679 decay_npages.size_val = pmuzzy;
680
681 decay_sweeps.type = emitter_type_uint64;
682 decay_sweeps.uint64_val = muzzy_npurge;
683
684 decay_madvises.type = emitter_type_uint64;
685 decay_madvises.uint64_val = muzzy_nmadvise;
686
687 decay_purged.type = emitter_type_uint64;
688 decay_purged.uint64_val = muzzy_purged;
689
690 emitter_table_row(emitter, &decay_row);
691
692 /* Small / large / total allocation counts. */
693 emitter_row_t alloc_count_row;
694 emitter_row_init(&alloc_count_row);
695
696 emitter_col_t alloc_count_title;
697 emitter_col_init(&alloc_count_title, &alloc_count_row);
698 alloc_count_title.justify = emitter_justify_left;
699 alloc_count_title.width = 25;
700 alloc_count_title.type = emitter_type_title;
701 alloc_count_title.str_val = "";
702
703 emitter_col_t alloc_count_allocated;
704 emitter_col_init(&alloc_count_allocated, &alloc_count_row);
705 alloc_count_allocated.justify = emitter_justify_right;
706 alloc_count_allocated.width = 12;
707 alloc_count_allocated.type = emitter_type_title;
708 alloc_count_allocated.str_val = "allocated";
709
710 emitter_col_t alloc_count_nmalloc;
711 emitter_col_init(&alloc_count_nmalloc, &alloc_count_row);
712 alloc_count_nmalloc.justify = emitter_justify_right;
713 alloc_count_nmalloc.width = 12;
714 alloc_count_nmalloc.type = emitter_type_title;
715 alloc_count_nmalloc.str_val = "nmalloc";
716
717 emitter_col_t alloc_count_ndalloc;
718 emitter_col_init(&alloc_count_ndalloc, &alloc_count_row);
719 alloc_count_ndalloc.justify = emitter_justify_right;
720 alloc_count_ndalloc.width = 12;
721 alloc_count_ndalloc.type = emitter_type_title;
722 alloc_count_ndalloc.str_val = "ndalloc";
723
724 emitter_col_t alloc_count_nrequests;
725 emitter_col_init(&alloc_count_nrequests, &alloc_count_row);
726 alloc_count_nrequests.justify = emitter_justify_right;
727 alloc_count_nrequests.width = 12;
728 alloc_count_nrequests.type = emitter_type_title;
729 alloc_count_nrequests.str_val = "nrequests";
730
731 emitter_table_row(emitter, &alloc_count_row);
732
733 #define GET_AND_EMIT_ALLOC_STAT(small_or_large, name, valtype) \
734 CTL_M2_GET("stats.arenas.0." #small_or_large "." #name, i, \
735 &small_or_large##_##name, valtype##_t); \
736 emitter_json_kv(emitter, #name, emitter_type_##valtype, \
737 &small_or_large##_##name); \
738 alloc_count_##name.type = emitter_type_##valtype; \
739 alloc_count_##name.valtype##_val = small_or_large##_##name;
740
741 emitter_json_dict_begin(emitter, "small");
742 alloc_count_title.str_val = "small:";
743
744 GET_AND_EMIT_ALLOC_STAT(small, allocated, size)
745 GET_AND_EMIT_ALLOC_STAT(small, nmalloc, uint64)
746 GET_AND_EMIT_ALLOC_STAT(small, ndalloc, uint64)
747 GET_AND_EMIT_ALLOC_STAT(small, nrequests, uint64)
748
749 emitter_table_row(emitter, &alloc_count_row);
750 emitter_json_dict_end(emitter); /* Close "small". */
751
752 emitter_json_dict_begin(emitter, "large");
753 alloc_count_title.str_val = "large:";
754
755 GET_AND_EMIT_ALLOC_STAT(large, allocated, size)
756 GET_AND_EMIT_ALLOC_STAT(large, nmalloc, uint64)
757 GET_AND_EMIT_ALLOC_STAT(large, ndalloc, uint64)
758 GET_AND_EMIT_ALLOC_STAT(large, nrequests, uint64)
759
760 emitter_table_row(emitter, &alloc_count_row);
761 emitter_json_dict_end(emitter); /* Close "large". */
762
763 #undef GET_AND_EMIT_ALLOC_STAT
764
765 /* Aggregated small + large stats are emitter only in table mode. */
766 alloc_count_title.str_val = "total:";
767 alloc_count_allocated.size_val = small_allocated + large_allocated;
768 alloc_count_nmalloc.uint64_val = small_nmalloc + large_nmalloc;
769 alloc_count_ndalloc.uint64_val = small_ndalloc + large_ndalloc;
770 alloc_count_nrequests.uint64_val = small_nrequests + large_nrequests;
771 emitter_table_row(emitter, &alloc_count_row);
772
773 emitter_row_t mem_count_row;
774 emitter_row_init(&mem_count_row);
775
776 emitter_col_t mem_count_title;
777 emitter_col_init(&mem_count_title, &mem_count_row);
778 mem_count_title.justify = emitter_justify_left;
779 mem_count_title.width = 25;
780 mem_count_title.type = emitter_type_title;
781 mem_count_title.str_val = "";
782
783 emitter_col_t mem_count_val;
784 emitter_col_init(&mem_count_val, &mem_count_row);
785 mem_count_val.justify = emitter_justify_right;
786 mem_count_val.width = 12;
787 mem_count_val.type = emitter_type_title;
788 mem_count_val.str_val = "";
789
790 emitter_table_row(emitter, &mem_count_row);
791 mem_count_val.type = emitter_type_size;
792
793 /* Active count in bytes is emitted only in table mode. */
794 mem_count_title.str_val = "active:";
795 mem_count_val.size_val = pactive * page;
796 emitter_table_row(emitter, &mem_count_row);
797
798 #define GET_AND_EMIT_MEM_STAT(stat) \
799 CTL_M2_GET("stats.arenas.0."#stat, i, &stat, size_t); \
800 emitter_json_kv(emitter, #stat, emitter_type_size, &stat); \
801 mem_count_title.str_val = #stat":"; \
802 mem_count_val.size_val = stat; \
803 emitter_table_row(emitter, &mem_count_row);
804
805 GET_AND_EMIT_MEM_STAT(mapped)
806 GET_AND_EMIT_MEM_STAT(retained)
807 GET_AND_EMIT_MEM_STAT(base)
808 GET_AND_EMIT_MEM_STAT(internal)
809 GET_AND_EMIT_MEM_STAT(metadata_thp)
810 GET_AND_EMIT_MEM_STAT(tcache_bytes)
811 GET_AND_EMIT_MEM_STAT(resident)
812 #undef GET_AND_EMIT_MEM_STAT
813
814 if (mutex) {
815 stats_arena_mutexes_print(emitter, i);
816 }
817 if (bins) {
818 stats_arena_bins_print(emitter, mutex, i);
819 }
820 if (large) {
821 stats_arena_lextents_print(emitter, i);
822 }
823 }
824
825 static void
stats_general_print(emitter_t * emitter)826 stats_general_print(emitter_t *emitter) {
827 const char *cpv;
828 bool bv, bv2;
829 unsigned uv;
830 uint32_t u32v;
831 uint64_t u64v;
832 ssize_t ssv, ssv2;
833 size_t sv, bsz, usz, ssz, sssz, cpsz;
834
835 bsz = sizeof(bool);
836 usz = sizeof(unsigned);
837 ssz = sizeof(size_t);
838 sssz = sizeof(ssize_t);
839 cpsz = sizeof(const char *);
840
841 CTL_GET("version", &cpv, const char *);
842 emitter_kv(emitter, "version", "Version", emitter_type_string, &cpv);
843
844 /* config. */
845 emitter_dict_begin(emitter, "config", "Build-time option settings");
846 #define CONFIG_WRITE_BOOL(name) \
847 do { \
848 CTL_GET("config."#name, &bv, bool); \
849 emitter_kv(emitter, #name, "config."#name, \
850 emitter_type_bool, &bv); \
851 } while (0)
852
853 CONFIG_WRITE_BOOL(cache_oblivious);
854 CONFIG_WRITE_BOOL(debug);
855 CONFIG_WRITE_BOOL(fill);
856 CONFIG_WRITE_BOOL(lazy_lock);
857 emitter_kv(emitter, "malloc_conf", "config.malloc_conf",
858 emitter_type_string, &config_malloc_conf);
859
860 CONFIG_WRITE_BOOL(prof);
861 CONFIG_WRITE_BOOL(prof_libgcc);
862 CONFIG_WRITE_BOOL(prof_libunwind);
863 CONFIG_WRITE_BOOL(stats);
864 CONFIG_WRITE_BOOL(utrace);
865 CONFIG_WRITE_BOOL(xmalloc);
866 #undef CONFIG_WRITE_BOOL
867 emitter_dict_end(emitter); /* Close "config" dict. */
868
869 /* opt. */
870 #define OPT_WRITE(name, var, size, emitter_type) \
871 if (je_mallctl("opt."name, (void *)&var, &size, NULL, 0) == \
872 0) { \
873 emitter_kv(emitter, name, "opt."name, emitter_type, \
874 &var); \
875 }
876
877 #define OPT_WRITE_MUTABLE(name, var1, var2, size, emitter_type, \
878 altname) \
879 if (je_mallctl("opt."name, (void *)&var1, &size, NULL, 0) == \
880 0 && je_mallctl(altname, (void *)&var2, &size, NULL, 0) \
881 == 0) { \
882 emitter_kv_note(emitter, name, "opt."name, \
883 emitter_type, &var1, altname, emitter_type, \
884 &var2); \
885 }
886
887 #define OPT_WRITE_BOOL(name) OPT_WRITE(name, bv, bsz, emitter_type_bool)
888 #define OPT_WRITE_BOOL_MUTABLE(name, altname) \
889 OPT_WRITE_MUTABLE(name, bv, bv2, bsz, emitter_type_bool, altname)
890
891 #define OPT_WRITE_UNSIGNED(name) \
892 OPT_WRITE(name, uv, usz, emitter_type_unsigned)
893
894 #define OPT_WRITE_SSIZE_T(name) \
895 OPT_WRITE(name, ssv, sssz, emitter_type_ssize)
896 #define OPT_WRITE_SSIZE_T_MUTABLE(name, altname) \
897 OPT_WRITE_MUTABLE(name, ssv, ssv2, sssz, emitter_type_ssize, \
898 altname)
899
900 #define OPT_WRITE_CHAR_P(name) \
901 OPT_WRITE(name, cpv, cpsz, emitter_type_string)
902
903 emitter_dict_begin(emitter, "opt", "Run-time option settings");
904
905 OPT_WRITE_BOOL("abort")
906 OPT_WRITE_BOOL("abort_conf")
907 OPT_WRITE_BOOL("retain")
908 OPT_WRITE_CHAR_P("dss")
909 OPT_WRITE_UNSIGNED("narenas")
910 OPT_WRITE_CHAR_P("percpu_arena")
911 OPT_WRITE_CHAR_P("metadata_thp")
912 OPT_WRITE_BOOL_MUTABLE("background_thread", "background_thread")
913 OPT_WRITE_SSIZE_T_MUTABLE("dirty_decay_ms", "arenas.dirty_decay_ms")
914 OPT_WRITE_SSIZE_T_MUTABLE("muzzy_decay_ms", "arenas.muzzy_decay_ms")
915 OPT_WRITE_UNSIGNED("lg_extent_max_active_fit")
916 OPT_WRITE_CHAR_P("junk")
917 OPT_WRITE_BOOL("zero")
918 OPT_WRITE_BOOL("utrace")
919 OPT_WRITE_BOOL("xmalloc")
920 OPT_WRITE_BOOL("tcache")
921 OPT_WRITE_SSIZE_T("lg_tcache_max")
922 OPT_WRITE_CHAR_P("thp")
923 OPT_WRITE_BOOL("prof")
924 OPT_WRITE_CHAR_P("prof_prefix")
925 OPT_WRITE_BOOL_MUTABLE("prof_active", "prof.active")
926 OPT_WRITE_BOOL_MUTABLE("prof_thread_active_init",
927 "prof.thread_active_init")
928 OPT_WRITE_SSIZE_T_MUTABLE("lg_prof_sample", "prof.lg_sample")
929 OPT_WRITE_BOOL("prof_accum")
930 OPT_WRITE_SSIZE_T("lg_prof_interval")
931 OPT_WRITE_BOOL("prof_gdump")
932 OPT_WRITE_BOOL("prof_final")
933 OPT_WRITE_BOOL("prof_leak")
934 OPT_WRITE_BOOL("stats_print")
935 OPT_WRITE_CHAR_P("stats_print_opts")
936
937 emitter_dict_end(emitter);
938
939 #undef OPT_WRITE
940 #undef OPT_WRITE_MUTABLE
941 #undef OPT_WRITE_BOOL
942 #undef OPT_WRITE_BOOL_MUTABLE
943 #undef OPT_WRITE_UNSIGNED
944 #undef OPT_WRITE_SSIZE_T
945 #undef OPT_WRITE_SSIZE_T_MUTABLE
946 #undef OPT_WRITE_CHAR_P
947
948 /* prof. */
949 if (config_prof) {
950 emitter_dict_begin(emitter, "prof", "Profiling settings");
951
952 CTL_GET("prof.thread_active_init", &bv, bool);
953 emitter_kv(emitter, "thread_active_init",
954 "prof.thread_active_init", emitter_type_bool, &bv);
955
956 CTL_GET("prof.active", &bv, bool);
957 emitter_kv(emitter, "active", "prof.active", emitter_type_bool,
958 &bv);
959
960 CTL_GET("prof.gdump", &bv, bool);
961 emitter_kv(emitter, "gdump", "prof.gdump", emitter_type_bool,
962 &bv);
963
964 CTL_GET("prof.interval", &u64v, uint64_t);
965 emitter_kv(emitter, "interval", "prof.interval",
966 emitter_type_uint64, &u64v);
967
968 CTL_GET("prof.lg_sample", &ssv, ssize_t);
969 emitter_kv(emitter, "lg_sample", "prof.lg_sample",
970 emitter_type_ssize, &ssv);
971
972 emitter_dict_end(emitter); /* Close "prof". */
973 }
974
975 /* arenas. */
976 /*
977 * The json output sticks arena info into an "arenas" dict; the table
978 * output puts them at the top-level.
979 */
980 emitter_json_dict_begin(emitter, "arenas");
981
982 CTL_GET("arenas.narenas", &uv, unsigned);
983 emitter_kv(emitter, "narenas", "Arenas", emitter_type_unsigned, &uv);
984
985 /*
986 * Decay settings are emitted only in json mode; in table mode, they're
987 * emitted as notes with the opt output, above.
988 */
989 CTL_GET("arenas.dirty_decay_ms", &ssv, ssize_t);
990 emitter_json_kv(emitter, "dirty_decay_ms", emitter_type_ssize, &ssv);
991
992 CTL_GET("arenas.muzzy_decay_ms", &ssv, ssize_t);
993 emitter_json_kv(emitter, "muzzy_decay_ms", emitter_type_ssize, &ssv);
994
995 CTL_GET("arenas.quantum", &sv, size_t);
996 emitter_kv(emitter, "quantum", "Quantum size", emitter_type_size, &sv);
997
998 CTL_GET("arenas.page", &sv, size_t);
999 emitter_kv(emitter, "page", "Page size", emitter_type_size, &sv);
1000
1001 if (je_mallctl("arenas.tcache_max", (void *)&sv, &ssz, NULL, 0) == 0) {
1002 emitter_kv(emitter, "tcache_max",
1003 "Maximum thread-cached size class", emitter_type_size, &sv);
1004 }
1005
1006 unsigned nbins;
1007 CTL_GET("arenas.nbins", &nbins, unsigned);
1008 emitter_kv(emitter, "nbins", "Number of bin size classes",
1009 emitter_type_unsigned, &nbins);
1010
1011 unsigned nhbins;
1012 CTL_GET("arenas.nhbins", &nhbins, unsigned);
1013 emitter_kv(emitter, "nhbins", "Number of thread-cache bin size classes",
1014 emitter_type_unsigned, &nhbins);
1015
1016 /*
1017 * We do enough mallctls in a loop that we actually want to omit them
1018 * (not just omit the printing).
1019 */
1020 if (emitter->output == emitter_output_json) {
1021 emitter_json_arr_begin(emitter, "bin");
1022 for (unsigned i = 0; i < nbins; i++) {
1023 emitter_json_arr_obj_begin(emitter);
1024
1025 CTL_M2_GET("arenas.bin.0.size", i, &sv, size_t);
1026 emitter_json_kv(emitter, "size", emitter_type_size,
1027 &sv);
1028
1029 CTL_M2_GET("arenas.bin.0.nregs", i, &u32v, uint32_t);
1030 emitter_json_kv(emitter, "nregs", emitter_type_uint32,
1031 &u32v);
1032
1033 CTL_M2_GET("arenas.bin.0.slab_size", i, &sv, size_t);
1034 emitter_json_kv(emitter, "slab_size", emitter_type_size,
1035 &sv);
1036
1037 emitter_json_arr_obj_end(emitter);
1038 }
1039 emitter_json_arr_end(emitter); /* Close "bin". */
1040 }
1041
1042 unsigned nlextents;
1043 CTL_GET("arenas.nlextents", &nlextents, unsigned);
1044 emitter_kv(emitter, "nlextents", "Number of large size classes",
1045 emitter_type_unsigned, &nlextents);
1046
1047 if (emitter->output == emitter_output_json) {
1048 emitter_json_arr_begin(emitter, "lextent");
1049 for (unsigned i = 0; i < nlextents; i++) {
1050 emitter_json_arr_obj_begin(emitter);
1051
1052 CTL_M2_GET("arenas.lextent.0.size", i, &sv, size_t);
1053 emitter_json_kv(emitter, "size", emitter_type_size,
1054 &sv);
1055
1056 emitter_json_arr_obj_end(emitter);
1057 }
1058 emitter_json_arr_end(emitter); /* Close "lextent". */
1059 }
1060
1061 emitter_json_dict_end(emitter); /* Close "arenas" */
1062 }
1063
1064 static void
stats_print_helper(emitter_t * emitter,bool merged,bool destroyed,bool unmerged,bool bins,bool large,bool mutex)1065 stats_print_helper(emitter_t *emitter, bool merged, bool destroyed,
1066 bool unmerged, bool bins, bool large, bool mutex) {
1067 /*
1068 * These should be deleted. We keep them around for a while, to aid in
1069 * the transition to the emitter code.
1070 */
1071 size_t allocated, active, metadata, metadata_thp, resident, mapped,
1072 retained;
1073 size_t num_background_threads;
1074 uint64_t background_thread_num_runs, background_thread_run_interval;
1075
1076 CTL_GET("stats.allocated", &allocated, size_t);
1077 CTL_GET("stats.active", &active, size_t);
1078 CTL_GET("stats.metadata", &metadata, size_t);
1079 CTL_GET("stats.metadata_thp", &metadata_thp, size_t);
1080 CTL_GET("stats.resident", &resident, size_t);
1081 CTL_GET("stats.mapped", &mapped, size_t);
1082 CTL_GET("stats.retained", &retained, size_t);
1083
1084 if (have_background_thread) {
1085 CTL_GET("stats.background_thread.num_threads",
1086 &num_background_threads, size_t);
1087 CTL_GET("stats.background_thread.num_runs",
1088 &background_thread_num_runs, uint64_t);
1089 CTL_GET("stats.background_thread.run_interval",
1090 &background_thread_run_interval, uint64_t);
1091 } else {
1092 num_background_threads = 0;
1093 background_thread_num_runs = 0;
1094 background_thread_run_interval = 0;
1095 }
1096
1097 /* Generic global stats. */
1098 emitter_json_dict_begin(emitter, "stats");
1099 emitter_json_kv(emitter, "allocated", emitter_type_size, &allocated);
1100 emitter_json_kv(emitter, "active", emitter_type_size, &active);
1101 emitter_json_kv(emitter, "metadata", emitter_type_size, &metadata);
1102 emitter_json_kv(emitter, "metadata_thp", emitter_type_size,
1103 &metadata_thp);
1104 emitter_json_kv(emitter, "resident", emitter_type_size, &resident);
1105 emitter_json_kv(emitter, "mapped", emitter_type_size, &mapped);
1106 emitter_json_kv(emitter, "retained", emitter_type_size, &retained);
1107
1108 emitter_table_printf(emitter, "Allocated: %zu, active: %zu, "
1109 "metadata: %zu (n_thp %zu), resident: %zu, mapped: %zu, "
1110 "retained: %zu\n", allocated, active, metadata, metadata_thp,
1111 resident, mapped, retained);
1112
1113 /* Background thread stats. */
1114 emitter_json_dict_begin(emitter, "background_thread");
1115 emitter_json_kv(emitter, "num_threads", emitter_type_size,
1116 &num_background_threads);
1117 emitter_json_kv(emitter, "num_runs", emitter_type_uint64,
1118 &background_thread_num_runs);
1119 emitter_json_kv(emitter, "run_interval", emitter_type_uint64,
1120 &background_thread_run_interval);
1121 emitter_json_dict_end(emitter); /* Close "background_thread". */
1122
1123 emitter_table_printf(emitter, "Background threads: %zu, "
1124 "num_runs: %"FMTu64", run_interval: %"FMTu64" ns\n",
1125 num_background_threads, background_thread_num_runs,
1126 background_thread_run_interval);
1127
1128 if (mutex) {
1129 emitter_row_t row;
1130 emitter_col_t name;
1131 emitter_col_t col64[mutex_prof_num_uint64_t_counters];
1132 emitter_col_t col32[mutex_prof_num_uint32_t_counters];
1133
1134 emitter_row_init(&row);
1135 mutex_stats_init_cols(&row, "", &name, col64, col32);
1136
1137 emitter_table_row(emitter, &row);
1138 emitter_json_dict_begin(emitter, "mutexes");
1139
1140 for (int i = 0; i < mutex_prof_num_global_mutexes; i++) {
1141 mutex_stats_read_global(global_mutex_names[i], &name,
1142 col64, col32);
1143 emitter_json_dict_begin(emitter, global_mutex_names[i]);
1144 mutex_stats_emit(emitter, &row, col64, col32);
1145 emitter_json_dict_end(emitter);
1146 }
1147
1148 emitter_json_dict_end(emitter); /* Close "mutexes". */
1149 }
1150
1151 emitter_json_dict_end(emitter); /* Close "stats". */
1152
1153 if (merged || destroyed || unmerged) {
1154 unsigned narenas;
1155
1156 emitter_json_dict_begin(emitter, "stats.arenas");
1157
1158 CTL_GET("arenas.narenas", &narenas, unsigned);
1159 size_t mib[3];
1160 size_t miblen = sizeof(mib) / sizeof(size_t);
1161 size_t sz;
1162 VARIABLE_ARRAY(bool, initialized, narenas);
1163 bool destroyed_initialized;
1164 unsigned i, j, ninitialized;
1165
1166 xmallctlnametomib("arena.0.initialized", mib, &miblen);
1167 for (i = ninitialized = 0; i < narenas; i++) {
1168 mib[1] = i;
1169 sz = sizeof(bool);
1170 xmallctlbymib(mib, miblen, &initialized[i], &sz,
1171 NULL, 0);
1172 if (initialized[i]) {
1173 ninitialized++;
1174 }
1175 }
1176 mib[1] = MALLCTL_ARENAS_DESTROYED;
1177 sz = sizeof(bool);
1178 xmallctlbymib(mib, miblen, &destroyed_initialized, &sz,
1179 NULL, 0);
1180
1181 /* Merged stats. */
1182 if (merged && (ninitialized > 1 || !unmerged)) {
1183 /* Print merged arena stats. */
1184 emitter_table_printf(emitter, "Merged arenas stats:\n");
1185 emitter_json_dict_begin(emitter, "merged");
1186 stats_arena_print(emitter, MALLCTL_ARENAS_ALL, bins,
1187 large, mutex);
1188 emitter_json_dict_end(emitter); /* Close "merged". */
1189 }
1190
1191 /* Destroyed stats. */
1192 if (destroyed_initialized && destroyed) {
1193 /* Print destroyed arena stats. */
1194 emitter_table_printf(emitter,
1195 "Destroyed arenas stats:\n");
1196 emitter_json_dict_begin(emitter, "destroyed");
1197 stats_arena_print(emitter, MALLCTL_ARENAS_DESTROYED,
1198 bins, large, mutex);
1199 emitter_json_dict_end(emitter); /* Close "destroyed". */
1200 }
1201
1202 /* Unmerged stats. */
1203 if (unmerged) {
1204 for (i = j = 0; i < narenas; i++) {
1205 if (initialized[i]) {
1206 char arena_ind_str[20];
1207 malloc_snprintf(arena_ind_str,
1208 sizeof(arena_ind_str), "%u", i);
1209 emitter_json_dict_begin(emitter,
1210 arena_ind_str);
1211 emitter_table_printf(emitter,
1212 "arenas[%s]:\n", arena_ind_str);
1213 stats_arena_print(emitter, i, bins,
1214 large, mutex);
1215 /* Close "<arena-ind>". */
1216 emitter_json_dict_end(emitter);
1217 }
1218 }
1219 }
1220 emitter_json_dict_end(emitter); /* Close "stats.arenas". */
1221 }
1222 }
1223
1224 void
stats_print(void (* write_cb)(void *,const char *),void * cbopaque,const char * opts)1225 stats_print(void (*write_cb)(void *, const char *), void *cbopaque,
1226 const char *opts) {
1227 int err;
1228 uint64_t epoch;
1229 size_t u64sz;
1230 #define OPTION(o, v, d, s) bool v = d;
1231 STATS_PRINT_OPTIONS
1232 #undef OPTION
1233
1234 /*
1235 * Refresh stats, in case mallctl() was called by the application.
1236 *
1237 * Check for OOM here, since refreshing the ctl cache can trigger
1238 * allocation. In practice, none of the subsequent mallctl()-related
1239 * calls in this function will cause OOM if this one succeeds.
1240 * */
1241 epoch = 1;
1242 u64sz = sizeof(uint64_t);
1243 err = je_mallctl("epoch", (void *)&epoch, &u64sz, (void *)&epoch,
1244 sizeof(uint64_t));
1245 if (err != 0) {
1246 if (err == EAGAIN) {
1247 malloc_write("<jemalloc>: Memory allocation failure in "
1248 "mallctl(\"epoch\", ...)\n");
1249 return;
1250 }
1251 malloc_write("<jemalloc>: Failure in mallctl(\"epoch\", "
1252 "...)\n");
1253 abort();
1254 }
1255
1256 if (opts != NULL) {
1257 for (unsigned i = 0; opts[i] != '\0'; i++) {
1258 switch (opts[i]) {
1259 #define OPTION(o, v, d, s) case o: v = s; break;
1260 STATS_PRINT_OPTIONS
1261 #undef OPTION
1262 default:;
1263 }
1264 }
1265 }
1266
1267 emitter_t emitter;
1268 emitter_init(&emitter,
1269 json ? emitter_output_json : emitter_output_table, write_cb,
1270 cbopaque);
1271 emitter_begin(&emitter);
1272 emitter_table_printf(&emitter, "___ Begin jemalloc statistics ___\n");
1273 emitter_json_dict_begin(&emitter, "jemalloc");
1274
1275 if (general) {
1276 stats_general_print(&emitter);
1277 }
1278 if (config_stats) {
1279 stats_print_helper(&emitter, merged, destroyed, unmerged,
1280 bins, large, mutex);
1281 }
1282
1283 emitter_json_dict_end(&emitter); /* Closes the "jemalloc" dict. */
1284 emitter_table_printf(&emitter, "--- End jemalloc statistics ---\n");
1285 emitter_end(&emitter);
1286 }
1287