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