/src/cpython/Objects/mimalloc/stats.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* ---------------------------------------------------------------------------- |
2 | | Copyright (c) 2018-2021, Microsoft Research, Daan Leijen |
3 | | This is free software; you can redistribute it and/or modify it under the |
4 | | terms of the MIT license. A copy of the license can be found in the file |
5 | | "LICENSE" at the root of this distribution. |
6 | | -----------------------------------------------------------------------------*/ |
7 | | #include "mimalloc.h" |
8 | | #include "mimalloc/internal.h" |
9 | | #include "mimalloc/atomic.h" |
10 | | #include "mimalloc/prim.h" |
11 | | |
12 | | #include <stdio.h> // snprintf |
13 | | #include <string.h> // memset |
14 | | |
15 | | #if defined(_MSC_VER) && (_MSC_VER < 1920) |
16 | | #pragma warning(disable:4204) // non-constant aggregate initializer |
17 | | #endif |
18 | | |
19 | | /* ----------------------------------------------------------- |
20 | | Statistics operations |
21 | | ----------------------------------------------------------- */ |
22 | | |
23 | 0 | static bool mi_is_in_main(void* stat) { |
24 | 0 | return ((uint8_t*)stat >= (uint8_t*)&_mi_stats_main |
25 | 0 | && (uint8_t*)stat < ((uint8_t*)&_mi_stats_main + sizeof(mi_stats_t))); |
26 | 0 | } |
27 | | |
28 | 0 | static void mi_stat_update(mi_stat_count_t* stat, int64_t amount) { |
29 | 0 | if (amount == 0) return; |
30 | 0 | if (mi_is_in_main(stat)) |
31 | 0 | { |
32 | | // add atomically (for abandoned pages) |
33 | 0 | int64_t current = mi_atomic_addi64_relaxed(&stat->current, amount); |
34 | 0 | mi_atomic_maxi64_relaxed(&stat->peak, current + amount); |
35 | 0 | if (amount > 0) { |
36 | 0 | mi_atomic_addi64_relaxed(&stat->allocated,amount); |
37 | 0 | } |
38 | 0 | else { |
39 | 0 | mi_atomic_addi64_relaxed(&stat->freed, -amount); |
40 | 0 | } |
41 | 0 | } |
42 | 0 | else { |
43 | | // add thread local |
44 | 0 | stat->current += amount; |
45 | 0 | if (stat->current > stat->peak) stat->peak = stat->current; |
46 | 0 | if (amount > 0) { |
47 | 0 | stat->allocated += amount; |
48 | 0 | } |
49 | 0 | else { |
50 | 0 | stat->freed += -amount; |
51 | 0 | } |
52 | 0 | } |
53 | 0 | } |
54 | | |
55 | 0 | void _mi_stat_counter_increase(mi_stat_counter_t* stat, size_t amount) { |
56 | 0 | if (mi_is_in_main(stat)) { |
57 | 0 | mi_atomic_addi64_relaxed( &stat->count, 1 ); |
58 | 0 | mi_atomic_addi64_relaxed( &stat->total, (int64_t)amount ); |
59 | 0 | } |
60 | 0 | else { |
61 | 0 | stat->count++; |
62 | 0 | stat->total += amount; |
63 | 0 | } |
64 | 0 | } |
65 | | |
66 | 0 | void _mi_stat_increase(mi_stat_count_t* stat, size_t amount) { |
67 | 0 | mi_stat_update(stat, (int64_t)amount); |
68 | 0 | } |
69 | | |
70 | 0 | void _mi_stat_decrease(mi_stat_count_t* stat, size_t amount) { |
71 | 0 | mi_stat_update(stat, -((int64_t)amount)); |
72 | 0 | } |
73 | | |
74 | | // must be thread safe as it is called from stats_merge |
75 | 0 | static void mi_stat_add(mi_stat_count_t* stat, const mi_stat_count_t* src, int64_t unit) { |
76 | 0 | if (stat==src) return; |
77 | 0 | if (src->allocated==0 && src->freed==0) return; |
78 | 0 | mi_atomic_addi64_relaxed( &stat->allocated, src->allocated * unit); |
79 | 0 | mi_atomic_addi64_relaxed( &stat->current, src->current * unit); |
80 | 0 | mi_atomic_addi64_relaxed( &stat->freed, src->freed * unit); |
81 | | // peak scores do not work across threads.. |
82 | 0 | mi_atomic_addi64_relaxed( &stat->peak, src->peak * unit); |
83 | 0 | } |
84 | | |
85 | 0 | static void mi_stat_counter_add(mi_stat_counter_t* stat, const mi_stat_counter_t* src, int64_t unit) { |
86 | 0 | if (stat==src) return; |
87 | 0 | mi_atomic_addi64_relaxed( &stat->total, src->total * unit); |
88 | 0 | mi_atomic_addi64_relaxed( &stat->count, src->count * unit); |
89 | 0 | } |
90 | | |
91 | | // must be thread safe as it is called from stats_merge |
92 | 0 | static void mi_stats_add(mi_stats_t* stats, const mi_stats_t* src) { |
93 | 0 | if (stats==src) return; |
94 | 0 | mi_stat_add(&stats->segments, &src->segments,1); |
95 | 0 | mi_stat_add(&stats->pages, &src->pages,1); |
96 | 0 | mi_stat_add(&stats->reserved, &src->reserved, 1); |
97 | 0 | mi_stat_add(&stats->committed, &src->committed, 1); |
98 | 0 | mi_stat_add(&stats->reset, &src->reset, 1); |
99 | 0 | mi_stat_add(&stats->purged, &src->purged, 1); |
100 | 0 | mi_stat_add(&stats->page_committed, &src->page_committed, 1); |
101 | |
|
102 | 0 | mi_stat_add(&stats->pages_abandoned, &src->pages_abandoned, 1); |
103 | 0 | mi_stat_add(&stats->segments_abandoned, &src->segments_abandoned, 1); |
104 | 0 | mi_stat_add(&stats->threads, &src->threads, 1); |
105 | |
|
106 | 0 | mi_stat_add(&stats->malloc, &src->malloc, 1); |
107 | 0 | mi_stat_add(&stats->segments_cache, &src->segments_cache, 1); |
108 | 0 | mi_stat_add(&stats->normal, &src->normal, 1); |
109 | 0 | mi_stat_add(&stats->huge, &src->huge, 1); |
110 | 0 | mi_stat_add(&stats->large, &src->large, 1); |
111 | |
|
112 | 0 | mi_stat_counter_add(&stats->pages_extended, &src->pages_extended, 1); |
113 | 0 | mi_stat_counter_add(&stats->mmap_calls, &src->mmap_calls, 1); |
114 | 0 | mi_stat_counter_add(&stats->commit_calls, &src->commit_calls, 1); |
115 | 0 | mi_stat_counter_add(&stats->reset_calls, &src->reset_calls, 1); |
116 | 0 | mi_stat_counter_add(&stats->purge_calls, &src->purge_calls, 1); |
117 | |
|
118 | 0 | mi_stat_counter_add(&stats->page_no_retire, &src->page_no_retire, 1); |
119 | 0 | mi_stat_counter_add(&stats->searches, &src->searches, 1); |
120 | 0 | mi_stat_counter_add(&stats->normal_count, &src->normal_count, 1); |
121 | 0 | mi_stat_counter_add(&stats->huge_count, &src->huge_count, 1); |
122 | 0 | mi_stat_counter_add(&stats->large_count, &src->large_count, 1); |
123 | | #if MI_STAT>1 |
124 | | for (size_t i = 0; i <= MI_BIN_HUGE; i++) { |
125 | | if (src->normal_bins[i].allocated > 0 || src->normal_bins[i].freed > 0) { |
126 | | mi_stat_add(&stats->normal_bins[i], &src->normal_bins[i], 1); |
127 | | } |
128 | | } |
129 | | #endif |
130 | 0 | } |
131 | | |
132 | | /* ----------------------------------------------------------- |
133 | | Display statistics |
134 | | ----------------------------------------------------------- */ |
135 | | |
136 | | // unit > 0 : size in binary bytes |
137 | | // unit == 0: count as decimal |
138 | | // unit < 0 : count in binary |
139 | 0 | static void mi_printf_amount(int64_t n, int64_t unit, mi_output_fun* out, void* arg, const char* fmt) { |
140 | 0 | char buf[32]; buf[0] = 0; |
141 | 0 | int len = 32; |
142 | 0 | const char* suffix = (unit <= 0 ? " " : "B"); |
143 | 0 | const int64_t base = (unit == 0 ? 1000 : 1024); |
144 | 0 | if (unit>0) n *= unit; |
145 | |
|
146 | 0 | const int64_t pos = (n < 0 ? -n : n); |
147 | 0 | if (pos < base) { |
148 | 0 | if (n!=1 || suffix[0] != 'B') { // skip printing 1 B for the unit column |
149 | 0 | snprintf(buf, len, "%d %-3s", (int)n, (n==0 ? "" : suffix)); |
150 | 0 | } |
151 | 0 | } |
152 | 0 | else { |
153 | 0 | int64_t divider = base; |
154 | 0 | const char* magnitude = "K"; |
155 | 0 | if (pos >= divider*base) { divider *= base; magnitude = "M"; } |
156 | 0 | if (pos >= divider*base) { divider *= base; magnitude = "G"; } |
157 | 0 | const int64_t tens = (n / (divider/10)); |
158 | 0 | const long whole = (long)(tens/10); |
159 | 0 | const long frac1 = (long)(tens%10); |
160 | 0 | char unitdesc[8]; |
161 | 0 | snprintf(unitdesc, 8, "%s%s%s", magnitude, (base==1024 ? "i" : ""), suffix); |
162 | 0 | snprintf(buf, len, "%ld.%ld %-3s", whole, (frac1 < 0 ? -frac1 : frac1), unitdesc); |
163 | 0 | } |
164 | 0 | _mi_fprintf(out, arg, (fmt==NULL ? "%12s" : fmt), buf); |
165 | 0 | } |
166 | | |
167 | | |
168 | 0 | static void mi_print_amount(int64_t n, int64_t unit, mi_output_fun* out, void* arg) { |
169 | 0 | mi_printf_amount(n,unit,out,arg,NULL); |
170 | 0 | } |
171 | | |
172 | 0 | static void mi_print_count(int64_t n, int64_t unit, mi_output_fun* out, void* arg) { |
173 | 0 | if (unit==1) _mi_fprintf(out, arg, "%12s"," "); |
174 | 0 | else mi_print_amount(n,0,out,arg); |
175 | 0 | } |
176 | | |
177 | 0 | static void mi_stat_print_ex(const mi_stat_count_t* stat, const char* msg, int64_t unit, mi_output_fun* out, void* arg, const char* notok ) { |
178 | 0 | _mi_fprintf(out, arg,"%10s:", msg); |
179 | 0 | if (unit > 0) { |
180 | 0 | mi_print_amount(stat->peak, unit, out, arg); |
181 | 0 | mi_print_amount(stat->allocated, unit, out, arg); |
182 | 0 | mi_print_amount(stat->freed, unit, out, arg); |
183 | 0 | mi_print_amount(stat->current, unit, out, arg); |
184 | 0 | mi_print_amount(unit, 1, out, arg); |
185 | 0 | mi_print_count(stat->allocated, unit, out, arg); |
186 | 0 | if (stat->allocated > stat->freed) { |
187 | 0 | _mi_fprintf(out, arg, " "); |
188 | 0 | _mi_fprintf(out, arg, (notok == NULL ? "not all freed" : notok)); |
189 | 0 | _mi_fprintf(out, arg, "\n"); |
190 | 0 | } |
191 | 0 | else { |
192 | 0 | _mi_fprintf(out, arg, " ok\n"); |
193 | 0 | } |
194 | 0 | } |
195 | 0 | else if (unit<0) { |
196 | 0 | mi_print_amount(stat->peak, -1, out, arg); |
197 | 0 | mi_print_amount(stat->allocated, -1, out, arg); |
198 | 0 | mi_print_amount(stat->freed, -1, out, arg); |
199 | 0 | mi_print_amount(stat->current, -1, out, arg); |
200 | 0 | if (unit==-1) { |
201 | 0 | _mi_fprintf(out, arg, "%24s", ""); |
202 | 0 | } |
203 | 0 | else { |
204 | 0 | mi_print_amount(-unit, 1, out, arg); |
205 | 0 | mi_print_count((stat->allocated / -unit), 0, out, arg); |
206 | 0 | } |
207 | 0 | if (stat->allocated > stat->freed) |
208 | 0 | _mi_fprintf(out, arg, " not all freed!\n"); |
209 | 0 | else |
210 | 0 | _mi_fprintf(out, arg, " ok\n"); |
211 | 0 | } |
212 | 0 | else { |
213 | 0 | mi_print_amount(stat->peak, 1, out, arg); |
214 | 0 | mi_print_amount(stat->allocated, 1, out, arg); |
215 | 0 | _mi_fprintf(out, arg, "%11s", " "); // no freed |
216 | 0 | mi_print_amount(stat->current, 1, out, arg); |
217 | 0 | _mi_fprintf(out, arg, "\n"); |
218 | 0 | } |
219 | 0 | } |
220 | | |
221 | 0 | static void mi_stat_print(const mi_stat_count_t* stat, const char* msg, int64_t unit, mi_output_fun* out, void* arg) { |
222 | 0 | mi_stat_print_ex(stat, msg, unit, out, arg, NULL); |
223 | 0 | } |
224 | | |
225 | 0 | static void mi_stat_peak_print(const mi_stat_count_t* stat, const char* msg, int64_t unit, mi_output_fun* out, void* arg) { |
226 | 0 | _mi_fprintf(out, arg, "%10s:", msg); |
227 | 0 | mi_print_amount(stat->peak, unit, out, arg); |
228 | 0 | _mi_fprintf(out, arg, "\n"); |
229 | 0 | } |
230 | | |
231 | 0 | static void mi_stat_counter_print(const mi_stat_counter_t* stat, const char* msg, mi_output_fun* out, void* arg ) { |
232 | 0 | _mi_fprintf(out, arg, "%10s:", msg); |
233 | 0 | mi_print_amount(stat->total, -1, out, arg); |
234 | 0 | _mi_fprintf(out, arg, "\n"); |
235 | 0 | } |
236 | | |
237 | | |
238 | 0 | static void mi_stat_counter_print_avg(const mi_stat_counter_t* stat, const char* msg, mi_output_fun* out, void* arg) { |
239 | 0 | const int64_t avg_tens = (stat->count == 0 ? 0 : (stat->total*10 / stat->count)); |
240 | 0 | const long avg_whole = (long)(avg_tens/10); |
241 | 0 | const long avg_frac1 = (long)(avg_tens%10); |
242 | 0 | _mi_fprintf(out, arg, "%10s: %5ld.%ld avg\n", msg, avg_whole, avg_frac1); |
243 | 0 | } |
244 | | |
245 | | |
246 | 0 | static void mi_print_header(mi_output_fun* out, void* arg ) { |
247 | 0 | _mi_fprintf(out, arg, "%10s: %11s %11s %11s %11s %11s %11s\n", "heap stats", "peak ", "total ", "freed ", "current ", "unit ", "count "); |
248 | 0 | } |
249 | | |
250 | | #if MI_STAT>1 |
251 | | static void mi_stats_print_bins(const mi_stat_count_t* bins, size_t max, const char* fmt, mi_output_fun* out, void* arg) { |
252 | | bool found = false; |
253 | | char buf[64]; |
254 | | for (size_t i = 0; i <= max; i++) { |
255 | | if (bins[i].allocated > 0) { |
256 | | found = true; |
257 | | int64_t unit = _mi_bin_size((uint8_t)i); |
258 | | snprintf(buf, 64, "%s %3lu", fmt, (long)i); |
259 | | mi_stat_print(&bins[i], buf, unit, out, arg); |
260 | | } |
261 | | } |
262 | | if (found) { |
263 | | _mi_fprintf(out, arg, "\n"); |
264 | | mi_print_header(out, arg); |
265 | | } |
266 | | } |
267 | | #endif |
268 | | |
269 | | |
270 | | |
271 | | //------------------------------------------------------------ |
272 | | // Use an output wrapper for line-buffered output |
273 | | // (which is nice when using loggers etc.) |
274 | | //------------------------------------------------------------ |
275 | | typedef struct buffered_s { |
276 | | mi_output_fun* out; // original output function |
277 | | void* arg; // and state |
278 | | char* buf; // local buffer of at least size `count+1` |
279 | | size_t used; // currently used chars `used <= count` |
280 | | size_t count; // total chars available for output |
281 | | } buffered_t; |
282 | | |
283 | 0 | static void mi_buffered_flush(buffered_t* buf) { |
284 | 0 | buf->buf[buf->used] = 0; |
285 | 0 | _mi_fputs(buf->out, buf->arg, NULL, buf->buf); |
286 | 0 | buf->used = 0; |
287 | 0 | } |
288 | | |
289 | 0 | static void mi_cdecl mi_buffered_out(const char* msg, void* arg) { |
290 | 0 | buffered_t* buf = (buffered_t*)arg; |
291 | 0 | if (msg==NULL || buf==NULL) return; |
292 | 0 | for (const char* src = msg; *src != 0; src++) { |
293 | 0 | char c = *src; |
294 | 0 | if (buf->used >= buf->count) mi_buffered_flush(buf); |
295 | 0 | mi_assert_internal(buf->used < buf->count); |
296 | 0 | buf->buf[buf->used++] = c; |
297 | 0 | if (c == '\n') mi_buffered_flush(buf); |
298 | 0 | } |
299 | 0 | } |
300 | | |
301 | | //------------------------------------------------------------ |
302 | | // Print statistics |
303 | | //------------------------------------------------------------ |
304 | | |
305 | 0 | static void _mi_stats_print(mi_stats_t* stats, mi_output_fun* out0, void* arg0) mi_attr_noexcept { |
306 | | // wrap the output function to be line buffered |
307 | 0 | char buf[256]; |
308 | 0 | buffered_t buffer = { out0, arg0, NULL, 0, 255 }; |
309 | 0 | buffer.buf = buf; |
310 | 0 | mi_output_fun* out = &mi_buffered_out; |
311 | 0 | void* arg = &buffer; |
312 | | |
313 | | // and print using that |
314 | 0 | mi_print_header(out,arg); |
315 | | #if MI_STAT>1 |
316 | | mi_stats_print_bins(stats->normal_bins, MI_BIN_HUGE, "normal",out,arg); |
317 | | #endif |
318 | | #if MI_STAT |
319 | | mi_stat_print(&stats->normal, "normal", (stats->normal_count.count == 0 ? 1 : -(stats->normal.allocated / stats->normal_count.count)), out, arg); |
320 | | mi_stat_print(&stats->large, "large", (stats->large_count.count == 0 ? 1 : -(stats->large.allocated / stats->large_count.count)), out, arg); |
321 | | mi_stat_print(&stats->huge, "huge", (stats->huge_count.count == 0 ? 1 : -(stats->huge.allocated / stats->huge_count.count)), out, arg); |
322 | | mi_stat_count_t total = { 0,0,0,0 }; |
323 | | mi_stat_add(&total, &stats->normal, 1); |
324 | | mi_stat_add(&total, &stats->large, 1); |
325 | | mi_stat_add(&total, &stats->huge, 1); |
326 | | mi_stat_print(&total, "total", 1, out, arg); |
327 | | #endif |
328 | | #if MI_STAT>1 |
329 | | mi_stat_print(&stats->malloc, "malloc req", 1, out, arg); |
330 | | _mi_fprintf(out, arg, "\n"); |
331 | | #endif |
332 | 0 | mi_stat_print_ex(&stats->reserved, "reserved", 1, out, arg, ""); |
333 | 0 | mi_stat_print_ex(&stats->committed, "committed", 1, out, arg, ""); |
334 | 0 | mi_stat_peak_print(&stats->reset, "reset", 1, out, arg ); |
335 | 0 | mi_stat_peak_print(&stats->purged, "purged", 1, out, arg ); |
336 | 0 | mi_stat_print(&stats->page_committed, "touched", 1, out, arg); |
337 | 0 | mi_stat_print(&stats->segments, "segments", -1, out, arg); |
338 | 0 | mi_stat_print(&stats->segments_abandoned, "-abandoned", -1, out, arg); |
339 | 0 | mi_stat_print(&stats->segments_cache, "-cached", -1, out, arg); |
340 | 0 | mi_stat_print(&stats->pages, "pages", -1, out, arg); |
341 | 0 | mi_stat_print(&stats->pages_abandoned, "-abandoned", -1, out, arg); |
342 | 0 | mi_stat_counter_print(&stats->pages_extended, "-extended", out, arg); |
343 | 0 | mi_stat_counter_print(&stats->page_no_retire, "-noretire", out, arg); |
344 | 0 | mi_stat_counter_print(&stats->mmap_calls, "mmaps", out, arg); |
345 | 0 | mi_stat_counter_print(&stats->commit_calls, "commits", out, arg); |
346 | 0 | mi_stat_counter_print(&stats->reset_calls, "resets", out, arg); |
347 | 0 | mi_stat_counter_print(&stats->purge_calls, "purges", out, arg); |
348 | 0 | mi_stat_print(&stats->threads, "threads", -1, out, arg); |
349 | 0 | mi_stat_counter_print_avg(&stats->searches, "searches", out, arg); |
350 | 0 | _mi_fprintf(out, arg, "%10s: %5zu\n", "numa nodes", _mi_os_numa_node_count()); |
351 | |
|
352 | 0 | size_t elapsed; |
353 | 0 | size_t user_time; |
354 | 0 | size_t sys_time; |
355 | 0 | size_t current_rss; |
356 | 0 | size_t peak_rss; |
357 | 0 | size_t current_commit; |
358 | 0 | size_t peak_commit; |
359 | 0 | size_t page_faults; |
360 | 0 | mi_process_info(&elapsed, &user_time, &sys_time, ¤t_rss, &peak_rss, ¤t_commit, &peak_commit, &page_faults); |
361 | 0 | _mi_fprintf(out, arg, "%10s: %5ld.%03ld s\n", "elapsed", elapsed/1000, elapsed%1000); |
362 | 0 | _mi_fprintf(out, arg, "%10s: user: %ld.%03ld s, system: %ld.%03ld s, faults: %lu, rss: ", "process", |
363 | 0 | user_time/1000, user_time%1000, sys_time/1000, sys_time%1000, (unsigned long)page_faults ); |
364 | 0 | mi_printf_amount((int64_t)peak_rss, 1, out, arg, "%s"); |
365 | 0 | if (peak_commit > 0) { |
366 | 0 | _mi_fprintf(out, arg, ", commit: "); |
367 | 0 | mi_printf_amount((int64_t)peak_commit, 1, out, arg, "%s"); |
368 | 0 | } |
369 | 0 | _mi_fprintf(out, arg, "\n"); |
370 | 0 | } |
371 | | |
372 | | static mi_msecs_t mi_process_start; // = 0 |
373 | | |
374 | 16 | static mi_stats_t* mi_stats_get_default(void) { |
375 | 16 | mi_heap_t* heap = mi_heap_get_default(); |
376 | 16 | return &heap->tld->stats; |
377 | 16 | } |
378 | | |
379 | 0 | static void mi_stats_merge_from(mi_stats_t* stats) { |
380 | 0 | if (stats != &_mi_stats_main) { |
381 | 0 | mi_stats_add(&_mi_stats_main, stats); |
382 | 0 | memset(stats, 0, sizeof(mi_stats_t)); |
383 | 0 | } |
384 | 0 | } |
385 | | |
386 | 16 | void mi_stats_reset(void) mi_attr_noexcept { |
387 | 16 | mi_stats_t* stats = mi_stats_get_default(); |
388 | 16 | if (stats != &_mi_stats_main) { memset(stats, 0, sizeof(mi_stats_t)); } |
389 | 16 | memset(&_mi_stats_main, 0, sizeof(mi_stats_t)); |
390 | 16 | if (mi_process_start == 0) { mi_process_start = _mi_clock_start(); }; |
391 | 16 | } |
392 | | |
393 | 0 | void mi_stats_merge(void) mi_attr_noexcept { |
394 | 0 | mi_stats_merge_from( mi_stats_get_default() ); |
395 | 0 | } |
396 | | |
397 | 0 | void _mi_stats_done(mi_stats_t* stats) { // called from `mi_thread_done` |
398 | 0 | mi_stats_merge_from(stats); |
399 | 0 | } |
400 | | |
401 | 0 | void mi_stats_print_out(mi_output_fun* out, void* arg) mi_attr_noexcept { |
402 | 0 | mi_stats_merge_from(mi_stats_get_default()); |
403 | 0 | _mi_stats_print(&_mi_stats_main, out, arg); |
404 | 0 | } |
405 | | |
406 | 0 | void mi_stats_print(void* out) mi_attr_noexcept { |
407 | | // for compatibility there is an `out` parameter (which can be `stdout` or `stderr`) |
408 | 0 | mi_stats_print_out((mi_output_fun*)out, NULL); |
409 | 0 | } |
410 | | |
411 | 0 | void mi_thread_stats_print_out(mi_output_fun* out, void* arg) mi_attr_noexcept { |
412 | 0 | _mi_stats_print(mi_stats_get_default(), out, arg); |
413 | 0 | } |
414 | | |
415 | | |
416 | | // ---------------------------------------------------------------- |
417 | | // Basic timer for convenience; use milli-seconds to avoid doubles |
418 | | // ---------------------------------------------------------------- |
419 | | |
420 | | static mi_msecs_t mi_clock_diff; |
421 | | |
422 | 48 | mi_msecs_t _mi_clock_now(void) { |
423 | 48 | return _mi_prim_clock_now(); |
424 | 48 | } |
425 | | |
426 | 16 | mi_msecs_t _mi_clock_start(void) { |
427 | 16 | if (mi_clock_diff == 0.0) { |
428 | 16 | mi_msecs_t t0 = _mi_clock_now(); |
429 | 16 | mi_clock_diff = _mi_clock_now() - t0; |
430 | 16 | } |
431 | 16 | return _mi_clock_now(); |
432 | 16 | } |
433 | | |
434 | 0 | mi_msecs_t _mi_clock_end(mi_msecs_t start) { |
435 | 0 | mi_msecs_t end = _mi_clock_now(); |
436 | 0 | return (end - start - mi_clock_diff); |
437 | 0 | } |
438 | | |
439 | | |
440 | | // -------------------------------------------------------- |
441 | | // Basic process statistics |
442 | | // -------------------------------------------------------- |
443 | | |
444 | | mi_decl_export void mi_process_info(size_t* elapsed_msecs, size_t* user_msecs, size_t* system_msecs, size_t* current_rss, size_t* peak_rss, size_t* current_commit, size_t* peak_commit, size_t* page_faults) mi_attr_noexcept |
445 | 0 | { |
446 | 0 | mi_process_info_t pinfo; |
447 | 0 | _mi_memzero_var(pinfo); |
448 | 0 | pinfo.elapsed = _mi_clock_end(mi_process_start); |
449 | 0 | pinfo.current_commit = (size_t)(mi_atomic_loadi64_relaxed((_Atomic(int64_t)*)&_mi_stats_main.committed.current)); |
450 | 0 | pinfo.peak_commit = (size_t)(mi_atomic_loadi64_relaxed((_Atomic(int64_t)*)&_mi_stats_main.committed.peak)); |
451 | 0 | pinfo.current_rss = pinfo.current_commit; |
452 | 0 | pinfo.peak_rss = pinfo.peak_commit; |
453 | 0 | pinfo.utime = 0; |
454 | 0 | pinfo.stime = 0; |
455 | 0 | pinfo.page_faults = 0; |
456 | |
|
457 | 0 | _mi_prim_process_info(&pinfo); |
458 | |
|
459 | 0 | if (elapsed_msecs!=NULL) *elapsed_msecs = (pinfo.elapsed < 0 ? 0 : (pinfo.elapsed < (mi_msecs_t)PTRDIFF_MAX ? (size_t)pinfo.elapsed : PTRDIFF_MAX)); |
460 | 0 | if (user_msecs!=NULL) *user_msecs = (pinfo.utime < 0 ? 0 : (pinfo.utime < (mi_msecs_t)PTRDIFF_MAX ? (size_t)pinfo.utime : PTRDIFF_MAX)); |
461 | 0 | if (system_msecs!=NULL) *system_msecs = (pinfo.stime < 0 ? 0 : (pinfo.stime < (mi_msecs_t)PTRDIFF_MAX ? (size_t)pinfo.stime : PTRDIFF_MAX)); |
462 | 0 | if (current_rss!=NULL) *current_rss = pinfo.current_rss; |
463 | 0 | if (peak_rss!=NULL) *peak_rss = pinfo.peak_rss; |
464 | 0 | if (current_commit!=NULL) *current_commit = pinfo.current_commit; |
465 | 0 | if (peak_commit!=NULL) *peak_commit = pinfo.peak_commit; |
466 | 0 | if (page_faults!=NULL) *page_faults = pinfo.page_faults; |
467 | 0 | } |