| 570 | } |
| 571 | |
| 572 | static bool mem_map_collect_impl(cbm_mem_map_t *out, bool walk_allocator) { |
| 573 | if (!out) { |
| 574 | return false; |
| 575 | } |
| 576 | memset(out, 0, sizeof(*out)); |
| 577 | |
| 578 | size_t elapsed_ms = 0; |
| 579 | size_t user_ms = 0; |
| 580 | size_t sys_ms = 0; |
| 581 | size_t current_rss = 0; |
| 582 | size_t peak_rss = 0; |
| 583 | size_t current_commit = 0; |
| 584 | size_t peak_commit = 0; |
| 585 | size_t page_faults = 0; |
| 586 | mi_process_info(&elapsed_ms, &user_ms, &sys_ms, ¤t_rss, &peak_rss, ¤t_commit, |
| 587 | &peak_commit, &page_faults); |
| 588 | out->os_rss_bytes = current_rss ? current_rss : cbm_mem_rss(); |
| 589 | out->os_committed_bytes = current_commit; |
| 590 | |
| 591 | /* Does plain malloc actually land in the allocator's regions? This single |
| 592 | * bit distinguishes "nothing is live" from "the allocator does not own |
| 593 | * this build's allocations", which are opposite conclusions from the same |
| 594 | * zero. Probe with a real malloc/free pair rather than inferring from |
| 595 | * build flags, because the Windows static-CRT override is defined at |
| 596 | * compile time yet can still fail to take effect at link time. */ |
| 597 | void *probe = malloc(CBM_SZ_64); |
| 598 | if (probe) { |
| 599 | out->malloc_is_allocator_owned = mi_is_in_heap_region(probe); |
| 600 | free(probe); |
| 601 | } |
| 602 | |
| 603 | /* Walk only heaps this thread may safely read. |
| 604 | * |
| 605 | * mi_heap_main() is the process-wide aggregate: any other thread can be |
| 606 | * allocating into it while the walk runs, which is a data race by |
| 607 | * construction and TSan reports it as one (init.c:452 in mi_heap_main). |
| 608 | * A diagnostic must not introduce a race into the code it measures, so the |
| 609 | * aggregate walk is gone. What remains is safe by ownership: this thread's |
| 610 | * own theap, plus abandoned pages, which by definition have no owning |
| 611 | * thread left to race with. |
| 612 | * |
| 613 | * That includes mi_heap_main() itself: merely CALLING it reads the |
| 614 | * allocator's main-heap pointer, which a thread exiting concurrently |
| 615 | * rewrites from _mi_thread_done -> _mi_theap_default_set. TSan caught |
| 616 | * exactly that pairing on macOS, so the abandoned-page walk goes too -- |
| 617 | * it could only be reached through mi_heap_main(). |
| 618 | * |
| 619 | * The cost is coverage -- other threads' live blocks and abandoned pages |
| 620 | * are not attributed -- and that is exactly what the residual in mem.h |
| 621 | * exists to carry. An unmeasured map must never read as an empty one. */ |
| 622 | if (walk_allocator) { |
| 623 | (void)mi_theap_visit_blocks(mi_theap_get_default(), false, mem_map_visit_area, out); |
| 624 | } |
| 625 | return true; |
| 626 | } |
| 627 | |
| 628 | /* ── Allocator ownership audit (see mem.h) ─────────────────────────── */ |
| 629 |
no test coverage detected