1#define	JEMALLOC_STATS_C_
2#include "jemalloc/internal/jemalloc_internal.h"
3
4#define	CTL_GET(n, v, t) do {						\
5	size_t sz = sizeof(t);						\
6	xmallctl(n, (void *)v, &sz, NULL, 0);				\
7} while (0)
8
9#define	CTL_M2_GET(n, i, v, t) do {					\
10	size_t mib[6];							\
11	size_t miblen = sizeof(mib) / sizeof(size_t);			\
12	size_t sz = sizeof(t);						\
13	xmallctlnametomib(n, mib, &miblen);				\
14	mib[2] = (i);							\
15	xmallctlbymib(mib, miblen, (void *)v, &sz, NULL, 0);		\
16} while (0)
17
18#define	CTL_M2_M4_GET(n, i, j, v, t) do {				\
19	size_t mib[6];							\
20	size_t miblen = sizeof(mib) / sizeof(size_t);			\
21	size_t sz = sizeof(t);						\
22	xmallctlnametomib(n, mib, &miblen);				\
23	mib[2] = (i);							\
24	mib[4] = (j);							\
25	xmallctlbymib(mib, miblen, (void *)v, &sz, NULL, 0);		\
26} while (0)
27
28/******************************************************************************/
29/* Data. */
30
31bool	opt_stats_print = false;
32
33/******************************************************************************/
34
35static void
36stats_arena_bins_print(void (*write_cb)(void *, const char *), void *cbopaque,
37    bool json, bool large, unsigned i)
38{
39	size_t page;
40	bool in_gap, in_gap_prev;
41	unsigned nbins, j;
42
43	CTL_GET("arenas.page", &page, size_t);
44
45	CTL_GET("arenas.nbins", &nbins, unsigned);
46	if (json) {
47		malloc_cprintf(write_cb, cbopaque,
48		    "\t\t\t\t\"bins\": [\n");
49	} else {
50		if (config_tcache) {
51			malloc_cprintf(write_cb, cbopaque,
52			    "bins:           size ind    allocated      nmalloc"
53			    "      ndalloc    nrequests      curregs"
54			    "     curslabs regs pgs  util       nfills"
55			    "     nflushes     newslabs      reslabs\n");
56		} else {
57			malloc_cprintf(write_cb, cbopaque,
58			    "bins:           size ind    allocated      nmalloc"
59			    "      ndalloc    nrequests      curregs"
60			    "     curslabs regs pgs  util     newslabs"
61			    "      reslabs\n");
62		}
63	}
64	for (j = 0, in_gap = false; j < nbins; j++) {
65		uint64_t nslabs;
66		size_t reg_size, slab_size, curregs;
67		size_t curslabs;
68		uint32_t nregs;
69		uint64_t nmalloc, ndalloc, nrequests, nfills, nflushes;
70		uint64_t nreslabs;
71
72		CTL_M2_M4_GET("stats.arenas.0.bins.0.nslabs", i, j, &nslabs,
73		    uint64_t);
74		in_gap_prev = in_gap;
75		in_gap = (nslabs == 0);
76
77		if (!json && in_gap_prev && !in_gap) {
78			malloc_cprintf(write_cb, cbopaque,
79			    "                     ---\n");
80		}
81
82		CTL_M2_GET("arenas.bin.0.size", j, &reg_size, size_t);
83		CTL_M2_GET("arenas.bin.0.nregs", j, &nregs, uint32_t);
84		CTL_M2_GET("arenas.bin.0.slab_size", j, &slab_size, size_t);
85
86		CTL_M2_M4_GET("stats.arenas.0.bins.0.nmalloc", i, j, &nmalloc,
87		    uint64_t);
88		CTL_M2_M4_GET("stats.arenas.0.bins.0.ndalloc", i, j, &ndalloc,
89		    uint64_t);
90		CTL_M2_M4_GET("stats.arenas.0.bins.0.curregs", i, j, &curregs,
91		    size_t);
92		CTL_M2_M4_GET("stats.arenas.0.bins.0.nrequests", i, j,
93		    &nrequests, uint64_t);
94		if (config_tcache) {
95			CTL_M2_M4_GET("stats.arenas.0.bins.0.nfills", i, j,
96			    &nfills, uint64_t);
97			CTL_M2_M4_GET("stats.arenas.0.bins.0.nflushes", i, j,
98			    &nflushes, uint64_t);
99		}
100		CTL_M2_M4_GET("stats.arenas.0.bins.0.nreslabs", i, j, &nreslabs,
101		    uint64_t);
102		CTL_M2_M4_GET("stats.arenas.0.bins.0.curslabs", i, j, &curslabs,
103		    size_t);
104
105		if (json) {
106			malloc_cprintf(write_cb, cbopaque,
107			    "\t\t\t\t\t{\n"
108			    "\t\t\t\t\t\t\"nmalloc\": %"FMTu64",\n"
109			    "\t\t\t\t\t\t\"ndalloc\": %"FMTu64",\n"
110			    "\t\t\t\t\t\t\"curregs\": %zu,\n"
111			    "\t\t\t\t\t\t\"nrequests\": %"FMTu64",\n",
112			    nmalloc,
113			    ndalloc,
114			    curregs,
115			    nrequests);
116			if (config_tcache) {
117				malloc_cprintf(write_cb, cbopaque,
118				    "\t\t\t\t\t\t\"nfills\": %"FMTu64",\n"
119				    "\t\t\t\t\t\t\"nflushes\": %"FMTu64",\n",
120				    nfills,
121				    nflushes);
122			}
123			malloc_cprintf(write_cb, cbopaque,
124			    "\t\t\t\t\t\t\"nreslabs\": %"FMTu64",\n"
125			    "\t\t\t\t\t\t\"curslabs\": %zu\n"
126			    "\t\t\t\t\t}%s\n",
127			    nreslabs,
128			    curslabs,
129			    (j + 1 < nbins) ? "," : "");
130		} else if (!in_gap) {
131			size_t availregs, milli;
132			char util[6]; /* "x.yyy". */
133
134			availregs = nregs * curslabs;
135			milli = (availregs != 0) ? (1000 * curregs) / availregs
136			    : 1000;
137			assert(milli <= 1000);
138			if (milli < 10) {
139				malloc_snprintf(util, sizeof(util),
140				    "0.00%zu", milli);
141			} else if (milli < 100) {
142				malloc_snprintf(util, sizeof(util), "0.0%zu",
143				    milli);
144			} else if (milli < 1000) {
145				malloc_snprintf(util, sizeof(util), "0.%zu",
146				    milli);
147			} else
148				malloc_snprintf(util, sizeof(util), "1");
149
150			if (config_tcache) {
151				malloc_cprintf(write_cb, cbopaque,
152				    "%20zu %3u %12zu %12"FMTu64
153				    " %12"FMTu64" %12"FMTu64" %12zu"
154				    " %12zu %4u %3zu %-5s %12"FMTu64
155				    " %12"FMTu64" %12"FMTu64" %12"FMTu64"\n",
156				    reg_size, j, curregs * reg_size, nmalloc,
157				    ndalloc, nrequests, curregs, curslabs,
158				    nregs, slab_size / page, util, nfills,
159				    nflushes, nslabs, nreslabs);
160			} else {
161				malloc_cprintf(write_cb, cbopaque,
162				    "%20zu %3u %12zu %12"FMTu64
163				    " %12"FMTu64" %12"FMTu64" %12zu"
164				    " %12zu %4u %3zu %-5s %12"FMTu64
165				    " %12"FMTu64"\n",
166				    reg_size, j, curregs * reg_size, nmalloc,
167				    ndalloc, nrequests, curregs, curslabs,
168				    nregs, slab_size / page, util, nslabs,
169				    nreslabs);
170			}
171		}
172	}
173	if (json) {
174		malloc_cprintf(write_cb, cbopaque,
175		    "\t\t\t\t]%s\n", large ? "," : "");
176	} else {
177		if (in_gap) {
178			malloc_cprintf(write_cb, cbopaque,
179			    "                     ---\n");
180		}
181	}
182}
183
184static void
185stats_arena_lextents_print(void (*write_cb)(void *, const char *),
186    void *cbopaque, bool json, unsigned i)
187{
188	unsigned nbins, nlextents, j;
189	bool in_gap, in_gap_prev;
190
191	CTL_GET("arenas.nbins", &nbins, unsigned);
192	CTL_GET("arenas.nlextents", &nlextents, unsigned);
193	if (json) {
194		malloc_cprintf(write_cb, cbopaque,
195		    "\t\t\t\t\"lextents\": [\n");
196	} else {
197		malloc_cprintf(write_cb, cbopaque,
198		    "large:          size ind    allocated      nmalloc"
199		    "      ndalloc    nrequests  curlextents\n");
200	}
201	for (j = 0, in_gap = false; j < nlextents; j++) {
202		uint64_t nmalloc, ndalloc, nrequests;
203		size_t lextent_size, curlextents;
204
205		CTL_M2_M4_GET("stats.arenas.0.lextents.0.nmalloc", i, j,
206		    &nmalloc, uint64_t);
207		CTL_M2_M4_GET("stats.arenas.0.lextents.0.ndalloc", i, j,
208		    &ndalloc, uint64_t);
209		CTL_M2_M4_GET("stats.arenas.0.lextents.0.nrequests", i, j,
210		    &nrequests, uint64_t);
211		in_gap_prev = in_gap;
212		in_gap = (nrequests == 0);
213
214		if (!json && in_gap_prev && !in_gap) {
215			malloc_cprintf(write_cb, cbopaque,
216			    "                     ---\n");
217		}
218
219		CTL_M2_GET("arenas.lextent.0.size", j, &lextent_size, size_t);
220		CTL_M2_M4_GET("stats.arenas.0.lextents.0.curlextents", i, j,
221		    &curlextents, size_t);
222		if (json) {
223			malloc_cprintf(write_cb, cbopaque,
224			    "\t\t\t\t\t{\n"
225			    "\t\t\t\t\t\t\"curlextents\": %zu\n"
226			    "\t\t\t\t\t}%s\n",
227			    curlextents,
228			    (j + 1 < nlextents) ? "," : "");
229		} else if (!in_gap) {
230			malloc_cprintf(write_cb, cbopaque,
231			    "%20zu %3u %12zu %12"FMTu64" %12"FMTu64
232			    " %12"FMTu64" %12zu\n",
233			    lextent_size, nbins + j,
234			    curlextents * lextent_size, nmalloc, ndalloc,
235			    nrequests, curlextents);
236		}
237	}
238	if (json) {
239		malloc_cprintf(write_cb, cbopaque,
240		    "\t\t\t\t]\n");
241	} else {
242		if (in_gap) {
243			malloc_cprintf(write_cb, cbopaque,
244			    "                     ---\n");
245		}
246	}
247}
248
249static void
250stats_arena_print(void (*write_cb)(void *, const char *), void *cbopaque,
251    bool json, unsigned i, bool bins, bool large)
252{
253	unsigned nthreads;
254	const char *dss;
255	ssize_t decay_time;
256	size_t page, pactive, pdirty, mapped, retained;
257	size_t base, internal, resident;
258	uint64_t npurge, nmadvise, purged;
259	size_t small_allocated;
260	uint64_t small_nmalloc, small_ndalloc, small_nrequests;
261	size_t large_allocated;
262	uint64_t large_nmalloc, large_ndalloc, large_nrequests;
263	size_t tcache_bytes;
264
265	CTL_GET("arenas.page", &page, size_t);
266
267	CTL_M2_GET("stats.arenas.0.nthreads", i, &nthreads, unsigned);
268	if (json) {
269		malloc_cprintf(write_cb, cbopaque,
270		    "\t\t\t\t\"nthreads\": %u,\n", nthreads);
271	} else {
272		malloc_cprintf(write_cb, cbopaque,
273		    "assigned threads: %u\n", nthreads);
274	}
275
276	CTL_M2_GET("stats.arenas.0.dss", i, &dss, const char *);
277	if (json) {
278		malloc_cprintf(write_cb, cbopaque,
279		    "\t\t\t\t\"dss\": \"%s\",\n", dss);
280	} else {
281		malloc_cprintf(write_cb, cbopaque,
282		    "dss allocation precedence: %s\n", dss);
283	}
284
285	CTL_M2_GET("stats.arenas.0.decay_time", i, &decay_time, ssize_t);
286	if (json) {
287		malloc_cprintf(write_cb, cbopaque,
288		    "\t\t\t\t\"decay_time\": %zd,\n", decay_time);
289	} else {
290		if (decay_time >= 0) {
291			malloc_cprintf(write_cb, cbopaque, "decay time: %zd\n",
292			    decay_time);
293		} else
294			malloc_cprintf(write_cb, cbopaque, "decay time: N/A\n");
295	}
296
297	CTL_M2_GET("stats.arenas.0.pactive", i, &pactive, size_t);
298	CTL_M2_GET("stats.arenas.0.pdirty", i, &pdirty, size_t);
299	CTL_M2_GET("stats.arenas.0.npurge", i, &npurge, uint64_t);
300	CTL_M2_GET("stats.arenas.0.nmadvise", i, &nmadvise, uint64_t);
301	CTL_M2_GET("stats.arenas.0.purged", i, &purged, uint64_t);
302	if (json) {
303		malloc_cprintf(write_cb, cbopaque,
304		    "\t\t\t\t\"pactive\": %zu,\n", pactive);
305		malloc_cprintf(write_cb, cbopaque,
306		    "\t\t\t\t\"pdirty\": %zu,\n", pdirty);
307		malloc_cprintf(write_cb, cbopaque,
308		    "\t\t\t\t\"npurge\": %"FMTu64",\n", npurge);
309		malloc_cprintf(write_cb, cbopaque,
310		    "\t\t\t\t\"nmadvise\": %"FMTu64",\n", nmadvise);
311		malloc_cprintf(write_cb, cbopaque,
312		    "\t\t\t\t\"purged\": %"FMTu64",\n", purged);
313	} else {
314		malloc_cprintf(write_cb, cbopaque,
315		    "purging: dirty: %zu, sweeps: %"FMTu64", madvises: %"FMTu64
316		    ", purged: %"FMTu64"\n", pdirty, npurge, nmadvise, purged);
317	}
318
319	CTL_M2_GET("stats.arenas.0.small.allocated", i, &small_allocated,
320	    size_t);
321	CTL_M2_GET("stats.arenas.0.small.nmalloc", i, &small_nmalloc, uint64_t);
322	CTL_M2_GET("stats.arenas.0.small.ndalloc", i, &small_ndalloc, uint64_t);
323	CTL_M2_GET("stats.arenas.0.small.nrequests", i, &small_nrequests,
324	    uint64_t);
325	if (json) {
326		malloc_cprintf(write_cb, cbopaque,
327		    "\t\t\t\t\"small\": {\n");
328
329		malloc_cprintf(write_cb, cbopaque,
330		    "\t\t\t\t\t\"allocated\": %zu,\n", small_allocated);
331		malloc_cprintf(write_cb, cbopaque,
332		    "\t\t\t\t\t\"nmalloc\": %"FMTu64",\n", small_nmalloc);
333		malloc_cprintf(write_cb, cbopaque,
334		    "\t\t\t\t\t\"ndalloc\": %"FMTu64",\n", small_ndalloc);
335		malloc_cprintf(write_cb, cbopaque,
336		    "\t\t\t\t\t\"nrequests\": %"FMTu64"\n", small_nrequests);
337
338		malloc_cprintf(write_cb, cbopaque,
339		    "\t\t\t\t},\n");
340	} else {
341		malloc_cprintf(write_cb, cbopaque,
342		    "                            allocated      nmalloc"
343		    "      ndalloc    nrequests\n");
344		malloc_cprintf(write_cb, cbopaque,
345		    "small:                   %12zu %12"FMTu64" %12"FMTu64
346		    " %12"FMTu64"\n",
347		    small_allocated, small_nmalloc, small_ndalloc,
348		    small_nrequests);
349	}
350
351	CTL_M2_GET("stats.arenas.0.large.allocated", i, &large_allocated,
352	    size_t);
353	CTL_M2_GET("stats.arenas.0.large.nmalloc", i, &large_nmalloc, uint64_t);
354	CTL_M2_GET("stats.arenas.0.large.ndalloc", i, &large_ndalloc, uint64_t);
355	CTL_M2_GET("stats.arenas.0.large.nrequests", i, &large_nrequests,
356	    uint64_t);
357	if (json) {
358		malloc_cprintf(write_cb, cbopaque,
359		    "\t\t\t\t\"large\": {\n");
360
361		malloc_cprintf(write_cb, cbopaque,
362		    "\t\t\t\t\t\"allocated\": %zu,\n", large_allocated);
363		malloc_cprintf(write_cb, cbopaque,
364		    "\t\t\t\t\t\"nmalloc\": %"FMTu64",\n", large_nmalloc);
365		malloc_cprintf(write_cb, cbopaque,
366		    "\t\t\t\t\t\"ndalloc\": %"FMTu64",\n", large_ndalloc);
367		malloc_cprintf(write_cb, cbopaque,
368		    "\t\t\t\t\t\"nrequests\": %"FMTu64"\n", large_nrequests);
369
370		malloc_cprintf(write_cb, cbopaque,
371		    "\t\t\t\t},\n");
372	} else {
373		malloc_cprintf(write_cb, cbopaque,
374		    "large:                   %12zu %12"FMTu64" %12"FMTu64
375		    " %12"FMTu64"\n",
376		    large_allocated, large_nmalloc, large_ndalloc,
377		    large_nrequests);
378		malloc_cprintf(write_cb, cbopaque,
379		    "total:                   %12zu %12"FMTu64" %12"FMTu64
380		    " %12"FMTu64"\n",
381		    small_allocated + large_allocated, small_nmalloc +
382		    large_nmalloc, small_ndalloc + large_ndalloc,
383		    small_nrequests + large_nrequests);
384	}
385	if (!json) {
386		malloc_cprintf(write_cb, cbopaque,
387		    "active:                  %12zu\n", pactive * page);
388	}
389
390	CTL_M2_GET("stats.arenas.0.mapped", i, &mapped, size_t);
391	if (json) {
392		malloc_cprintf(write_cb, cbopaque,
393		    "\t\t\t\t\"mapped\": %zu,\n", mapped);
394	} else {
395		malloc_cprintf(write_cb, cbopaque,
396		    "mapped:                  %12zu\n", mapped);
397	}
398
399	CTL_M2_GET("stats.arenas.0.retained", i, &retained, size_t);
400	if (json) {
401		malloc_cprintf(write_cb, cbopaque,
402		    "\t\t\t\t\"retained\": %zu,\n", retained);
403	} else {
404		malloc_cprintf(write_cb, cbopaque,
405		    "retained:                %12zu\n", retained);
406	}
407
408	CTL_M2_GET("stats.arenas.0.base", i, &base, size_t);
409	if (json) {
410		malloc_cprintf(write_cb, cbopaque,
411		    "\t\t\t\t\"base\": %zu,\n", base);
412	} else {
413		malloc_cprintf(write_cb, cbopaque,
414		    "base:                    %12zu\n", base);
415	}
416
417	CTL_M2_GET("stats.arenas.0.internal", i, &internal, size_t);
418	if (json) {
419		malloc_cprintf(write_cb, cbopaque,
420		    "\t\t\t\t\"internal\": %zu,\n", internal);
421	} else {
422		malloc_cprintf(write_cb, cbopaque,
423		    "internal:                %12zu\n", internal);
424	}
425
426	if (config_tcache) {
427		CTL_M2_GET("stats.arenas.0.tcache_bytes", i, &tcache_bytes,
428		    size_t);
429		if (json) {
430			malloc_cprintf(write_cb, cbopaque,
431			    "\t\t\t\t\"tcache\": %zu,\n", tcache_bytes);
432		} else {
433			malloc_cprintf(write_cb, cbopaque,
434			    "tcache:                  %12zu\n", tcache_bytes);
435		}
436	}
437
438	CTL_M2_GET("stats.arenas.0.resident", i, &resident, size_t);
439	if (json) {
440		malloc_cprintf(write_cb, cbopaque,
441		    "\t\t\t\t\"resident\": %zu%s\n", resident, (bins || large) ?
442		    "," : "");
443	} else {
444		malloc_cprintf(write_cb, cbopaque,
445		    "resident:                %12zu\n", resident);
446	}
447
448	if (bins)
449		stats_arena_bins_print(write_cb, cbopaque, json, large, i);
450	if (large)
451		stats_arena_lextents_print(write_cb, cbopaque, json, i);
452}
453
454static void
455stats_general_print(void (*write_cb)(void *, const char *), void *cbopaque,
456    bool json, bool more)
457{
458	const char *cpv;
459	bool bv;
460	unsigned uv;
461	uint32_t u32v;
462	uint64_t u64v;
463	ssize_t ssv;
464	size_t sv, bsz, usz, ssz, sssz, cpsz;
465
466	bsz = sizeof(bool);
467	usz = sizeof(unsigned);
468	ssz = sizeof(size_t);
469	sssz = sizeof(ssize_t);
470	cpsz = sizeof(const char *);
471
472	CTL_GET("version", &cpv, const char *);
473	if (json) {
474		malloc_cprintf(write_cb, cbopaque,
475		"\t\t\"version\": \"%s\",\n", cpv);
476	} else
477		malloc_cprintf(write_cb, cbopaque, "Version: %s\n", cpv);
478
479	/* config. */
480#define	CONFIG_WRITE_BOOL_JSON(n, c)					\
481	if (json) {							\
482		CTL_GET("config."#n, &bv, bool);			\
483		malloc_cprintf(write_cb, cbopaque,			\
484		    "\t\t\t\""#n"\": %s%s\n", bv ? "true" : "false",	\
485		    (c));						\
486	}
487
488	if (json) {
489		malloc_cprintf(write_cb, cbopaque,
490		    "\t\t\"config\": {\n");
491	}
492
493	CONFIG_WRITE_BOOL_JSON(cache_oblivious, ",")
494
495	CTL_GET("config.debug", &bv, bool);
496	if (json) {
497		malloc_cprintf(write_cb, cbopaque,
498		    "\t\t\t\"debug\": %s,\n", bv ? "true" : "false");
499	} else {
500		malloc_cprintf(write_cb, cbopaque, "Assertions %s\n",
501		    bv ? "enabled" : "disabled");
502	}
503
504	CONFIG_WRITE_BOOL_JSON(fill, ",")
505	CONFIG_WRITE_BOOL_JSON(lazy_lock, ",")
506
507	if (json) {
508		malloc_cprintf(write_cb, cbopaque,
509		    "\t\t\t\"malloc_conf\": \"%s\",\n",
510		    config_malloc_conf);
511	} else {
512		malloc_cprintf(write_cb, cbopaque,
513		    "config.malloc_conf: \"%s\"\n", config_malloc_conf);
514	}
515
516	CONFIG_WRITE_BOOL_JSON(munmap, ",")
517	CONFIG_WRITE_BOOL_JSON(prof, ",")
518	CONFIG_WRITE_BOOL_JSON(prof_libgcc, ",")
519	CONFIG_WRITE_BOOL_JSON(prof_libunwind, ",")
520	CONFIG_WRITE_BOOL_JSON(stats, ",")
521	CONFIG_WRITE_BOOL_JSON(tcache, ",")
522	CONFIG_WRITE_BOOL_JSON(tls, ",")
523	CONFIG_WRITE_BOOL_JSON(utrace, ",")
524	CONFIG_WRITE_BOOL_JSON(xmalloc, "")
525
526	if (json) {
527		malloc_cprintf(write_cb, cbopaque,
528		    "\t\t},\n");
529	}
530#undef CONFIG_WRITE_BOOL_JSON
531
532	/* opt. */
533#define	OPT_WRITE_BOOL(n, c)						\
534	if (je_mallctl("opt."#n, (void *)&bv, &bsz, NULL, 0) == 0) {	\
535		if (json) {						\
536			malloc_cprintf(write_cb, cbopaque,		\
537			    "\t\t\t\""#n"\": %s%s\n", bv ? "true" :	\
538			    "false", (c));				\
539		} else {						\
540			malloc_cprintf(write_cb, cbopaque,		\
541			    "  opt."#n": %s\n", bv ? "true" : "false");	\
542		}							\
543	}
544#define	OPT_WRITE_BOOL_MUTABLE(n, m, c) {				\
545	bool bv2;							\
546	if (je_mallctl("opt."#n, (void *)&bv, &bsz, NULL, 0) == 0 &&	\
547	    je_mallctl(#m, (void *)&bv2, &bsz, NULL, 0) == 0) {		\
548		if (json) {						\
549			malloc_cprintf(write_cb, cbopaque,		\
550			    "\t\t\t\""#n"\": %s%s\n", bv ? "true" :	\
551			    "false", (c));				\
552		} else {						\
553			malloc_cprintf(write_cb, cbopaque,		\
554			    "  opt."#n": %s ("#m": %s)\n", bv ? "true"	\
555			    : "false", bv2 ? "true" : "false");		\
556		}							\
557	}								\
558}
559#define	OPT_WRITE_UNSIGNED(n, c)					\
560	if (je_mallctl("opt."#n, (void *)&uv, &usz, NULL, 0) == 0) {	\
561		if (json) {						\
562			malloc_cprintf(write_cb, cbopaque,		\
563			    "\t\t\t\""#n"\": %u%s\n", uv, (c));		\
564		} else {						\
565			malloc_cprintf(write_cb, cbopaque,		\
566			"  opt."#n": %u\n", uv);			\
567		}							\
568	}
569#define	OPT_WRITE_SSIZE_T(n, c)						\
570	if (je_mallctl("opt."#n, (void *)&ssv, &sssz, NULL, 0) == 0) {	\
571		if (json) {						\
572			malloc_cprintf(write_cb, cbopaque,		\
573			    "\t\t\t\""#n"\": %zd%s\n", ssv, (c));	\
574		} else {						\
575			malloc_cprintf(write_cb, cbopaque,		\
576			    "  opt."#n": %zd\n", ssv);			\
577		}							\
578	}
579#define	OPT_WRITE_SSIZE_T_MUTABLE(n, m, c) {				\
580	ssize_t ssv2;							\
581	if (je_mallctl("opt."#n, (void *)&ssv, &sssz, NULL, 0) == 0 &&	\
582	    je_mallctl(#m, (void *)&ssv2, &sssz, NULL, 0) == 0) {	\
583		if (json) {						\
584			malloc_cprintf(write_cb, cbopaque,		\
585			    "\t\t\t\""#n"\": %zd%s\n", ssv, (c));	\
586		} else {						\
587			malloc_cprintf(write_cb, cbopaque,		\
588			    "  opt."#n": %zd ("#m": %zd)\n",		\
589			    ssv, ssv2);					\
590		}							\
591	}								\
592}
593#define	OPT_WRITE_CHAR_P(n, c)						\
594	if (je_mallctl("opt."#n, (void *)&cpv, &cpsz, NULL, 0) == 0) {	\
595		if (json) {						\
596			malloc_cprintf(write_cb, cbopaque,		\
597			    "\t\t\t\""#n"\": \"%s\"%s\n", cpv, (c));	\
598		} else {						\
599			malloc_cprintf(write_cb, cbopaque,		\
600			    "  opt."#n": \"%s\"\n", cpv);		\
601		}							\
602	}
603
604	if (json) {
605		malloc_cprintf(write_cb, cbopaque,
606		    "\t\t\"opt\": {\n");
607	} else {
608		malloc_cprintf(write_cb, cbopaque,
609		    "Run-time option settings:\n");
610	}
611	OPT_WRITE_BOOL(abort, ",")
612	OPT_WRITE_CHAR_P(dss, ",")
613	OPT_WRITE_UNSIGNED(narenas, ",")
614	OPT_WRITE_SSIZE_T_MUTABLE(decay_time, arenas.decay_time, ",")
615	OPT_WRITE_CHAR_P(junk, ",")
616	OPT_WRITE_BOOL(zero, ",")
617	OPT_WRITE_BOOL(utrace, ",")
618	OPT_WRITE_BOOL(xmalloc, ",")
619	OPT_WRITE_BOOL(tcache, ",")
620	OPT_WRITE_SSIZE_T(lg_tcache_max, ",")
621	OPT_WRITE_BOOL(prof, ",")
622	OPT_WRITE_CHAR_P(prof_prefix, ",")
623	OPT_WRITE_BOOL_MUTABLE(prof_active, prof.active, ",")
624	OPT_WRITE_BOOL_MUTABLE(prof_thread_active_init, prof.thread_active_init,
625	    ",")
626	OPT_WRITE_SSIZE_T_MUTABLE(lg_prof_sample, prof.lg_sample, ",")
627	OPT_WRITE_BOOL(prof_accum, ",")
628	OPT_WRITE_SSIZE_T(lg_prof_interval, ",")
629	OPT_WRITE_BOOL(prof_gdump, ",")
630	OPT_WRITE_BOOL(prof_final, ",")
631	OPT_WRITE_BOOL(prof_leak, ",")
632	/*
633	 * stats_print is always emitted, so as long as stats_print comes last
634	 * it's safe to unconditionally omit the comma here (rather than having
635	 * to conditionally omit it elsewhere depending on configuration).
636	 */
637	OPT_WRITE_BOOL(stats_print, "")
638	if (json) {
639		malloc_cprintf(write_cb, cbopaque,
640		    "\t\t},\n");
641	}
642
643#undef OPT_WRITE_BOOL
644#undef OPT_WRITE_BOOL_MUTABLE
645#undef OPT_WRITE_SSIZE_T
646#undef OPT_WRITE_CHAR_P
647
648	/* arenas. */
649	if (json) {
650		malloc_cprintf(write_cb, cbopaque,
651		    "\t\t\"arenas\": {\n");
652	}
653
654	CTL_GET("arenas.narenas", &uv, unsigned);
655	if (json) {
656		malloc_cprintf(write_cb, cbopaque,
657		    "\t\t\t\"narenas\": %u,\n", uv);
658	} else
659		malloc_cprintf(write_cb, cbopaque, "Arenas: %u\n", uv);
660
661	CTL_GET("arenas.decay_time", &ssv, ssize_t);
662	if (json) {
663		malloc_cprintf(write_cb, cbopaque,
664		    "\t\t\t\"decay_time\": %zd,\n", ssv);
665	} else {
666		malloc_cprintf(write_cb, cbopaque,
667		    "Unused dirty page decay time: %zd%s\n", ssv, (ssv < 0) ?
668		    " (no decay)" : "");
669	}
670
671	CTL_GET("arenas.quantum", &sv, size_t);
672	if (json) {
673		malloc_cprintf(write_cb, cbopaque,
674		    "\t\t\t\"quantum\": %zu,\n", sv);
675	} else
676		malloc_cprintf(write_cb, cbopaque, "Quantum size: %zu\n", sv);
677
678	CTL_GET("arenas.page", &sv, size_t);
679	if (json) {
680		malloc_cprintf(write_cb, cbopaque,
681		    "\t\t\t\"page\": %zu,\n", sv);
682	} else
683		malloc_cprintf(write_cb, cbopaque, "Page size: %zu\n", sv);
684
685	if (je_mallctl("arenas.tcache_max", (void *)&sv, &ssz, NULL, 0) == 0) {
686		if (json) {
687			malloc_cprintf(write_cb, cbopaque,
688			    "\t\t\t\"tcache_max\": %zu,\n", sv);
689		} else {
690			malloc_cprintf(write_cb, cbopaque,
691			    "Maximum thread-cached size class: %zu\n", sv);
692		}
693	}
694
695	if (json) {
696		unsigned nbins, nlextents, i;
697
698		CTL_GET("arenas.nbins", &nbins, unsigned);
699		malloc_cprintf(write_cb, cbopaque,
700		    "\t\t\t\"nbins\": %u,\n", nbins);
701
702		if (config_tcache) {
703			CTL_GET("arenas.nhbins", &uv, unsigned);
704			malloc_cprintf(write_cb, cbopaque,
705			    "\t\t\t\"nhbins\": %u,\n", uv);
706		}
707
708		malloc_cprintf(write_cb, cbopaque,
709		    "\t\t\t\"bin\": [\n");
710		for (i = 0; i < nbins; i++) {
711			malloc_cprintf(write_cb, cbopaque,
712			    "\t\t\t\t{\n");
713
714			CTL_M2_GET("arenas.bin.0.size", i, &sv, size_t);
715			malloc_cprintf(write_cb, cbopaque,
716			    "\t\t\t\t\t\"size\": %zu,\n", sv);
717
718			CTL_M2_GET("arenas.bin.0.nregs", i, &u32v, uint32_t);
719			malloc_cprintf(write_cb, cbopaque,
720			    "\t\t\t\t\t\"nregs\": %"FMTu32",\n", u32v);
721
722			CTL_M2_GET("arenas.bin.0.slab_size", i, &sv, size_t);
723			malloc_cprintf(write_cb, cbopaque,
724			    "\t\t\t\t\t\"slab_size\": %zu\n", sv);
725
726			malloc_cprintf(write_cb, cbopaque,
727			    "\t\t\t\t}%s\n", (i + 1 < nbins) ? "," : "");
728		}
729		malloc_cprintf(write_cb, cbopaque,
730		    "\t\t\t],\n");
731
732		CTL_GET("arenas.nlextents", &nlextents, unsigned);
733		malloc_cprintf(write_cb, cbopaque,
734		    "\t\t\t\"nlextents\": %u,\n", nlextents);
735
736		malloc_cprintf(write_cb, cbopaque,
737		    "\t\t\t\"lextent\": [\n");
738		for (i = 0; i < nlextents; i++) {
739			malloc_cprintf(write_cb, cbopaque,
740			    "\t\t\t\t{\n");
741
742			CTL_M2_GET("arenas.lextent.0.size", i, &sv, size_t);
743			malloc_cprintf(write_cb, cbopaque,
744			    "\t\t\t\t\t\"size\": %zu\n", sv);
745
746			malloc_cprintf(write_cb, cbopaque,
747			    "\t\t\t\t}%s\n", (i + 1 < nlextents) ? "," : "");
748		}
749		malloc_cprintf(write_cb, cbopaque,
750		    "\t\t\t]\n");
751
752		malloc_cprintf(write_cb, cbopaque,
753		    "\t\t}%s\n", (config_prof || more) ? "," : "");
754	}
755
756	/* prof. */
757	if (config_prof && json) {
758		malloc_cprintf(write_cb, cbopaque,
759		    "\t\t\"prof\": {\n");
760
761		CTL_GET("prof.thread_active_init", &bv, bool);
762		malloc_cprintf(write_cb, cbopaque,
763		    "\t\t\t\"thread_active_init\": %s,\n", bv ? "true" :
764		    "false");
765
766		CTL_GET("prof.active", &bv, bool);
767		malloc_cprintf(write_cb, cbopaque,
768		    "\t\t\t\"active\": %s,\n", bv ? "true" : "false");
769
770		CTL_GET("prof.gdump", &bv, bool);
771		malloc_cprintf(write_cb, cbopaque,
772		    "\t\t\t\"gdump\": %s,\n", bv ? "true" : "false");
773
774		CTL_GET("prof.interval", &u64v, uint64_t);
775		malloc_cprintf(write_cb, cbopaque,
776		    "\t\t\t\"interval\": %"FMTu64",\n", u64v);
777
778		CTL_GET("prof.lg_sample", &ssv, ssize_t);
779		malloc_cprintf(write_cb, cbopaque,
780		    "\t\t\t\"lg_sample\": %zd\n", ssv);
781
782		malloc_cprintf(write_cb, cbopaque,
783		    "\t\t}%s\n", more ? "," : "");
784	}
785}
786
787static void
788stats_print_helper(void (*write_cb)(void *, const char *), void *cbopaque,
789    bool json, bool merged, bool destroyed, bool unmerged, bool bins,
790    bool large)
791{
792	size_t allocated, active, metadata, resident, mapped, retained;
793
794	CTL_GET("stats.allocated", &allocated, size_t);
795	CTL_GET("stats.active", &active, size_t);
796	CTL_GET("stats.metadata", &metadata, size_t);
797	CTL_GET("stats.resident", &resident, size_t);
798	CTL_GET("stats.mapped", &mapped, size_t);
799	CTL_GET("stats.retained", &retained, size_t);
800	if (json) {
801		malloc_cprintf(write_cb, cbopaque,
802		    "\t\t\"stats\": {\n");
803
804		malloc_cprintf(write_cb, cbopaque,
805		    "\t\t\t\"allocated\": %zu,\n", allocated);
806		malloc_cprintf(write_cb, cbopaque,
807		    "\t\t\t\"active\": %zu,\n", active);
808		malloc_cprintf(write_cb, cbopaque,
809		    "\t\t\t\"metadata\": %zu,\n", metadata);
810		malloc_cprintf(write_cb, cbopaque,
811		    "\t\t\t\"resident\": %zu,\n", resident);
812		malloc_cprintf(write_cb, cbopaque,
813		    "\t\t\t\"mapped\": %zu,\n", mapped);
814		malloc_cprintf(write_cb, cbopaque,
815		    "\t\t\t\"retained\": %zu\n", retained);
816
817		malloc_cprintf(write_cb, cbopaque,
818		    "\t\t}%s\n", (merged || unmerged) ? "," : "");
819	} else {
820		malloc_cprintf(write_cb, cbopaque,
821		    "Allocated: %zu, active: %zu, metadata: %zu,"
822		    " resident: %zu, mapped: %zu, retained: %zu\n",
823		    allocated, active, metadata, resident, mapped, retained);
824	}
825
826	if (merged || destroyed || unmerged) {
827		unsigned narenas;
828
829		if (json) {
830			malloc_cprintf(write_cb, cbopaque,
831			    "\t\t\"stats.arenas\": {\n");
832		}
833
834		CTL_GET("arenas.narenas", &narenas, unsigned);
835		{
836			size_t mib[3];
837			size_t miblen = sizeof(mib) / sizeof(size_t);
838			size_t sz;
839			VARIABLE_ARRAY(bool, initialized, narenas);
840			bool destroyed_initialized;
841			unsigned i, j, ninitialized;
842
843			xmallctlnametomib("arena.0.initialized", mib, &miblen);
844			for (i = ninitialized = 0; i < narenas; i++) {
845				mib[1] = i;
846				sz = sizeof(bool);
847				xmallctlbymib(mib, miblen, &initialized[i], &sz,
848				    NULL, 0);
849				if (initialized[i])
850					ninitialized++;
851			}
852			mib[1] = MALLCTL_ARENAS_DESTROYED;
853			sz = sizeof(bool);
854			xmallctlbymib(mib, miblen, &destroyed_initialized, &sz,
855			    NULL, 0);
856
857			/* Merged stats. */
858			if (merged && (ninitialized > 1 || !unmerged)) {
859				/* Print merged arena stats. */
860				if (json) {
861					malloc_cprintf(write_cb, cbopaque,
862					    "\t\t\t\"merged\": {\n");
863				} else {
864					malloc_cprintf(write_cb, cbopaque,
865					    "\nMerged arenas stats:\n");
866				}
867				stats_arena_print(write_cb, cbopaque, json,
868				    MALLCTL_ARENAS_ALL, bins, large);
869				if (json) {
870					malloc_cprintf(write_cb, cbopaque,
871					    "\t\t\t}%s\n",
872					    ((destroyed_initialized &&
873					    destroyed) || unmerged) ?  "," :
874					    "");
875				}
876			}
877
878			/* Destroyed stats. */
879			if (destroyed_initialized && destroyed) {
880				/* Print destroyed arena stats. */
881				if (json) {
882					malloc_cprintf(write_cb, cbopaque,
883					    "\t\t\t\"destroyed\": {\n");
884				} else {
885					malloc_cprintf(write_cb, cbopaque,
886					    "\nDestroyed arenas stats:\n");
887				}
888				stats_arena_print(write_cb, cbopaque, json,
889				    MALLCTL_ARENAS_DESTROYED, bins, large);
890				if (json) {
891					malloc_cprintf(write_cb, cbopaque,
892					    "\t\t\t}%s\n", unmerged ?  "," :
893					    "");
894				}
895			}
896
897			/* Unmerged stats. */
898			if (unmerged) {
899				for (i = j = 0; i < narenas; i++) {
900					if (initialized[i]) {
901						if (json) {
902							j++;
903							malloc_cprintf(write_cb,
904							    cbopaque,
905							    "\t\t\t\"%u\": {\n",
906							    i);
907						} else {
908							malloc_cprintf(write_cb,
909							    cbopaque,
910							    "\narenas[%u]:\n",
911							    i);
912						}
913						stats_arena_print(write_cb,
914						    cbopaque, json, i, bins,
915						    large);
916						if (json) {
917							malloc_cprintf(write_cb,
918							    cbopaque,
919							    "\t\t\t}%s\n", (j <
920							    ninitialized) ? ","
921							    : "");
922						}
923					}
924				}
925			}
926		}
927
928		if (json) {
929			malloc_cprintf(write_cb, cbopaque,
930			    "\t\t}\n");
931		}
932	}
933}
934
935void
936stats_print(void (*write_cb)(void *, const char *), void *cbopaque,
937    const char *opts)
938{
939	int err;
940	uint64_t epoch;
941	size_t u64sz;
942	bool json = false;
943	bool general = true;
944	bool merged = config_stats;
945	bool destroyed = config_stats;
946	bool unmerged = config_stats;
947	bool bins = true;
948	bool large = true;
949
950	/*
951	 * Refresh stats, in case mallctl() was called by the application.
952	 *
953	 * Check for OOM here, since refreshing the ctl cache can trigger
954	 * allocation.  In practice, none of the subsequent mallctl()-related
955	 * calls in this function will cause OOM if this one succeeds.
956	 * */
957	epoch = 1;
958	u64sz = sizeof(uint64_t);
959	err = je_mallctl("epoch", (void *)&epoch, &u64sz, (void *)&epoch,
960	    sizeof(uint64_t));
961	if (err != 0) {
962		if (err == EAGAIN) {
963			malloc_write("<jemalloc>: Memory allocation failure in "
964			    "mallctl(\"epoch\", ...)\n");
965			return;
966		}
967		malloc_write("<jemalloc>: Failure in mallctl(\"epoch\", "
968		    "...)\n");
969		abort();
970	}
971
972	if (opts != NULL) {
973		unsigned i;
974
975		for (i = 0; opts[i] != '\0'; i++) {
976			switch (opts[i]) {
977			case 'J':
978				json = true;
979				break;
980			case 'g':
981				general = false;
982				break;
983			case 'm':
984				merged = false;
985				break;
986			case 'd':
987				destroyed = false;
988				break;
989			case 'a':
990				unmerged = false;
991				break;
992			case 'b':
993				bins = false;
994				break;
995			case 'l':
996				large = false;
997				break;
998			default:;
999			}
1000		}
1001	}
1002
1003	if (json) {
1004		malloc_cprintf(write_cb, cbopaque,
1005		    "{\n"
1006		    "\t\"jemalloc\": {\n");
1007	} else {
1008		malloc_cprintf(write_cb, cbopaque,
1009		    "___ Begin jemalloc statistics ___\n");
1010	}
1011
1012	if (general) {
1013		bool more = (merged || unmerged);
1014		stats_general_print(write_cb, cbopaque, json, more);
1015	}
1016	if (config_stats) {
1017		stats_print_helper(write_cb, cbopaque, json, merged, destroyed,
1018		    unmerged, bins, large);
1019	}
1020
1021	if (json) {
1022		malloc_cprintf(write_cb, cbopaque,
1023		    "\t}\n"
1024		    "}\n");
1025	} else {
1026		malloc_cprintf(write_cb, cbopaque,
1027		    "--- End jemalloc statistics ---\n");
1028	}
1029}
1030