1/* 2 * Copyright (c) 2011 Apple Inc. All rights reserved. 3 * 4 * @APPLE_APACHE_LICENSE_HEADER_START@ 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 * 18 * @APPLE_APACHE_LICENSE_HEADER_END@ 19 */ 20/* 21 ZoneCollectionChecking 22 Copyright (c) 2010-2001 Apple Inc. All rights reserved. 23 */ 24 25#include "auto_zone.h" 26#include "Zone.h" 27#include "BlockIterator.h" 28#ifdef __BLOCKS__ 29#include <Block.h> 30#endif 31 32using namespace Auto; 33 34void Zone::enable_collection_checking() { 35 OSAtomicIncrement32(&_collection_checking_enabled); 36} 37 38 39void Zone::disable_collection_checking() { 40 uint32_t old_count; 41 do { 42 old_count = _collection_checking_enabled; 43 } while (old_count > 0 && !OSAtomicCompareAndSwap32(old_count, old_count-1, &_collection_checking_enabled)); 44 45 if (old_count == 1) { 46 // Reset the check counts of all blocks. 47 48 for (Region *region = region_list(); region != NULL; region = region->next()) { 49 SubzoneRangeIterator iterator(region->subzone_range()); 50 Subzone *sz; 51 for (sz = iterator.next(); sz != NULL; sz = iterator.next()) { 52 sz->reset_collection_checking(); 53 } 54 } 55 56 SpinLock lock(&_large_lock); 57 Large *large = _large_list; 58 while (large) { 59 large->set_collection_checking_count(0); 60 large = large->next(); 61 } 62 } 63} 64 65 66void Zone::track_pointer(void *pointer) { 67 assert(collection_checking_enabled()); 68 if (in_subzone_memory(pointer)) { 69 Subzone *sz = Subzone::subzone(pointer); 70 usword_t q; 71 if (sz->block_is_start(pointer, &q)) { 72 if (sz->collection_checking_count(q) == 0) { 73 sz->set_collection_checking_count(q, 1); 74 } 75 } 76 } else { 77 Large *large = block_start_large(pointer); 78 if (large && large->collection_checking_count() == 0) { 79 large->set_collection_checking_count(1); 80 } 81 } 82} 83 84 85// Clears the checking count for the blocks in the garbage list. 86void Zone::clear_garbage_checking_count(void **garbage, size_t count) { 87 for (size_t i=0; i<count; i++) { 88 void *block = garbage[i]; 89 if (in_subzone_memory(block)) { 90 Subzone *subzone = Subzone::subzone(block); 91 usword_t q = subzone->quantum_index_unchecked(block); 92 // most of the time the checking count is zero already, so don't dirty the page needlessly 93 if (subzone->collection_checking_count(q) > 0) { 94 subzone->set_collection_checking_count(q, 0); 95 } 96 } else { 97 Large *l = Large::large(block); 98 l->set_collection_checking_count(0); 99 } 100 } 101} 102 103 104// 105// checking_blocks_visitor 106// 107// checking_blocks_visitor searches for blocks with check counts that exceed the collection checking threshold. 108// Blocks which it finds are reported via report_uncollected_block(). 109// 110class update_checking_count_visitor { 111 112public: 113 114 inline bool visit(Zone *zone, Subzone *subzone, usword_t q) { 115 uint32_t count = subzone->collection_checking_count(q); 116 if (count > 0) { 117 subzone->set_collection_checking_count(q, count+1); 118 } 119 return true; 120 } 121 122 inline bool visit(Zone *zone, Large *large) { 123 uint32_t count = large->collection_checking_count(); 124 if (count > 0) { 125 large->set_collection_checking_count(count+1); 126 } 127 return true; 128 } 129}; 130 131 132void Zone::increment_check_counts() { 133 update_checking_count_visitor visitor; 134 visitAllocatedBlocks(this, visitor); 135} 136 137// 138// report_uncollected_blocks_visitor 139// 140// report_uncollected_blocks_visitor searches for blocks with check counts that exceed the collection checking threshold. 141// Blocks which it finds are reported via the callback, or just logged. 142// 143class report_uncollected_blocks_visitor { 144 auto_zone_collection_checking_callback_t _callback; 145 Thread &_thread; 146 147public: 148 149 report_uncollected_blocks_visitor(Zone *zone, auto_zone_collection_checking_callback_t callback) : _callback(callback), _thread(zone->registered_thread()) { 150 } 151 152 ~report_uncollected_blocks_visitor() { 153 } 154 155 void report_uncollected_block(Zone *zone, void *block, int32_t count) { 156 auto_memory_type_t layout = zone->block_layout(block); 157 if (!_callback) { 158 char *name; 159 bool free_name = false; 160 if ((layout & AUTO_OBJECT) == AUTO_OBJECT) { 161 if (zone->control.name_for_address) { 162 name = zone->control.name_for_address((auto_zone_t *)this, (vm_address_t)block, 0); 163 free_name = true; 164 } else { 165 name = (char *)"object"; 166 } 167 } else { 168 name = (char *)"non-object block"; 169 } 170 malloc_printf("%s %p was not collected after %d full collections\n", name, block, count-1); 171 if (free_name) free(name); 172 } else { 173 auto_zone_collection_checking_info info; 174 info.is_object = (layout & AUTO_OBJECT) == AUTO_OBJECT; 175 info.survived_count = count-1; 176 _callback(block, &info); 177 } 178 } 179 180 inline bool visit(Zone *zone, Subzone *subzone, usword_t q) { 181 uint32_t count = subzone->collection_checking_count(q); 182 if (count > 0) { 183 report_uncollected_block(zone, subzone->quantum_address(q), count); 184 } 185 return true; 186 } 187 188 inline bool visit(Zone *zone, Large *large) { 189 uint32_t count = large->collection_checking_count(); 190 if (count > 0) { 191 report_uncollected_block(zone, large->address(), count); 192 } 193 return true; 194 } 195}; 196 197void Zone::enumerate_uncollected(auto_zone_collection_checking_callback_t callback) { 198 dispatch_sync(_collection_queue, ^{ 199 report_uncollected_blocks_visitor visitor(this, callback); 200 visitAllocatedBlocks(this, visitor); 201 }); 202} 203