1/* ----------------------------------------------------------------------------
2Copyright (c) 2018-2021, Microsoft Research, Daan Leijen
3This is free software; you can redistribute it and/or modify it under the
4terms 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
11#include <stdio.h> // fputs, stderr
12#include <string.h> // memset
13
14#if defined(_MSC_VER) && (_MSC_VER < 1920)
15#pragma warning(disable:4204) // non-constant aggregate initializer
16#endif
17
18/* -----------------------------------------------------------
19 Statistics operations
20----------------------------------------------------------- */
21
22static bool mi_is_in_main(void* stat) {
23 return ((uint8_t*)stat >= (uint8_t*)&_mi_stats_main
24 && (uint8_t*)stat < ((uint8_t*)&_mi_stats_main + sizeof(mi_stats_t)));
25}
26
27static void mi_stat_update(mi_stat_count_t* stat, int64_t amount) {
28 if (amount == 0) return;
29 if (mi_is_in_main(stat))
30 {
31 // add atomically (for abandoned pages)
32 int64_t current = mi_atomic_addi64_relaxed(&stat->current, amount);
33 mi_atomic_maxi64_relaxed(&stat->peak, current + amount);
34 if (amount > 0) {
35 mi_atomic_addi64_relaxed(&stat->allocated,amount);
36 }
37 else {
38 mi_atomic_addi64_relaxed(&stat->freed, -amount);
39 }
40 }
41 else {
42 // add thread local
43 stat->current += amount;
44 if (stat->current > stat->peak) stat->peak = stat->current;
45 if (amount > 0) {
46 stat->allocated += amount;
47 }
48 else {
49 stat->freed += -amount;
50 }
51 }
52}
53
54void _mi_stat_counter_increase(mi_stat_counter_t* stat, size_t amount) {
55 if (mi_is_in_main(stat)) {
56 mi_atomic_addi64_relaxed( &stat->count, 1 );
57 mi_atomic_addi64_relaxed( &stat->total, (int64_t)amount );
58 }
59 else {
60 stat->count++;
61 stat->total += amount;
62 }
63}
64
65void _mi_stat_increase(mi_stat_count_t* stat, size_t amount) {
66 mi_stat_update(stat, (int64_t)amount);
67}
68
69void _mi_stat_decrease(mi_stat_count_t* stat, size_t amount) {
70 mi_stat_update(stat, -((int64_t)amount));
71}
72
73// must be thread safe as it is called from stats_merge
74static void mi_stat_add(mi_stat_count_t* stat, const mi_stat_count_t* src, int64_t unit) {
75 if (stat==src) return;
76 if (src->allocated==0 && src->freed==0) return;
77 mi_atomic_addi64_relaxed( &stat->allocated, src->allocated * unit);
78 mi_atomic_addi64_relaxed( &stat->current, src->current * unit);
79 mi_atomic_addi64_relaxed( &stat->freed, src->freed * unit);
80 // peak scores do not work across threads..
81 mi_atomic_addi64_relaxed( &stat->peak, src->peak * unit);
82}
83
84static void mi_stat_counter_add(mi_stat_counter_t* stat, const mi_stat_counter_t* src, int64_t unit) {
85 if (stat==src) return;
86 mi_atomic_addi64_relaxed( &stat->total, src->total * unit);
87 mi_atomic_addi64_relaxed( &stat->count, src->count * unit);
88}
89
90// must be thread safe as it is called from stats_merge
91static void mi_stats_add(mi_stats_t* stats, const mi_stats_t* src) {
92 if (stats==src) return;
93 mi_stat_add(&stats->segments, &src->segments,1);
94 mi_stat_add(&stats->pages, &src->pages,1);
95 mi_stat_add(&stats->reserved, &src->reserved, 1);
96 mi_stat_add(&stats->committed, &src->committed, 1);
97 mi_stat_add(&stats->reset, &src->reset, 1);
98 mi_stat_add(&stats->page_committed, &src->page_committed, 1);
99
100 mi_stat_add(&stats->pages_abandoned, &src->pages_abandoned, 1);
101 mi_stat_add(&stats->segments_abandoned, &src->segments_abandoned, 1);
102 mi_stat_add(&stats->threads, &src->threads, 1);
103
104 mi_stat_add(&stats->malloc, &src->malloc, 1);
105 mi_stat_add(&stats->segments_cache, &src->segments_cache, 1);
106 mi_stat_add(&stats->normal, &src->normal, 1);
107 mi_stat_add(&stats->huge, &src->huge, 1);
108 mi_stat_add(&stats->large, &src->large, 1);
109
110 mi_stat_counter_add(&stats->pages_extended, &src->pages_extended, 1);
111 mi_stat_counter_add(&stats->mmap_calls, &src->mmap_calls, 1);
112 mi_stat_counter_add(&stats->commit_calls, &src->commit_calls, 1);
113
114 mi_stat_counter_add(&stats->page_no_retire, &src->page_no_retire, 1);
115 mi_stat_counter_add(&stats->searches, &src->searches, 1);
116 mi_stat_counter_add(&stats->normal_count, &src->normal_count, 1);
117 mi_stat_counter_add(&stats->huge_count, &src->huge_count, 1);
118 mi_stat_counter_add(&stats->large_count, &src->large_count, 1);
119#if MI_STAT>1
120 for (size_t i = 0; i <= MI_BIN_HUGE; i++) {
121 if (src->normal_bins[i].allocated > 0 || src->normal_bins[i].freed > 0) {
122 mi_stat_add(&stats->normal_bins[i], &src->normal_bins[i], 1);
123 }
124 }
125#endif
126}
127
128/* -----------------------------------------------------------
129 Display statistics
130----------------------------------------------------------- */
131
132// unit > 0 : size in binary bytes
133// unit == 0: count as decimal
134// unit < 0 : count in binary
135static void mi_printf_amount(int64_t n, int64_t unit, mi_output_fun* out, void* arg, const char* fmt) {
136 char buf[32]; buf[0] = 0;
137 int len = 32;
138 const char* suffix = (unit <= 0 ? " " : "B");
139 const int64_t base = (unit == 0 ? 1000 : 1024);
140 if (unit>0) n *= unit;
141
142 const int64_t pos = (n < 0 ? -n : n);
143 if (pos < base) {
144 if (n!=1 || suffix[0] != 'B') { // skip printing 1 B for the unit column
145 snprintf(buf, len, "%d %-3s", (int)n, (n==0 ? "" : suffix));
146 }
147 }
148 else {
149 int64_t divider = base;
150 const char* magnitude = "K";
151 if (pos >= divider*base) { divider *= base; magnitude = "M"; }
152 if (pos >= divider*base) { divider *= base; magnitude = "G"; }
153 const int64_t tens = (n / (divider/10));
154 const long whole = (long)(tens/10);
155 const long frac1 = (long)(tens%10);
156 char unitdesc[8];
157 snprintf(unitdesc, 8, "%s%s%s", magnitude, (base==1024 ? "i" : ""), suffix);
158 snprintf(buf, len, "%ld.%ld %-3s", whole, (frac1 < 0 ? -frac1 : frac1), unitdesc);
159 }
160 _mi_fprintf(out, arg, (fmt==NULL ? "%11s" : fmt), buf);
161}
162
163
164static void mi_print_amount(int64_t n, int64_t unit, mi_output_fun* out, void* arg) {
165 mi_printf_amount(n,unit,out,arg,NULL);
166}
167
168static void mi_print_count(int64_t n, int64_t unit, mi_output_fun* out, void* arg) {
169 if (unit==1) _mi_fprintf(out, arg, "%11s"," ");
170 else mi_print_amount(n,0,out,arg);
171}
172
173static void mi_stat_print(const mi_stat_count_t* stat, const char* msg, int64_t unit, mi_output_fun* out, void* arg ) {
174 _mi_fprintf(out, arg,"%10s:", msg);
175 if (unit>0) {
176 mi_print_amount(stat->peak, unit, out, arg);
177 mi_print_amount(stat->allocated, unit, out, arg);
178 mi_print_amount(stat->freed, unit, out, arg);
179 mi_print_amount(stat->current, unit, out, arg);
180 mi_print_amount(unit, 1, out, arg);
181 mi_print_count(stat->allocated, unit, out, arg);
182 if (stat->allocated > stat->freed)
183 _mi_fprintf(out, arg, " not all freed!\n");
184 else
185 _mi_fprintf(out, arg, " ok\n");
186 }
187 else if (unit<0) {
188 mi_print_amount(stat->peak, -1, out, arg);
189 mi_print_amount(stat->allocated, -1, out, arg);
190 mi_print_amount(stat->freed, -1, out, arg);
191 mi_print_amount(stat->current, -1, out, arg);
192 if (unit==-1) {
193 _mi_fprintf(out, arg, "%22s", "");
194 }
195 else {
196 mi_print_amount(-unit, 1, out, arg);
197 mi_print_count((stat->allocated / -unit), 0, out, arg);
198 }
199 if (stat->allocated > stat->freed)
200 _mi_fprintf(out, arg, " not all freed!\n");
201 else
202 _mi_fprintf(out, arg, " ok\n");
203 }
204 else {
205 mi_print_amount(stat->peak, 1, out, arg);
206 mi_print_amount(stat->allocated, 1, out, arg);
207 _mi_fprintf(out, arg, "%11s", " "); // no freed
208 mi_print_amount(stat->current, 1, out, arg);
209 _mi_fprintf(out, arg, "\n");
210 }
211}
212
213static void mi_stat_counter_print(const mi_stat_counter_t* stat, const char* msg, mi_output_fun* out, void* arg ) {
214 _mi_fprintf(out, arg, "%10s:", msg);
215 mi_print_amount(stat->total, -1, out, arg);
216 _mi_fprintf(out, arg, "\n");
217}
218
219static void mi_stat_counter_print_avg(const mi_stat_counter_t* stat, const char* msg, mi_output_fun* out, void* arg) {
220 const int64_t avg_tens = (stat->count == 0 ? 0 : (stat->total*10 / stat->count));
221 const long avg_whole = (long)(avg_tens/10);
222 const long avg_frac1 = (long)(avg_tens%10);
223 _mi_fprintf(out, arg, "%10s: %5ld.%ld avg\n", msg, avg_whole, avg_frac1);
224}
225
226
227static void mi_print_header(mi_output_fun* out, void* arg ) {
228 _mi_fprintf(out, arg, "%10s: %10s %10s %10s %10s %10s %10s\n", "heap stats", "peak ", "total ", "freed ", "current ", "unit ", "count ");
229}
230
231#if MI_STAT>1
232static void mi_stats_print_bins(const mi_stat_count_t* bins, size_t max, const char* fmt, mi_output_fun* out, void* arg) {
233 bool found = false;
234 char buf[64];
235 for (size_t i = 0; i <= max; i++) {
236 if (bins[i].allocated > 0) {
237 found = true;
238 int64_t unit = _mi_bin_size((uint8_t)i);
239 snprintf(buf, 64, "%s %3lu", fmt, (long)i);
240 mi_stat_print(&bins[i], buf, unit, out, arg);
241 }
242 }
243 if (found) {
244 _mi_fprintf(out, arg, "\n");
245 mi_print_header(out, arg);
246 }
247}
248#endif
249
250
251
252//------------------------------------------------------------
253// Use an output wrapper for line-buffered output
254// (which is nice when using loggers etc.)
255//------------------------------------------------------------
256typedef struct buffered_s {
257 mi_output_fun* out; // original output function
258 void* arg; // and state
259 char* buf; // local buffer of at least size `count+1`
260 size_t used; // currently used chars `used <= count`
261 size_t count; // total chars available for output
262} buffered_t;
263
264static void mi_buffered_flush(buffered_t* buf) {
265 buf->buf[buf->used] = 0;
266 _mi_fputs(buf->out, buf->arg, NULL, buf->buf);
267 buf->used = 0;
268}
269
270static void mi_cdecl mi_buffered_out(const char* msg, void* arg) {
271 buffered_t* buf = (buffered_t*)arg;
272 if (msg==NULL || buf==NULL) return;
273 for (const char* src = msg; *src != 0; src++) {
274 char c = *src;
275 if (buf->used >= buf->count) mi_buffered_flush(buf);
276 mi_assert_internal(buf->used < buf->count);
277 buf->buf[buf->used++] = c;
278 if (c == '\n') mi_buffered_flush(buf);
279 }
280}
281
282//------------------------------------------------------------
283// Print statistics
284//------------------------------------------------------------
285
286static void mi_stat_process_info(mi_msecs_t* elapsed, mi_msecs_t* utime, mi_msecs_t* stime, size_t* current_rss, size_t* peak_rss, size_t* current_commit, size_t* peak_commit, size_t* page_faults);
287
288static void _mi_stats_print(mi_stats_t* stats, mi_output_fun* out0, void* arg0) mi_attr_noexcept {
289 // wrap the output function to be line buffered
290 char buf[256];
291 buffered_t buffer = { out0, arg0, NULL, 0, 255 };
292 buffer.buf = buf;
293 mi_output_fun* out = &mi_buffered_out;
294 void* arg = &buffer;
295
296 // and print using that
297 mi_print_header(out,arg);
298 #if MI_STAT>1
299 mi_stats_print_bins(stats->normal_bins, MI_BIN_HUGE, "normal",out,arg);
300 #endif
301 #if MI_STAT
302 mi_stat_print(&stats->normal, "normal", (stats->normal_count.count == 0 ? 1 : -(stats->normal.allocated / stats->normal_count.count)), out, arg);
303 mi_stat_print(&stats->large, "large", (stats->large_count.count == 0 ? 1 : -(stats->large.allocated / stats->large_count.count)), out, arg);
304 mi_stat_print(&stats->huge, "huge", (stats->huge_count.count == 0 ? 1 : -(stats->huge.allocated / stats->huge_count.count)), out, arg);
305 mi_stat_count_t total = { 0,0,0,0 };
306 mi_stat_add(&total, &stats->normal, 1);
307 mi_stat_add(&total, &stats->large, 1);
308 mi_stat_add(&total, &stats->huge, 1);
309 mi_stat_print(&total, "total", 1, out, arg);
310 #endif
311 #if MI_STAT>1
312 mi_stat_print(&stats->malloc, "malloc req", 1, out, arg);
313 _mi_fprintf(out, arg, "\n");
314 #endif
315 mi_stat_print(&stats->reserved, "reserved", 1, out, arg);
316 mi_stat_print(&stats->committed, "committed", 1, out, arg);
317 mi_stat_print(&stats->reset, "reset", 1, out, arg);
318 mi_stat_print(&stats->page_committed, "touched", 1, out, arg);
319 mi_stat_print(&stats->segments, "segments", -1, out, arg);
320 mi_stat_print(&stats->segments_abandoned, "-abandoned", -1, out, arg);
321 mi_stat_print(&stats->segments_cache, "-cached", -1, out, arg);
322 mi_stat_print(&stats->pages, "pages", -1, out, arg);
323 mi_stat_print(&stats->pages_abandoned, "-abandoned", -1, out, arg);
324 mi_stat_counter_print(&stats->pages_extended, "-extended", out, arg);
325 mi_stat_counter_print(&stats->page_no_retire, "-noretire", out, arg);
326 mi_stat_counter_print(&stats->mmap_calls, "mmaps", out, arg);
327 mi_stat_counter_print(&stats->commit_calls, "commits", out, arg);
328 mi_stat_print(&stats->threads, "threads", -1, out, arg);
329 mi_stat_counter_print_avg(&stats->searches, "searches", out, arg);
330 _mi_fprintf(out, arg, "%10s: %7zu\n", "numa nodes", _mi_os_numa_node_count());
331
332 mi_msecs_t elapsed;
333 mi_msecs_t user_time;
334 mi_msecs_t sys_time;
335 size_t current_rss;
336 size_t peak_rss;
337 size_t current_commit;
338 size_t peak_commit;
339 size_t page_faults;
340 mi_stat_process_info(&elapsed, &user_time, &sys_time, &current_rss, &peak_rss, &current_commit, &peak_commit, &page_faults);
341 _mi_fprintf(out, arg, "%10s: %7ld.%03ld s\n", "elapsed", elapsed/1000, elapsed%1000);
342 _mi_fprintf(out, arg, "%10s: user: %ld.%03ld s, system: %ld.%03ld s, faults: %lu, rss: ", "process",
343 user_time/1000, user_time%1000, sys_time/1000, sys_time%1000, (unsigned long)page_faults );
344 mi_printf_amount((int64_t)peak_rss, 1, out, arg, "%s");
345 if (peak_commit > 0) {
346 _mi_fprintf(out, arg, ", commit: ");
347 mi_printf_amount((int64_t)peak_commit, 1, out, arg, "%s");
348 }
349 _mi_fprintf(out, arg, "\n");
350}
351
352static mi_msecs_t mi_process_start; // = 0
353
354static mi_stats_t* mi_stats_get_default(void) {
355 mi_heap_t* heap = mi_heap_get_default();
356 return &heap->tld->stats;
357}
358
359static void mi_stats_merge_from(mi_stats_t* stats) {
360 if (stats != &_mi_stats_main) {
361 mi_stats_add(&_mi_stats_main, stats);
362 memset(stats, 0, sizeof(mi_stats_t));
363 }
364}
365
366void mi_stats_reset(void) mi_attr_noexcept {
367 mi_stats_t* stats = mi_stats_get_default();
368 if (stats != &_mi_stats_main) { memset(stats, 0, sizeof(mi_stats_t)); }
369 memset(&_mi_stats_main, 0, sizeof(mi_stats_t));
370 if (mi_process_start == 0) { mi_process_start = _mi_clock_start(); };
371}
372
373void mi_stats_merge(void) mi_attr_noexcept {
374 mi_stats_merge_from( mi_stats_get_default() );
375}
376
377void _mi_stats_done(mi_stats_t* stats) { // called from `mi_thread_done`
378 mi_stats_merge_from(stats);
379}
380
381void mi_stats_print_out(mi_output_fun* out, void* arg) mi_attr_noexcept {
382 mi_stats_merge_from(mi_stats_get_default());
383 _mi_stats_print(&_mi_stats_main, out, arg);
384}
385
386void mi_stats_print(void* out) mi_attr_noexcept {
387 // for compatibility there is an `out` parameter (which can be `stdout` or `stderr`)
388 mi_stats_print_out((mi_output_fun*)out, NULL);
389}
390
391void mi_thread_stats_print_out(mi_output_fun* out, void* arg) mi_attr_noexcept {
392 _mi_stats_print(mi_stats_get_default(), out, arg);
393}
394
395
396// ----------------------------------------------------------------
397// Basic timer for convenience; use milli-seconds to avoid doubles
398// ----------------------------------------------------------------
399#ifdef _WIN32
400#include <windows.h>
401static mi_msecs_t mi_to_msecs(LARGE_INTEGER t) {
402 static LARGE_INTEGER mfreq; // = 0
403 if (mfreq.QuadPart == 0LL) {
404 LARGE_INTEGER f;
405 QueryPerformanceFrequency(&f);
406 mfreq.QuadPart = f.QuadPart/1000LL;
407 if (mfreq.QuadPart == 0) mfreq.QuadPart = 1;
408 }
409 return (mi_msecs_t)(t.QuadPart / mfreq.QuadPart);
410}
411
412mi_msecs_t _mi_clock_now(void) {
413 LARGE_INTEGER t;
414 QueryPerformanceCounter(&t);
415 return mi_to_msecs(t);
416}
417#else
418#include <time.h>
419#if defined(CLOCK_REALTIME) || defined(CLOCK_MONOTONIC)
420mi_msecs_t _mi_clock_now(void) {
421 struct timespec t;
422 #ifdef CLOCK_MONOTONIC
423 clock_gettime(CLOCK_MONOTONIC, &t);
424 #else
425 clock_gettime(CLOCK_REALTIME, &t);
426 #endif
427 return ((mi_msecs_t)t.tv_sec * 1000) + ((mi_msecs_t)t.tv_nsec / 1000000);
428}
429#else
430// low resolution timer
431mi_msecs_t _mi_clock_now(void) {
432 return ((mi_msecs_t)clock() / ((mi_msecs_t)CLOCKS_PER_SEC / 1000));
433}
434#endif
435#endif
436
437
438static mi_msecs_t mi_clock_diff;
439
440mi_msecs_t _mi_clock_start(void) {
441 if (mi_clock_diff == 0.0) {
442 mi_msecs_t t0 = _mi_clock_now();
443 mi_clock_diff = _mi_clock_now() - t0;
444 }
445 return _mi_clock_now();
446}
447
448mi_msecs_t _mi_clock_end(mi_msecs_t start) {
449 mi_msecs_t end = _mi_clock_now();
450 return (end - start - mi_clock_diff);
451}
452
453
454// --------------------------------------------------------
455// Basic process statistics
456// --------------------------------------------------------
457
458#if defined(_WIN32)
459#include <windows.h>
460#include <psapi.h>
461#pragma comment(lib,"psapi.lib")
462
463static mi_msecs_t filetime_msecs(const FILETIME* ftime) {
464 ULARGE_INTEGER i;
465 i.LowPart = ftime->dwLowDateTime;
466 i.HighPart = ftime->dwHighDateTime;
467 mi_msecs_t msecs = (i.QuadPart / 10000); // FILETIME is in 100 nano seconds
468 return msecs;
469}
470
471static void mi_stat_process_info(mi_msecs_t* elapsed, mi_msecs_t* utime, mi_msecs_t* stime, size_t* current_rss, size_t* peak_rss, size_t* current_commit, size_t* peak_commit, size_t* page_faults)
472{
473 *elapsed = _mi_clock_end(mi_process_start);
474 FILETIME ct;
475 FILETIME ut;
476 FILETIME st;
477 FILETIME et;
478 GetProcessTimes(GetCurrentProcess(), &ct, &et, &st, &ut);
479 *utime = filetime_msecs(&ut);
480 *stime = filetime_msecs(&st);
481 PROCESS_MEMORY_COUNTERS info;
482 GetProcessMemoryInfo(GetCurrentProcess(), &info, sizeof(info));
483 *current_rss = (size_t)info.WorkingSetSize;
484 *peak_rss = (size_t)info.PeakWorkingSetSize;
485 *current_commit = (size_t)info.PagefileUsage;
486 *peak_commit = (size_t)info.PeakPagefileUsage;
487 *page_faults = (size_t)info.PageFaultCount;
488}
489
490#elif !defined(__wasi__) && (defined(__unix__) || defined(__unix) || defined(unix) || defined(__APPLE__) || defined(__HAIKU__))
491#include <stdio.h>
492#include <unistd.h>
493#include <sys/resource.h>
494
495#if defined(__APPLE__)
496#include <mach/mach.h>
497#endif
498
499#if defined(__HAIKU__)
500#include <kernel/OS.h>
501#endif
502
503static mi_msecs_t timeval_secs(const struct timeval* tv) {
504 return ((mi_msecs_t)tv->tv_sec * 1000L) + ((mi_msecs_t)tv->tv_usec / 1000L);
505}
506
507static void mi_stat_process_info(mi_msecs_t* elapsed, mi_msecs_t* utime, mi_msecs_t* stime, size_t* current_rss, size_t* peak_rss, size_t* current_commit, size_t* peak_commit, size_t* page_faults)
508{
509 *elapsed = _mi_clock_end(mi_process_start);
510 struct rusage rusage;
511 getrusage(RUSAGE_SELF, &rusage);
512 *utime = timeval_secs(&rusage.ru_utime);
513 *stime = timeval_secs(&rusage.ru_stime);
514#if !defined(__HAIKU__)
515 *page_faults = rusage.ru_majflt;
516#endif
517 // estimate commit using our stats
518 *peak_commit = (size_t)(mi_atomic_loadi64_relaxed((_Atomic(int64_t)*)&_mi_stats_main.committed.peak));
519 *current_commit = (size_t)(mi_atomic_loadi64_relaxed((_Atomic(int64_t)*)&_mi_stats_main.committed.current));
520 *current_rss = *current_commit; // estimate
521#if defined(__HAIKU__)
522 // Haiku does not have (yet?) a way to
523 // get these stats per process
524 thread_info tid;
525 area_info mem;
526 ssize_t c;
527 get_thread_info(find_thread(0), &tid);
528 while (get_next_area_info(tid.team, &c, &mem) == B_OK) {
529 *peak_rss += mem.ram_size;
530 }
531 *page_faults = 0;
532#elif defined(__APPLE__)
533 *peak_rss = rusage.ru_maxrss; // BSD reports in bytes
534 struct mach_task_basic_info info;
535 mach_msg_type_number_t infoCount = MACH_TASK_BASIC_INFO_COUNT;
536 if (task_info(mach_task_self(), MACH_TASK_BASIC_INFO, (task_info_t)&info, &infoCount) == KERN_SUCCESS) {
537 *current_rss = (size_t)info.resident_size;
538 }
539#else
540 *peak_rss = rusage.ru_maxrss * 1024; // Linux reports in KiB
541#endif
542}
543
544#else
545#ifndef __wasi__
546// WebAssembly instances are not processes
547#pragma message("define a way to get process info")
548#endif
549
550static void mi_stat_process_info(mi_msecs_t* elapsed, mi_msecs_t* utime, mi_msecs_t* stime, size_t* current_rss, size_t* peak_rss, size_t* current_commit, size_t* peak_commit, size_t* page_faults)
551{
552 *elapsed = _mi_clock_end(mi_process_start);
553 *peak_commit = (size_t)(mi_atomic_loadi64_relaxed((_Atomic(int64_t)*)&_mi_stats_main.committed.peak));
554 *current_commit = (size_t)(mi_atomic_loadi64_relaxed((_Atomic(int64_t)*)&_mi_stats_main.committed.current));
555 *peak_rss = *peak_commit;
556 *current_rss = *current_commit;
557 *page_faults = 0;
558 *utime = 0;
559 *stime = 0;
560}
561#endif
562
563
564mi_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
565{
566 mi_msecs_t elapsed = 0;
567 mi_msecs_t utime = 0;
568 mi_msecs_t stime = 0;
569 size_t current_rss0 = 0;
570 size_t peak_rss0 = 0;
571 size_t current_commit0 = 0;
572 size_t peak_commit0 = 0;
573 size_t page_faults0 = 0;
574 mi_stat_process_info(&elapsed,&utime, &stime, &current_rss0, &peak_rss0, &current_commit0, &peak_commit0, &page_faults0);
575 if (elapsed_msecs!=NULL) *elapsed_msecs = (elapsed < 0 ? 0 : (elapsed < (mi_msecs_t)PTRDIFF_MAX ? (size_t)elapsed : PTRDIFF_MAX));
576 if (user_msecs!=NULL) *user_msecs = (utime < 0 ? 0 : (utime < (mi_msecs_t)PTRDIFF_MAX ? (size_t)utime : PTRDIFF_MAX));
577 if (system_msecs!=NULL) *system_msecs = (stime < 0 ? 0 : (stime < (mi_msecs_t)PTRDIFF_MAX ? (size_t)stime : PTRDIFF_MAX));
578 if (current_rss!=NULL) *current_rss = current_rss0;
579 if (peak_rss!=NULL) *peak_rss = peak_rss0;
580 if (current_commit!=NULL) *current_commit = current_commit0;
581 if (peak_commit!=NULL) *peak_commit = peak_commit0;
582 if (page_faults!=NULL) *page_faults = page_faults0;
583}
584
585