• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &current_rss, &peak_rss, &current_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