auto_gdb_interface.cpp [plain text]
#include <vector>
#include <deque>
#include "auto_gdb_interface.h"
#include "auto_impl_utilities.h"
#include "Zone.h"
#include "BlockIterator.h"
#include "ReferenceIterator.h"
namespace Auto {
template <typename ReferenceIterator> class GDBPendingStack {
typedef std::vector<uintptr_t, AuxAllocator<uintptr_t> > uintptr_vector;
uintptr_vector _small_stack, _large_stack;
public:
const PendingStackHint hints() { return PendingStackWantsEagerScanning; }
void push(Subzone *subzone, usword_t q) {
assert(q <= 65536);
assert(uintptr_t(subzone) == (uintptr_t(subzone) & ~0x1FFFF));
_small_stack.push_back(uintptr_t(subzone) | q); }
void push(Large *large) {
_large_stack.push_back(uintptr_t(large));
}
static bool mark(Subzone *subzone, usword_t q) { return subzone->test_and_set_mark(q); }
static bool mark(Large *large) { return large->test_and_set_mark(); }
static bool is_marked(Subzone *subzone, usword_t q) { return subzone->is_marked(q); }
static bool is_marked(Large *large) { return large->is_marked(); }
void scan(ReferenceIterator &scanner) {
for (;;) {
if (_small_stack.size()) {
uintptr_t back = _small_stack.back();
_small_stack.pop_back();
Subzone *subzone = reinterpret_cast<Subzone *>(back & ~0x1FFFF);
usword_t q = back & 0x1FFFF;
scanner.scan(subzone, q);
} else if (_large_stack.size()) {
Large *large = reinterpret_cast<Large*>(_large_stack.back());
_large_stack.pop_back();
scanner.scan(large);
} else {
return;
}
}
}
template <typename U> struct rebind { typedef GDBPendingStack<U> other; };
};
template <typename ReferenceIterator> class GDBScanningStrategy : public FullScanningStrategy<ReferenceIterator> {
public:
template <typename U> struct rebind { typedef GDBScanningStrategy<U> other; };
};
typedef std::vector<auto_memory_reference_t, AuxAllocator<auto_memory_reference_t> > RefVector;
class GDBReferenceRecorder {
private:
Zone *_zone;
void *_block;
void *_stack_bottom;
RefVector _refs;
struct Configuration;
typedef ReferenceIterator<Configuration> GDBReferenceIterator;
struct Configuration {
typedef GDBReferenceRecorder ReferenceVisitor;
typedef GDBPendingStack<GDBReferenceIterator> PendingStack;
typedef GDBScanningStrategy<GDBReferenceIterator> ScanningStrategy;
};
public:
GDBReferenceRecorder(Zone *zone, void *block, void *stack_bottom) : _zone(zone), _block(block), _stack_bottom(stack_bottom) {}
void visit(const ReferenceInfo &info, void **slot, void *block) {
if (block == _block) {
auto_memory_reference_t ref = { NULL };
switch (info.kind()) {
case kRootReference:
ref.address = slot;
ref.offset = 0;
ref.kind = auto_memory_block_global;
break;
case kStackReference:
ref.address = info.thread().stack_base();
ref.offset = (intptr_t)slot - (intptr_t)ref.address;
ref.kind = auto_memory_block_stack;
break;
case kConservativeHeapReference:
case kAllPointersHeapReference:
ref.address = _zone->block_start((void*)slot);
ref.offset = (intptr_t)slot - (intptr_t)ref.address;
ref.kind = auto_memory_block_bytes;
ref.retainCount = auto_zone_retain_count((auto_zone_t *)_zone,ref.address);
break;
case kExactHeapReference:
ref.address = _zone->block_start((void*)slot);
ref.offset = (intptr_t)slot - (intptr_t)ref.address;
ref.kind = auto_memory_block_object;
ref.retainCount = auto_zone_retain_count((auto_zone_t *)_zone,ref.address);
break;
case kAssociativeReference:
ref.address = info.object();
ref.offset = (intptr_t)info.key();
ref.kind = auto_memory_block_association;
ref.retainCount = auto_zone_retain_count((auto_zone_t *)_zone,ref.address);
break;
default:
break;
}
if (ref.address) _refs.push_back(ref);
}
}
void visit(const ReferenceInfo &info, void **slot, Subzone *subzone, usword_t q) {
visit(info, slot, subzone->quantum_address(q));
}
void visit(const ReferenceInfo &info, void **slot, Large *large) {
visit(info, slot, large->address());
}
void scan() {
Configuration::PendingStack stack;
GDBReferenceIterator scanner(_zone, *this, stack, _stack_bottom);
scanner.scan();
}
auto_memory_reference_list_t *copy_refs() {
uint32_t count = _refs.size();
auto_memory_reference_list_t *result = (auto_memory_reference_list_t *) aux_malloc(sizeof(auto_memory_reference_list_t) + count * sizeof(auto_memory_reference_t));
result->count = count;
std::copy(_refs.begin(), _refs.end(), result->references);
return result;
}
};
class GDBRootFinder {
private:
struct Configuration;
typedef ReferenceIterator<Configuration> GDBRootIterator;
struct Configuration {
typedef GDBRootFinder ReferenceVisitor;
typedef GDBPendingStack<GDBRootIterator> PendingStack;
typedef GDBScanningStrategy<GDBRootIterator> ScanningStrategy;
};
struct Node;
typedef std::vector<Node *, AuxAllocator<Node *> > NodeVector;
typedef std::deque<Node *, AuxAllocator<Node *> > NodeQueue;
typedef __gnu_cxx::hash_map<void *, Node*, AuxPointerHash, AuxPointerEqual, AuxAllocator<void *> > NodeSet;
typedef __gnu_cxx::hash_map<Node *, auto_memory_reference_t, AuxPointerHash, AuxPointerEqual, AuxAllocator<Node *> > NodeRefMap;
struct Node {
void *_address; Node *_target; NodeRefMap _references; enum Color { White = 0, Gray, Black }; Color _color : 2;
char _is_thread_local : 1; char _is_retained : 1;
Node(void *address) : _address(address), _target(NULL), _references(), _color(White), _is_thread_local(0), _is_retained(0) {}
Color darken() { if (_color < Black) _color = (Color)(_color + 1); return _color; }
void addRef(Zone *zone, Node *slotNode, const ReferenceInfo &info, void **slot) {
if (_references.find(slotNode) == _references.end()) {
auto_memory_reference_t ref = { NULL };
switch (info.kind()) {
case kRootReference:
ref.address = slotNode->_address;
ref.offset = 0;
ref.kind = auto_memory_block_global;
break;
case kStackReference:
ref.address = info.thread().stack_base();
ref.offset = (intptr_t)slot - (intptr_t)ref.address;
ref.kind = auto_memory_block_stack;
break;
case kConservativeHeapReference:
case kAllPointersHeapReference:
ref.address = slotNode->_address;
ref.offset = (intptr_t)slot - (intptr_t)ref.address;
ref.kind = auto_memory_block_bytes;
ref.retainCount = auto_zone_retain_count((auto_zone_t *)zone,ref.address);
break;
case kExactHeapReference:
ref.address = slotNode->_address;
ref.offset = (intptr_t)slot - (intptr_t)ref.address;
ref.kind = auto_memory_block_object;
ref.retainCount = auto_zone_retain_count((auto_zone_t *)zone,ref.address);
break;
case kAssociativeReference:
ref.address = slotNode->_address;
ref.offset = (intptr_t)info.key();
ref.kind = auto_memory_block_association;
ref.retainCount = auto_zone_retain_count((auto_zone_t *)zone,ref.address);
break;
default:
return;
}
_references[slotNode] = ref;
}
}
typedef void (^ref_visitor_t) (Node *targetNode, Node* slotNode, auto_memory_reference_t &ref);
void visitRefs(ref_visitor_t visitor) {
NodeQueue queue;
queue.push_back(this);
this->_color = Black;
while (!queue.empty()) {
Node *node = queue.front();
queue.pop_front();
for (NodeRefMap::iterator i = node->_references.begin(), end = node->_references.end(); i != end; ++i) {
Node *child = i->first;
if (child->_color == White) {
child->_color = Black;
visitor(node, child, i->second);
queue.push_back(child);
}
}
}
}
};
Zone *_zone;
Node *_blockNode;
auto_memory_reference_t _blockRef;
void *_stack_bottom;
NodeSet _nodes;
NodeSet _nodesToExplore;
public:
GDBRootFinder(Zone *zone, void *block, void* stack_bottom) : _zone(zone), _blockNode(new Node(block)), _stack_bottom(stack_bottom) {
_nodes[_blockNode->_address] = _blockNode;
_nodesToExplore[_blockNode->_address] = _blockNode;
auto_block_info_sieve<AUTO_BLOCK_INFO_REFCOUNT|AUTO_BLOCK_INFO_LAYOUT> block_info(_zone, _blockNode->_address);
_blockRef = (auto_memory_reference_t) { _blockNode->_address, 0, is_object(block_info.layout()) ? auto_memory_block_object : auto_memory_block_bytes, block_info.refcount() };
}
~GDBRootFinder() {
for (NodeSet::iterator i = _nodes.begin(), end = _nodes.end(); i != end; ++i) {
delete i->second;
}
}
Node *nodeForSlot(const ReferenceInfo &info, void **slot) {
Node *node = NULL;
NodeSet::iterator i;
switch (info.kind()) {
case kRootReference:
case kStackReference:
i = _nodes.find(slot);
if (i != _nodes.end()) {
node = i->second;
} else {
node = new Node(slot);
_nodes[slot] = node;
}
break;
case kAssociativeReference:
i = _nodes.find(slot);
if (i != _nodes.end()) {
node = i->second;
} else {
node = new Node(slot);
_nodes[slot] = node;
_nodesToExplore[slot] = node;
}
break;
case kConservativeHeapReference:
case kAllPointersHeapReference:
case kExactHeapReference:
{
void *start = _zone->block_start(slot);
i = _nodes.find(start);
if (i != _nodes.end()) {
node = i->second;
} else {
node = new Node(start);
_nodes[start] = node;
_nodesToExplore[start] = node;
}
}
break;
default:
break;
}
return node;
}
void visit(const ReferenceInfo &info, void **slot, void *block) {
NodeSet::iterator i = _nodesToExplore.find(block);
if (i != _nodesToExplore.end()) {
Node *node = i->second;
switch (info.kind()) {
case kThreadLocalReference:
node->_is_thread_local = 1;
break;
case kRetainedReference:
node->_is_retained = 1;
break;
case kAssociativeReference:
slot = (void**)info.object();
default:
Node *slotNode = nodeForSlot(info, slot);
if (slotNode) node->addRef(_zone, slotNode, info, slot);
}
}
}
void visit(const ReferenceInfo &info, void **slot, Subzone *subzone, usword_t q) {
visit(info, slot, subzone->quantum_address(q));
}
void visit(const ReferenceInfo &info, void **slot, Large *large) {
visit(info, slot, large->address());
}
bool darkenExploredNodes() {
__block NodeVector blackNodes;
std::for_each(_nodesToExplore.begin(), _nodesToExplore.end(), ^(NodeSet::value_type &value) {
if (value.second->darken() == Node::Black) blackNodes.push_back(value.second);
});
std::for_each(blackNodes.begin(), blackNodes.end(), ^(Node *node) { _nodesToExplore.erase(node->_address); });
return (_nodesToExplore.size() != 0);
}
void scan() {
Configuration::PendingStack stack;
GDBRootIterator scanner(_zone, *this, stack, _stack_bottom);
while (darkenExploredNodes()) {
scanner.scan();
_zone->reset_all_marks();
}
std::for_each(_nodes.begin(), _nodes.end(), ^(NodeSet::value_type &value) { value.second->_color = Node::White; });
}
typedef std::vector<RefVector, AuxAllocator<RefVector> > PathsVector;
void addPath(PathsVector &paths, Node *rootNode) {
paths.resize(paths.size() + 1);
RefVector &path = paths.back();
Node *slotNode = rootNode;
while (slotNode != _blockNode) {
Node *targetNode = slotNode->_target;
assert(targetNode != NULL);
auto_memory_reference_t &ref = targetNode->_references[slotNode];
path.push_back(ref);
slotNode = targetNode;
}
path.push_back(_blockRef);
}
auto_root_list_t *copy_roots() {
auto_root_list_t *result = NULL;
__block PathsVector paths;
__block bool considerThreadLocalGarbage = false;
Node::ref_visitor_t visitor = ^(Node *targetNode, Node *slotNode, auto_memory_reference_t &ref) {
assert(slotNode->_target == NULL);
slotNode->_target = targetNode;
switch (ref.kind) {
case auto_memory_block_global:
case auto_memory_block_stack:
addPath(paths, slotNode);
break;
case auto_memory_block_bytes:
case auto_memory_block_object:
if (slotNode->_is_retained) {
addPath(paths, slotNode);
} else if (considerThreadLocalGarbage && slotNode->_is_thread_local) {
addPath(paths, slotNode);
}
break;
case auto_memory_block_association:
if (!_zone->is_block(slotNode->_address) || slotNode->_is_retained) {
addPath(paths, slotNode);
}
break;
default:
break;
}
};
_blockNode->visitRefs(visitor);
if (_blockRef.retainCount) addPath(paths, _blockNode);
if (paths.empty()) {
std::for_each(_nodes.begin(), _nodes.end(), ^(NodeSet::value_type &value) {
value.second->_color = Node::White;
value.second->_target = NULL;
});
considerThreadLocalGarbage = true;
_blockNode->visitRefs(visitor);
}
size_t count = paths.size();
size_t list_size = sizeof(auto_root_list_t) + count * sizeof(auto_memory_reference_list_t);
for (usword_t i = 0; i < count; i++) list_size += paths[i].size() * sizeof(auto_memory_reference_t);
result = (auto_root_list_t *)aux_malloc(list_size);
result->count = count;
auto_memory_reference_list_t *list = result->roots;
for (usword_t i = 0; i < count; i++) {
const RefVector &refs = paths[i];
list->count = refs.size();
std::copy(refs.begin(), refs.end(), list->references);
list = (auto_memory_reference_list_t *)displace(list, sizeof(auto_root_list_t) + list->count * sizeof(auto_memory_reference_t));
}
return result;
}
};
};
using namespace Auto;
struct MallocLoggerInhibitor {
malloc_logger_t *_old_logger;
static void inhibited_logger(uint32_t, uintptr_t, uintptr_t, uintptr_t, uintptr_t, uint32_t) {}
MallocLoggerInhibitor() : _old_logger(malloc_logger) { if (_old_logger) malloc_logger = &inhibited_logger; }
~MallocLoggerInhibitor() { if (_old_logger) malloc_logger = _old_logger; }
};
auto_memory_reference_list_t *auto_gdb_enumerate_references(auto_zone_t *zone, void *address, void *stack_base) {
auto_memory_reference_list_t *result = NULL;
Zone *azone = (Zone *)zone;
if (azone && azone->block_collector()) {
MallocLoggerInhibitor inhibitor;
GDBReferenceRecorder recorder(azone, address, stack_base);
recorder.scan();
azone->reset_all_marks();
result = recorder.copy_refs();
azone->unblock_collector();
}
return result;
}
auto_root_list_t *auto_gdb_enumerate_roots(auto_zone_t *zone, void *address, void *stack_base) {
auto_root_list_t *result = NULL;
Zone *azone = (Zone *)zone;
if (azone && azone->block_collector()) {
MallocLoggerInhibitor inhibitor;
GDBRootFinder finder(azone, address, stack_base);
finder.scan();
result = finder.copy_roots();
azone->unblock_collector();
}
return result;
}
extern "C" bool gdb_is_local(void *address) {
Zone *azone = (Zone *)auto_zone_from_pointer(address);
if (azone && azone->in_subzone_memory(address)) {
Subzone *subzone = Subzone::subzone(address);
return subzone->is_live_thread_local(subzone->quantum_index(address));
}
return false;
}
#if DEBUG
extern "C" void gdb_refs(void *address) {
auto_zone_t *zone = auto_zone_from_pointer(address);
if (zone) {
auto_memory_reference_list_t *refs = auto_gdb_enumerate_references(zone, address, (void *)auto_get_sp());
if (refs) aux_free(refs);
}
}
extern "C" void gdb_roots(void *address) {
auto_zone_t *zone = auto_zone_from_pointer(address);
if (zone) {
auto_root_list_t *roots = auto_gdb_enumerate_roots(zone, address, (void *)auto_get_sp());
if (roots) aux_free(roots);
}
}
extern "C" bool gdb_is_root(auto_zone_t *zone, void *address) {
Zone *azone = (Zone *)zone;
return azone->is_root(address);
}
struct RetainedBlocksVisitor {
Zone *_zone;
struct Configuration;
typedef ReferenceIterator<Configuration> Iterator;
struct Configuration {
typedef RetainedBlocksVisitor ReferenceVisitor;
typedef GDBPendingStack<Iterator> PendingStack;
typedef FullScanningStrategy<Iterator> ScanningStrategy;
};
RetainedBlocksVisitor(Zone *zone) : _zone(zone) {}
void visit(const ReferenceInfo &info, void **ref, Subzone *subzone, usword_t q) {
if (subzone->has_refcount(q)) {
void *block = subzone->quantum_address(q);
SubzoneBlockRef blockRef(subzone, q);
printf("small/medium block %p (sz = %ld, rc = %d)\n", block, subzone->size(q), (int)blockRef.refcount());
}
}
void visit(const ReferenceInfo &info, void **ref, Large *large) {
if (large->refcount()) {
printf("large block %p (sz = %ld, rc = %lu)\n", large->address(), large->size(), large->refcount());
} else if (info.kind() == kAssociativeReference) {
printf("large block %p associatively referenced\n", large->address());
}
}
};
extern "C" void gdb_print_retained_blocks(auto_zone_t *zone) {
Zone *azone = (Zone *)zone;
if (azone->block_collector()) {
RetainedBlocksVisitor visitor(azone);
RetainedBlocksVisitor::Configuration::PendingStack pending_stack;
RetainedBlocksVisitor::Iterator scanner(azone, visitor, pending_stack, (void *)auto_get_sp());
scanner.scan();
azone->reset_all_marks();
azone->unblock_collector();
}
}
struct NewBlocksVisitor {
struct Configuration;
typedef ReferenceIterator<Configuration> Iterator;
struct Configuration {
typedef NewBlocksVisitor ReferenceVisitor;
typedef GDBPendingStack<Iterator> PendingStack;
typedef GenerationalScanningStrategy<Iterator> ScanningStrategy;
};
size_t _small_count, _large_count;
NewBlocksVisitor() : _small_count(0), _large_count(0) {}
void visit(const ReferenceInfo &info, void **ref, Subzone *subzone, usword_t q) {
if (subzone->is_new(q)) {
++_small_count;
}
}
void visit(const ReferenceInfo &info, void **ref, Large *large) {
if (large->is_new()) {
++_large_count;
}
}
};
extern "C" void gdb_print_new_blocks(auto_zone_t *zone) {
Zone *azone = (Zone *)zone;
if (azone->block_collector()) {
NewBlocksVisitor visitor;
NewBlocksVisitor::Configuration::PendingStack pending_stack;
NewBlocksVisitor::Iterator scanner(azone, visitor, pending_stack, (void *)auto_get_sp());
scanner.scan();
printf("new blocks: %lu small/medium, %ld large\n", visitor._small_count, visitor._large_count);
azone->reset_all_marks();
azone->unblock_collector();
}
}
extern "C" void gdb_print_large_blocks(auto_zone_t *zone) {
Zone *azone = (Zone *)zone;
SpinLock lock(azone->large_lock());
if (azone->large_list()) {
printf("global large blocks:\n");
for (Large *large = azone->large_list(); large != NULL; large = large->next()) {
printf("large block %p: size = %ld, rc = %lu\n", large->address(), large->size(), large->refcount());
}
}
}
#endif