Changeset 283178 in webkit
- Timestamp:
- Sep 28, 2021 9:59:52 AM (3 years ago)
- Location:
- trunk/Source/bmalloc
- Files:
-
- 1 added
- 1 deleted
- 9 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/Source/bmalloc/ChangeLog
r282899 r283178 1 2021-09-27 Filip Pizlo <fpizlo@apple.com> 2 3 [libpas] Fix coalescing of the large sharing pool and make it easy to introspect it (update to e4d20851ee9ff00f2962b349a9ff8465695a83d7) 4 https://bugs.webkit.org/show_bug.cgi?id=230867 5 6 Reviewed by Yusuke Suzuki. 7 8 This adds the ability to enable the libpas status reporter, adds a large sharing pool dump to 9 the status report, and fixes a large sharing pool coalescing bug found by doing that. Previously 10 we weren't coalescing things that are not free+committed. 11 12 Also updates the export script that I use to keep the libpas git repo in sync with what's in WK. 13 14 The large sharing pool is the mechanism by which libpas can find memory that can be decommitted 15 across isolated large heaps, even if those large heaps share pages with one another. The main 16 data structure is a red-black tree of nodes that represent memory ranges. If there are two 17 adjacent ranges of memory that are both fully live and committed or both decommitted, then we 18 want those to be represented using a single node. That wasn't quite working right. Even the 19 libpas test for this was testing the wrong thing. This fixes the behavior and the test. It's 20 perf-neutral since large heaps usually have a small number of objects in them anyway. 21 22 The new status reporting functionality can be enabled with the WebKitPasStatusReporter 23 environment variable. This takes an integer that tells the amount of data in the report. Here 24 are the recognized values: 25 26 1 - just report number of heaps 27 2 - something in between 1 and 3 28 3 - report everything that the status reporter can report right now (per-page data for 29 segregated/bitfit heaps, lots of details for large heaps) 30 31 If the status reporter ever reported per-object information, it would be at level 4 or higher. 32 It's safe to pass 9999 or whatever if you just want the maximum report that libpas supports. 33 TL;DR for now you usually want WebKitPasStatusReporter=3. 34 35 * bmalloc/Environment.cpp: 36 (bmalloc::Environment::Environment): 37 * libpas/export.rb: Added. 38 * libpas/export.sh: Removed. 39 * libpas/src/libpas/pas_bitfit_directory.c: 40 (pas_bitfit_directory_construct): I needed to rationalize how we initialize disabled directories to make status reporting work. 41 (pas_bitfit_directory_get_first_free_view): 42 * libpas/src/libpas/pas_large_sharing_pool.c: 43 (states_match): 44 * libpas/src/libpas/pas_status_reporter.c: 45 (pas_status_reporter_dump_bitfit_directory): 46 (dump_large_sharing_pool_node_callback): 47 (pas_status_reporter_dump_large_sharing_pool): 48 (pas_status_reporter_dump_everything): 49 * libpas/src/libpas/pas_status_reporter.h: 50 * libpas/src/test/LargeSharingPoolDump.cpp: 51 * libpas/src/test/LargeSharingPoolDump.h: 52 * libpas/src/test/LargeSharingPoolTests.cpp: 53 (std::Range::Range): 54 (std::Range::operator== const): 55 (std::Range::operator!= const): 56 (std::operator<<): 57 (std::assertState): 58 (std::testGoodCoalesceEpochUpdate): 59 (addLargeSharingPoolTests): 60 (std::testBadCoalesceEpochUpdate): Deleted. 61 1 62 2021-09-22 Filip Pizlo <fpizlo@apple.com> 2 63 -
trunk/Source/bmalloc/bmalloc/Environment.cpp
r261207 r283178 48 48 int malloc_engaged_nano(void); 49 49 } 50 #endif 51 52 #if BUSE(LIBPAS) 53 #include "pas_status_reporter.h" 50 54 #endif 51 55 … … 137 141 : m_isDebugHeapEnabled(computeIsDebugHeapEnabled()) 138 142 { 143 #if BUSE(LIBPAS) 144 const char* statusReporter = getenv("WebKitPasStatusReporter"); 145 if (statusReporter) { 146 unsigned enabled; 147 if (sscanf(statusReporter, "%u", &enabled) == 1) 148 pas_status_reporter_enabled = enabled; 149 } 150 #endif 139 151 } 140 152 -
trunk/Source/bmalloc/libpas/src/libpas/pas_bitfit_directory.c
r282556 r283178 47 47 static const bool verbose = false; 48 48 49 /* NOTE - this works even if the config is disabled, and produces a directory that is empty and 50 does nothing. This makes sense because it makes it easy to iterate over the directories in a heap 51 without knowing what your config is. */ 52 49 53 if (verbose) 50 54 pas_log("Creating directory %p\n", directory); … … 55 59 pas_bitfit_directory_view_vector_construct(&directory->views); 56 60 pas_compact_atomic_bitfit_size_class_ptr_store(&directory->largest_size_class, NULL); 57 directory->config_kind = config-> kind;61 directory->config_kind = config->base.is_enabled ? config->kind : pas_bitfit_page_config_kind_null; 58 62 59 63 directory->heap = heap; … … 63 67 /* We could have been lazy about this - but it's probably not super necessary since the point 64 68 of bitfit global directories is that there won't be too many of them. */ 65 if ( pas_bitfit_directory_does_sharing(directory)) {69 if (config->base.is_enabled && pas_bitfit_directory_does_sharing(directory)) { 66 70 pas_page_sharing_pool_add( 67 71 &pas_physical_page_sharing_pool, … … 103 107 { 104 108 static const bool verbose = false; 109 110 PAS_ASSERT(page_config->base.is_enabled); 105 111 106 112 for (;;) { -
trunk/Source/bmalloc/libpas/src/libpas/pas_large_sharing_pool.c
r279867 r283178 288 288 pas_large_sharing_node* right) 289 289 { 290 return left->is_committed == right->is_committed 291 && left->synchronization_style == right->synchronization_style 292 && ((!left->num_live_bytes && !right->num_live_bytes) || 293 (pas_range_size(left->range) == left->num_live_bytes && 294 pas_range_size(right->range) == right->num_live_bytes)) 295 && (left->use_epoch == right->use_epoch 296 || (!left->is_committed && !left->num_live_bytes)); 290 bool both_empty; 291 bool both_full; 292 293 if (left->is_committed != right->is_committed) 294 return false; 295 296 if (left->synchronization_style != right->synchronization_style) 297 return false; 298 299 both_empty = 300 !left->num_live_bytes && 301 !right->num_live_bytes; 302 303 both_full = 304 pas_range_size(left->range) == left->num_live_bytes && 305 pas_range_size(right->range) == right->num_live_bytes; 306 307 if (!both_empty && !both_full) 308 return false; 309 310 /* Right now: both sides have identical commit states and identical synchronization styes. And 311 either both sides are empty or both sides are full. 312 313 The only reason why we wouldn't want to coalesce is if epochs didn't match. But that only 314 matters when the memory is free and committed. */ 315 316 if (!left->is_committed) 317 return true; 318 319 if (both_full) 320 return true; 321 322 return left->use_epoch == right->use_epoch; 297 323 } 298 324 -
trunk/Source/bmalloc/libpas/src/libpas/pas_status_reporter.c
r282556 r283178 46 46 #include "pas_large_heap.h" 47 47 #include "pas_large_map.h" 48 #include "pas_large_sharing_pool.h" 48 49 #include "pas_large_utility_free_heap.h" 49 50 #include "pas_log.h" … … 197 198 pas_stream* stream, pas_bitfit_directory* directory) 198 199 { 200 if (directory->config_kind == pas_bitfit_page_config_kind_null) 201 return; 202 199 203 pas_stream_printf(stream, " %s Global Dir (%p): ", 200 204 pas_bitfit_page_config_variant_get_capitalized_string( … … 643 647 pas_heap_summary_dump(pas_all_heaps_compute_total_non_utility_large_summary(), stream); 644 648 pas_stream_printf(stream, "\n"); 649 } 650 651 static bool dump_large_sharing_pool_node_callback(pas_large_sharing_node* node, 652 void* arg) 653 { 654 pas_stream* stream; 655 656 stream = arg; 657 658 pas_stream_printf(stream, " %p...%p: %s, %zu/%zu live (%.0lf%%), %llu", 659 (void*)node->range.begin, 660 (void*)node->range.end, 661 pas_commit_mode_get_string(node->is_committed), 662 node->num_live_bytes, 663 pas_range_size(node->range), 664 100. * (double)node->num_live_bytes / (double)pas_range_size(node->range), 665 node->use_epoch); 666 667 if (node->synchronization_style != pas_physical_memory_is_locked_by_virtual_range_common_lock) { 668 pas_stream_printf( 669 stream, ", %s", 670 pas_physical_memory_synchronization_style_get_string(node->synchronization_style)); 671 } 672 673 pas_stream_printf(stream, "\n"); 674 675 return true; 676 } 677 678 void pas_status_reporter_dump_large_sharing_pool(pas_stream* stream) 679 { 680 pas_stream_printf(stream, " Large sharing pool contents:\n"); 681 pas_large_sharing_pool_for_each(dump_large_sharing_pool_node_callback, stream, pas_lock_is_held); 645 682 } 646 683 … … 959 996 pas_status_reporter_dump_all_shared_page_directories(stream); 960 997 pas_status_reporter_dump_all_heaps_non_utility_summaries(stream); 998 999 if (pas_status_reporter_enabled >= 3) 1000 pas_status_reporter_dump_large_sharing_pool(stream); 1001 961 1002 pas_status_reporter_dump_utility_heap(stream); 962 1003 -
trunk/Source/bmalloc/libpas/src/libpas/pas_status_reporter.h
r282556 r283178 1 1 /* 2 * Copyright (c) 2019-202 0Apple Inc. All rights reserved.2 * Copyright (c) 2019-2021 Apple Inc. All rights reserved. 3 3 * 4 4 * Redistribution and use in source and binary forms, with or without … … 69 69 PAS_API void pas_status_reporter_dump_all_shared_page_directories(pas_stream* stream); 70 70 PAS_API void pas_status_reporter_dump_all_heaps_non_utility_summaries(pas_stream* stream); 71 PAS_API void pas_status_reporter_dump_large_sharing_pool(pas_stream* stream); 71 72 PAS_API void pas_status_reporter_dump_utility_heap(pas_stream* stream); 72 73 PAS_API void pas_status_reporter_dump_total_fragmentation(pas_stream* stream); -
trunk/Source/bmalloc/libpas/src/test/LargeSharingPoolDump.cpp
r279867 r283178 50 50 } 51 51 52 vector<pas_large_sharing_node*> largeSharingPoolAsVector() 53 { 54 vector<pas_large_sharing_node*> result; 55 forEachLargeSharingPoolNode([&] (pas_large_sharing_node* node) -> bool { 56 result.push_back(node); 57 return true; 58 }); 59 return result; 60 } 61 52 62 void dumpLargeSharingPool() 53 63 { -
trunk/Source/bmalloc/libpas/src/test/LargeSharingPoolDump.h
r279867 r283178 32 32 #include <functional> 33 33 #include "pas_large_sharing_pool.h" 34 #include <vector> 34 35 35 36 void forEachLargeSharingPoolNode(std::function<bool(pas_large_sharing_node*)> visitor); 37 std::vector<pas_large_sharing_node*> largeSharingPoolAsVector(); 36 38 void dumpLargeSharingPool(); 37 39 -
trunk/Source/bmalloc/libpas/src/test/LargeSharingPoolTests.cpp
r279867 r283178 1 1 /* 2 * Copyright (c) 2018-20 19Apple Inc. All rights reserved.2 * Copyright (c) 2018-2021 Apple Inc. All rights reserved. 3 3 * 4 4 * Redistribution and use in source and binary forms, with or without … … 57 57 { 58 58 } 59 60 explicit Range(pas_large_sharing_node* node) 61 : begin(node->range.begin) 62 , end(node->range.end) 63 , isCommitted(node->is_committed) 64 , numLiveBytes(node->num_live_bytes) 65 , epoch(node->use_epoch) 66 { 67 } 68 69 bool operator==(Range other) const 70 { 71 return begin == other.begin 72 && end == other.end 73 && isCommitted == other.isCommitted 74 && numLiveBytes == other.numLiveBytes 75 && epoch == other.epoch; 76 } 77 78 bool operator!=(Range other) const { return !(*this == other); } 59 79 60 80 uintptr_t begin; … … 65 85 }; 66 86 87 ostream& operator<<(ostream& out, Range range) 88 { 89 out << reinterpret_cast<void*>(range.begin) << "..." << reinterpret_cast<void*>(range.end) 90 << ", " << (range.isCommitted ? "committed" : "decommitted") << ", " 91 << range.numLiveBytes << "/" << (range.end - range.begin) << ", " << range.epoch; 92 return out; 93 } 94 67 95 void assertState(const vector<Range>& ranges) 68 96 { 69 size_t index = 0; 70 forEachLargeSharingPoolNode( 71 [&] (pas_large_sharing_node* node) -> bool { 72 CHECK_LESS(index, ranges.size()); 73 Range expectedRange = ranges[index++]; 74 CHECK_EQUAL(node->range.begin, expectedRange.begin); 75 CHECK_EQUAL(node->range.end, expectedRange.end); 76 CHECK_EQUAL(node->is_committed, expectedRange.isCommitted); 77 CHECK_EQUAL(node->num_live_bytes, expectedRange.numLiveBytes); 78 CHECK_EQUAL(node->use_epoch, expectedRange.epoch); 79 return true; 80 }); 81 CHECK_EQUAL(index, ranges.size()); 97 vector<pas_large_sharing_node*> nodes = largeSharingPoolAsVector(); 98 99 bool allGood = true; 100 101 if (nodes.size() != ranges.size()) { 102 cout << "State does not match because we expected " << ranges.size() << " ranges but got " 103 << nodes.size() << " ranges.\n"; 104 allGood = false; 105 } else { 106 for (size_t index = 0; index < nodes.size(); ++index) { 107 pas_large_sharing_node* node = nodes[index]; 108 Range actualRange(node); 109 Range expectedRange = ranges[index]; 110 if (expectedRange != actualRange) { 111 cout << "State does not match at index " << index << ": expected:\n" 112 << " " << expectedRange << ", but got:\n" 113 << " " << actualRange << "\n"; 114 allGood = false; 115 } 116 } 117 } 118 119 if (!allGood) { 120 cout << "Got mismatch in states. Expected the state to be:\n"; 121 for (Range range : ranges) 122 cout << " " << range << "\n"; 123 cout << "But got:\n"; 124 for (pas_large_sharing_node* node : nodes) 125 cout << " " << Range(node) << "\n"; 126 } 127 128 CHECK(allGood); 82 129 } 83 130 84 void test BadCoalesceEpochUpdate()131 void testGoodCoalesceEpochUpdate() 85 132 { 86 133 static constexpr bool verbose = false; … … 124 171 dumpLargeSharingPool(); 125 172 126 assertState({ Range(0, 10 * PG, pas_committed, 10 * PG, 0), 127 Range(10 * PG, 30 * PG, pas_committed, 20 * PG, 3), 128 Range(30 * PG, END, pas_committed, END - 30 * PG, 0) }); 129 173 assertState({ Range(0, END, pas_committed, END, 3) }); 130 174 } 131 175 … … 139 183 EpochIsCounter epochIsCounter; 140 184 141 ADD_TEST(test BadCoalesceEpochUpdate());185 ADD_TEST(testGoodCoalesceEpochUpdate()); 142 186 #endif // TLC 143 187 }
Note: See TracChangeset
for help on using the changeset viewer.