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
mi_is_in_main(void * stat)23 static bool mi_is_in_main(void* stat) {
24 return ((uint8_t*)stat >= (uint8_t*)&_mi_stats_main
25 && (uint8_t*)stat < ((uint8_t*)&_mi_stats_main + sizeof(mi_stats_t)));
26 }
27
mi_stat_update(mi_stat_count_t * stat,int64_t amount)28 static void mi_stat_update(mi_stat_count_t* stat, int64_t amount) {
29 if (amount == 0) return;
30 if (mi_is_in_main(stat))
31 {
32 // add atomically (for abandoned pages)
33 int64_t current = mi_atomic_addi64_relaxed(&stat->current, amount);
34 mi_atomic_maxi64_relaxed(&stat->peak, current + amount);
35 if (amount > 0) {
36 mi_atomic_addi64_relaxed(&stat->allocated,amount);
37 }
38 else {
39 mi_atomic_addi64_relaxed(&stat->freed, -amount);
40 }
41 }
42 else {
43 // add thread local
44 stat->current += amount;
45 if (stat->current > stat->peak) stat->peak = stat->current;
46 if (amount > 0) {
47 stat->allocated += amount;
48 }
49 else {
50 stat->freed += -amount;
51 }
52 }
53 }
54
_mi_stat_counter_increase(mi_stat_counter_t * stat,size_t amount)55 void _mi_stat_counter_increase(mi_stat_counter_t* stat, size_t amount) {
56 if (mi_is_in_main(stat)) {
57 mi_atomic_addi64_relaxed( &stat->count, 1 );
58 mi_atomic_addi64_relaxed( &stat->total, (int64_t)amount );
59 }
60 else {
61 stat->count++;
62 stat->total += amount;
63 }
64 }
65
_mi_stat_increase(mi_stat_count_t * stat,size_t amount)66 void _mi_stat_increase(mi_stat_count_t* stat, size_t amount) {
67 mi_stat_update(stat, (int64_t)amount);
68 }
69
_mi_stat_decrease(mi_stat_count_t * stat,size_t amount)70 void _mi_stat_decrease(mi_stat_count_t* stat, size_t amount) {
71 mi_stat_update(stat, -((int64_t)amount));
72 }
73
74 // must be thread safe as it is called from stats_merge
mi_stat_add(mi_stat_count_t * stat,const mi_stat_count_t * src,int64_t unit)75 static void mi_stat_add(mi_stat_count_t* stat, const mi_stat_count_t* src, int64_t unit) {
76 if (stat==src) return;
77 if (src->allocated==0 && src->freed==0) return;
78 mi_atomic_addi64_relaxed( &stat->allocated, src->allocated * unit);
79 mi_atomic_addi64_relaxed( &stat->current, src->current * unit);
80 mi_atomic_addi64_relaxed( &stat->freed, src->freed * unit);
81 // peak scores do not work across threads..
82 mi_atomic_addi64_relaxed( &stat->peak, src->peak * unit);
83 }
84
mi_stat_counter_add(mi_stat_counter_t * stat,const mi_stat_counter_t * src,int64_t unit)85 static void mi_stat_counter_add(mi_stat_counter_t* stat, const mi_stat_counter_t* src, int64_t unit) {
86 if (stat==src) return;
87 mi_atomic_addi64_relaxed( &stat->total, src->total * unit);
88 mi_atomic_addi64_relaxed( &stat->count, src->count * unit);
89 }
90
91 // must be thread safe as it is called from stats_merge
mi_stats_add(mi_stats_t * stats,const mi_stats_t * src)92 static void mi_stats_add(mi_stats_t* stats, const mi_stats_t* src) {
93 if (stats==src) return;
94 mi_stat_add(&stats->segments, &src->segments,1);
95 mi_stat_add(&stats->pages, &src->pages,1);
96 mi_stat_add(&stats->reserved, &src->reserved, 1);
97 mi_stat_add(&stats->committed, &src->committed, 1);
98 mi_stat_add(&stats->reset, &src->reset, 1);
99 mi_stat_add(&stats->purged, &src->purged, 1);
100 mi_stat_add(&stats->page_committed, &src->page_committed, 1);
101
102 mi_stat_add(&stats->pages_abandoned, &src->pages_abandoned, 1);
103 mi_stat_add(&stats->segments_abandoned, &src->segments_abandoned, 1);
104 mi_stat_add(&stats->threads, &src->threads, 1);
105
106 mi_stat_add(&stats->malloc, &src->malloc, 1);
107 mi_stat_add(&stats->segments_cache, &src->segments_cache, 1);
108 mi_stat_add(&stats->normal, &src->normal, 1);
109 mi_stat_add(&stats->huge, &src->huge, 1);
110 mi_stat_add(&stats->large, &src->large, 1);
111
112 mi_stat_counter_add(&stats->pages_extended, &src->pages_extended, 1);
113 mi_stat_counter_add(&stats->mmap_calls, &src->mmap_calls, 1);
114 mi_stat_counter_add(&stats->commit_calls, &src->commit_calls, 1);
115 mi_stat_counter_add(&stats->reset_calls, &src->reset_calls, 1);
116 mi_stat_counter_add(&stats->purge_calls, &src->purge_calls, 1);
117
118 mi_stat_counter_add(&stats->page_no_retire, &src->page_no_retire, 1);
119 mi_stat_counter_add(&stats->searches, &src->searches, 1);
120 mi_stat_counter_add(&stats->normal_count, &src->normal_count, 1);
121 mi_stat_counter_add(&stats->huge_count, &src->huge_count, 1);
122 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 }
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
mi_printf_amount(int64_t n,int64_t unit,mi_output_fun * out,void * arg,const char * fmt)139 static void mi_printf_amount(int64_t n, int64_t unit, mi_output_fun* out, void* arg, const char* fmt) {
140 char buf[32]; buf[0] = 0;
141 int len = 32;
142 const char* suffix = (unit <= 0 ? " " : "B");
143 const int64_t base = (unit == 0 ? 1000 : 1024);
144 if (unit>0) n *= unit;
145
146 const int64_t pos = (n < 0 ? -n : n);
147 if (pos < base) {
148 if (n!=1 || suffix[0] != 'B') { // skip printing 1 B for the unit column
149 snprintf(buf, len, "%d %-3s", (int)n, (n==0 ? "" : suffix));
150 }
151 }
152 else {
153 int64_t divider = base;
154 const char* magnitude = "K";
155 if (pos >= divider*base) { divider *= base; magnitude = "M"; }
156 if (pos >= divider*base) { divider *= base; magnitude = "G"; }
157 const int64_t tens = (n / (divider/10));
158 const long whole = (long)(tens/10);
159 const long frac1 = (long)(tens%10);
160 char unitdesc[8];
161 snprintf(unitdesc, 8, "%s%s%s", magnitude, (base==1024 ? "i" : ""), suffix);
162 snprintf(buf, len, "%ld.%ld %-3s", whole, (frac1 < 0 ? -frac1 : frac1), unitdesc);
163 }
164 _mi_fprintf(out, arg, (fmt==NULL ? "%12s" : fmt), buf);
165 }
166
167
mi_print_amount(int64_t n,int64_t unit,mi_output_fun * out,void * arg)168 static void mi_print_amount(int64_t n, int64_t unit, mi_output_fun* out, void* arg) {
169 mi_printf_amount(n,unit,out,arg,NULL);
170 }
171
mi_print_count(int64_t n,int64_t unit,mi_output_fun * out,void * arg)172 static void mi_print_count(int64_t n, int64_t unit, mi_output_fun* out, void* arg) {
173 if (unit==1) _mi_fprintf(out, arg, "%12s"," ");
174 else mi_print_amount(n,0,out,arg);
175 }
176
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)177 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 _mi_fprintf(out, arg,"%10s:", msg);
179 if (unit > 0) {
180 mi_print_amount(stat->peak, unit, out, arg);
181 mi_print_amount(stat->allocated, unit, out, arg);
182 mi_print_amount(stat->freed, unit, out, arg);
183 mi_print_amount(stat->current, unit, out, arg);
184 mi_print_amount(unit, 1, out, arg);
185 mi_print_count(stat->allocated, unit, out, arg);
186 if (stat->allocated > stat->freed) {
187 _mi_fprintf(out, arg, " ");
188 _mi_fprintf(out, arg, (notok == NULL ? "not all freed" : notok));
189 _mi_fprintf(out, arg, "\n");
190 }
191 else {
192 _mi_fprintf(out, arg, " ok\n");
193 }
194 }
195 else if (unit<0) {
196 mi_print_amount(stat->peak, -1, out, arg);
197 mi_print_amount(stat->allocated, -1, out, arg);
198 mi_print_amount(stat->freed, -1, out, arg);
199 mi_print_amount(stat->current, -1, out, arg);
200 if (unit==-1) {
201 _mi_fprintf(out, arg, "%24s", "");
202 }
203 else {
204 mi_print_amount(-unit, 1, out, arg);
205 mi_print_count((stat->allocated / -unit), 0, out, arg);
206 }
207 if (stat->allocated > stat->freed)
208 _mi_fprintf(out, arg, " not all freed!\n");
209 else
210 _mi_fprintf(out, arg, " ok\n");
211 }
212 else {
213 mi_print_amount(stat->peak, 1, out, arg);
214 mi_print_amount(stat->allocated, 1, out, arg);
215 _mi_fprintf(out, arg, "%11s", " "); // no freed
216 mi_print_amount(stat->current, 1, out, arg);
217 _mi_fprintf(out, arg, "\n");
218 }
219 }
220
mi_stat_print(const mi_stat_count_t * stat,const char * msg,int64_t unit,mi_output_fun * out,void * arg)221 static void mi_stat_print(const mi_stat_count_t* stat, const char* msg, int64_t unit, mi_output_fun* out, void* arg) {
222 mi_stat_print_ex(stat, msg, unit, out, arg, NULL);
223 }
224
mi_stat_peak_print(const mi_stat_count_t * stat,const char * msg,int64_t unit,mi_output_fun * out,void * arg)225 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 _mi_fprintf(out, arg, "%10s:", msg);
227 mi_print_amount(stat->peak, unit, out, arg);
228 _mi_fprintf(out, arg, "\n");
229 }
230
mi_stat_counter_print(const mi_stat_counter_t * stat,const char * msg,mi_output_fun * out,void * arg)231 static void mi_stat_counter_print(const mi_stat_counter_t* stat, const char* msg, mi_output_fun* out, void* arg ) {
232 _mi_fprintf(out, arg, "%10s:", msg);
233 mi_print_amount(stat->total, -1, out, arg);
234 _mi_fprintf(out, arg, "\n");
235 }
236
237
mi_stat_counter_print_avg(const mi_stat_counter_t * stat,const char * msg,mi_output_fun * out,void * arg)238 static void mi_stat_counter_print_avg(const mi_stat_counter_t* stat, const char* msg, mi_output_fun* out, void* arg) {
239 const int64_t avg_tens = (stat->count == 0 ? 0 : (stat->total*10 / stat->count));
240 const long avg_whole = (long)(avg_tens/10);
241 const long avg_frac1 = (long)(avg_tens%10);
242 _mi_fprintf(out, arg, "%10s: %5ld.%ld avg\n", msg, avg_whole, avg_frac1);
243 }
244
245
mi_print_header(mi_output_fun * out,void * arg)246 static void mi_print_header(mi_output_fun* out, void* arg ) {
247 _mi_fprintf(out, arg, "%10s: %11s %11s %11s %11s %11s %11s\n", "heap stats", "peak ", "total ", "freed ", "current ", "unit ", "count ");
248 }
249
250 #if MI_STAT>1
mi_stats_print_bins(const mi_stat_count_t * bins,size_t max,const char * fmt,mi_output_fun * out,void * arg)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
mi_buffered_flush(buffered_t * buf)283 static void mi_buffered_flush(buffered_t* buf) {
284 buf->buf[buf->used] = 0;
285 _mi_fputs(buf->out, buf->arg, NULL, buf->buf);
286 buf->used = 0;
287 }
288
mi_buffered_out(const char * msg,void * arg)289 static void mi_cdecl mi_buffered_out(const char* msg, void* arg) {
290 buffered_t* buf = (buffered_t*)arg;
291 if (msg==NULL || buf==NULL) return;
292 for (const char* src = msg; *src != 0; src++) {
293 char c = *src;
294 if (buf->used >= buf->count) mi_buffered_flush(buf);
295 mi_assert_internal(buf->used < buf->count);
296 buf->buf[buf->used++] = c;
297 if (c == '\n') mi_buffered_flush(buf);
298 }
299 }
300
301 //------------------------------------------------------------
302 // Print statistics
303 //------------------------------------------------------------
304
_mi_stats_print(mi_stats_t * stats,mi_output_fun * out0,void * arg0)305 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 char buf[256];
308 buffered_t buffer = { out0, arg0, NULL, 0, 255 };
309 buffer.buf = buf;
310 mi_output_fun* out = &mi_buffered_out;
311 void* arg = &buffer;
312
313 // and print using that
314 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 mi_stat_print_ex(&stats->reserved, "reserved", 1, out, arg, "");
333 mi_stat_print_ex(&stats->committed, "committed", 1, out, arg, "");
334 mi_stat_peak_print(&stats->reset, "reset", 1, out, arg );
335 mi_stat_peak_print(&stats->purged, "purged", 1, out, arg );
336 mi_stat_print(&stats->page_committed, "touched", 1, out, arg);
337 mi_stat_print(&stats->segments, "segments", -1, out, arg);
338 mi_stat_print(&stats->segments_abandoned, "-abandoned", -1, out, arg);
339 mi_stat_print(&stats->segments_cache, "-cached", -1, out, arg);
340 mi_stat_print(&stats->pages, "pages", -1, out, arg);
341 mi_stat_print(&stats->pages_abandoned, "-abandoned", -1, out, arg);
342 mi_stat_counter_print(&stats->pages_extended, "-extended", out, arg);
343 mi_stat_counter_print(&stats->page_no_retire, "-noretire", out, arg);
344 mi_stat_counter_print(&stats->mmap_calls, "mmaps", out, arg);
345 mi_stat_counter_print(&stats->commit_calls, "commits", out, arg);
346 mi_stat_counter_print(&stats->reset_calls, "resets", out, arg);
347 mi_stat_counter_print(&stats->purge_calls, "purges", out, arg);
348 mi_stat_print(&stats->threads, "threads", -1, out, arg);
349 mi_stat_counter_print_avg(&stats->searches, "searches", out, arg);
350 _mi_fprintf(out, arg, "%10s: %5zu\n", "numa nodes", _mi_os_numa_node_count());
351
352 size_t elapsed;
353 size_t user_time;
354 size_t sys_time;
355 size_t current_rss;
356 size_t peak_rss;
357 size_t current_commit;
358 size_t peak_commit;
359 size_t page_faults;
360 mi_process_info(&elapsed, &user_time, &sys_time, ¤t_rss, &peak_rss, ¤t_commit, &peak_commit, &page_faults);
361 _mi_fprintf(out, arg, "%10s: %5ld.%03ld s\n", "elapsed", elapsed/1000, elapsed%1000);
362 _mi_fprintf(out, arg, "%10s: user: %ld.%03ld s, system: %ld.%03ld s, faults: %lu, rss: ", "process",
363 user_time/1000, user_time%1000, sys_time/1000, sys_time%1000, (unsigned long)page_faults );
364 mi_printf_amount((int64_t)peak_rss, 1, out, arg, "%s");
365 if (peak_commit > 0) {
366 _mi_fprintf(out, arg, ", commit: ");
367 mi_printf_amount((int64_t)peak_commit, 1, out, arg, "%s");
368 }
369 _mi_fprintf(out, arg, "\n");
370 }
371
372 static mi_msecs_t mi_process_start; // = 0
373
mi_stats_get_default(void)374 static mi_stats_t* mi_stats_get_default(void) {
375 mi_heap_t* heap = mi_heap_get_default();
376 return &heap->tld->stats;
377 }
378
mi_stats_merge_from(mi_stats_t * stats)379 static void mi_stats_merge_from(mi_stats_t* stats) {
380 if (stats != &_mi_stats_main) {
381 mi_stats_add(&_mi_stats_main, stats);
382 memset(stats, 0, sizeof(mi_stats_t));
383 }
384 }
385
mi_stats_reset(void)386 void mi_stats_reset(void) mi_attr_noexcept {
387 mi_stats_t* stats = mi_stats_get_default();
388 if (stats != &_mi_stats_main) { memset(stats, 0, sizeof(mi_stats_t)); }
389 memset(&_mi_stats_main, 0, sizeof(mi_stats_t));
390 if (mi_process_start == 0) { mi_process_start = _mi_clock_start(); };
391 }
392
mi_stats_merge(void)393 void mi_stats_merge(void) mi_attr_noexcept {
394 mi_stats_merge_from( mi_stats_get_default() );
395 }
396
_mi_stats_done(mi_stats_t * stats)397 void _mi_stats_done(mi_stats_t* stats) { // called from `mi_thread_done`
398 mi_stats_merge_from(stats);
399 }
400
mi_stats_print_out(mi_output_fun * out,void * arg)401 void mi_stats_print_out(mi_output_fun* out, void* arg) mi_attr_noexcept {
402 mi_stats_merge_from(mi_stats_get_default());
403 _mi_stats_print(&_mi_stats_main, out, arg);
404 }
405
mi_stats_print(void * out)406 void mi_stats_print(void* out) mi_attr_noexcept {
407 // for compatibility there is an `out` parameter (which can be `stdout` or `stderr`)
408 mi_stats_print_out((mi_output_fun*)out, NULL);
409 }
410
mi_thread_stats_print_out(mi_output_fun * out,void * arg)411 void mi_thread_stats_print_out(mi_output_fun* out, void* arg) mi_attr_noexcept {
412 _mi_stats_print(mi_stats_get_default(), out, arg);
413 }
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
_mi_clock_now(void)422 mi_msecs_t _mi_clock_now(void) {
423 return _mi_prim_clock_now();
424 }
425
_mi_clock_start(void)426 mi_msecs_t _mi_clock_start(void) {
427 if (mi_clock_diff == 0.0) {
428 mi_msecs_t t0 = _mi_clock_now();
429 mi_clock_diff = _mi_clock_now() - t0;
430 }
431 return _mi_clock_now();
432 }
433
_mi_clock_end(mi_msecs_t start)434 mi_msecs_t _mi_clock_end(mi_msecs_t start) {
435 mi_msecs_t end = _mi_clock_now();
436 return (end - start - mi_clock_diff);
437 }
438
439
440 // --------------------------------------------------------
441 // Basic process statistics
442 // --------------------------------------------------------
443
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)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 {
446 mi_process_info_t pinfo;
447 _mi_memzero_var(pinfo);
448 pinfo.elapsed = _mi_clock_end(mi_process_start);
449 pinfo.current_commit = (size_t)(mi_atomic_loadi64_relaxed((_Atomic(int64_t)*)&_mi_stats_main.committed.current));
450 pinfo.peak_commit = (size_t)(mi_atomic_loadi64_relaxed((_Atomic(int64_t)*)&_mi_stats_main.committed.peak));
451 pinfo.current_rss = pinfo.current_commit;
452 pinfo.peak_rss = pinfo.peak_commit;
453 pinfo.utime = 0;
454 pinfo.stime = 0;
455 pinfo.page_faults = 0;
456
457 _mi_prim_process_info(&pinfo);
458
459 if (elapsed_msecs!=NULL) *elapsed_msecs = (pinfo.elapsed < 0 ? 0 : (pinfo.elapsed < (mi_msecs_t)PTRDIFF_MAX ? (size_t)pinfo.elapsed : PTRDIFF_MAX));
460 if (user_msecs!=NULL) *user_msecs = (pinfo.utime < 0 ? 0 : (pinfo.utime < (mi_msecs_t)PTRDIFF_MAX ? (size_t)pinfo.utime : PTRDIFF_MAX));
461 if (system_msecs!=NULL) *system_msecs = (pinfo.stime < 0 ? 0 : (pinfo.stime < (mi_msecs_t)PTRDIFF_MAX ? (size_t)pinfo.stime : PTRDIFF_MAX));
462 if (current_rss!=NULL) *current_rss = pinfo.current_rss;
463 if (peak_rss!=NULL) *peak_rss = pinfo.peak_rss;
464 if (current_commit!=NULL) *current_commit = pinfo.current_commit;
465 if (peak_commit!=NULL) *peak_commit = pinfo.peak_commit;
466 if (page_faults!=NULL) *page_faults = pinfo.page_faults;
467 }
468