Permalink
Browse files

Add functions to help debug object leaks

  • Loading branch information...
1 parent 4b89e72 commit 3671d9e1caec89460507cf40be6fe3a50e96b329 Evan Phoenix committed Apr 2, 2010
Showing with 294 additions and 8 deletions.
  1. +16 −0 .gdbinit
  2. +1 −1 vm/builtin/object.cpp
  3. +108 −6 vm/gc/marksweep.cpp
  4. +4 −0 vm/gc/marksweep.hpp
  5. +158 −1 vm/objectmemory.cpp
  6. +7 −0 vm/objectmemory.hpp
View
@@ -17,3 +17,19 @@ end
define validate
p rubinius::VM::current_state()->om->validate_object($arg0)
end
+
+define refs
+p print_references($arg0)
+end
+
+define memstat
+p x_memstat()
+end
+
+define snapshot
+p x_snapshot()
+end
+
+define print_snapshot
+p x_print_snapshot()
+end
View
@@ -375,7 +375,7 @@ namespace rubinius {
if(id->nil_p()) {
/* All references have an even object_id. last_object_id starts out at 0
* but we don't want to use 0 as an object_id, so we just add before using */
- id = Fixnum::from(state->om->last_object_id += 2);
+ id = Fixnum::from(++state->om->last_object_id << 1);
set_ivar(state, G(sym_object_id), id);
}
View
@@ -5,20 +5,26 @@
#include "object_utils.hpp"
#include "builtin/tuple.hpp"
+#include "builtin/string.hpp"
+#include "builtin/class.hpp"
+#include "builtin/symbol.hpp"
#include "instruments/stats.hpp"
#include <iostream>
+#include <algorithm>
namespace rubinius {
MarkSweepGC::MarkSweepGC(ObjectMemory *om)
- :GarbageCollector(om) {
- allocated_objects = 0;
- allocated_bytes = 0;
- next_collection_bytes = MS_COLLECTION_BYTES;
- free_entries = true;
- }
+ : GarbageCollector(om)
+ , allocated_bytes(0)
+ , allocated_objects(0)
+ , next_collection_bytes(MS_COLLECTION_BYTES)
+ , free_entries(true)
+ , times_collected(0)
+ , last_freed(0)
+ {}
MarkSweepGC::~MarkSweepGC() { }
@@ -69,8 +75,11 @@ namespace rubinius {
delete_object(obj);
}
+ last_freed++;
+
allocated_objects--;
allocated_bytes -= obj->size_in_bytes(object_memory_->state);
+ obj->set_zone(UnspecifiedZone);
#ifdef USE_DLMALLOC
malloc_.release(reinterpret_cast<void*>(obj));
@@ -131,6 +140,9 @@ namespace rubinius {
}
void MarkSweepGC::after_marked() {
+ times_collected++;
+ last_freed = 0;
+
// Cleanup all weakrefs seen
clean_weakrefs();
@@ -152,6 +164,96 @@ namespace rubinius {
}
}
+ /*
+ static bool sort_by_size(Object* a, Object* b) {
+ STATE = rubinius::VM::current_state();
+ size_t a_size = a->size_in_bytes(state);
+ size_t b_size = b->size_in_bytes(state);
+
+ return b_size < a_size;
+ }
+ */
+
+ struct PerClass {
+ int objects;
+ int bytes;
+
+ PerClass()
+ : objects(0)
+ , bytes(0)
+ {}
+ };
+
+ void MarkSweepGC::profile() {
+
+ std::map<Class*, PerClass> stats;
+
+ for(std::list<Object*>::iterator i = entries.begin();
+ i != entries.end();
+ i++) {
+ Object* obj = *i;
+ Class* cls = obj->class_object(object_memory_->state);
+
+ std::map<Class*,PerClass>::iterator i = stats.find(cls);
+ if(i == stats.end()) {
+ PerClass pc;
+ pc.objects++;
+ pc.bytes += obj->size_in_bytes(object_memory_->state);
+
+ stats[cls] = pc;
+ } else {
+ i->second.objects++;
+ i->second.bytes += obj->size_in_bytes(object_memory_->state);
+ }
+ }
+
+ std::cout << stats.size() << " classes:\n";
+
+ for(std::map<Class*,PerClass>::iterator i = stats.begin();
+ i != stats.end();
+ i++) {
+ std::cout << i->first->name()->c_str(object_memory_->state) << "\n"
+ << " objects: " << i->second.objects << "\n"
+ << " bytes: " << i->second.bytes << "\n";
+ }
+
+ /*
+ int count = 0;
+
+ for(std::list<Object*>::reverse_iterator i = entries.rbegin();
+ i != entries.rend();
+ i++) {
+ Object* obj = *i;
+ if(ByteArray* ba = try_as<ByteArray>(obj)) {
+ ba->show(object_memory_->state);
+ if(++count == 10) break;
+ }
+ }
+ */
+
+ /*
+ std::list<Object*> sorted = entries;
+ sorted.sort(sort_by_size);
+
+ std::list<Object*>::iterator i;
+
+ std::cout << "Top 30:\n";
+
+ int count = 0;
+
+ for(i = sorted.begin(); i != sorted.end();) {
+ Object* obj = *i;
+
+ size_t sz = obj->size_in_bytes(object_memory_->state);
+
+ std::cout << obj->to_s(object_memory_->state, true)->c_str() << " bytes=" << sz << "\n";
+ if(++count == 30) break;
+
+ i++;
+ }
+ */
+ }
+
ObjectPosition MarkSweepGC::validate_object(Object* obj) {
std::list<Object*>::iterator i;
View
@@ -41,6 +41,8 @@ namespace rubinius {
size_t allocated_objects;
int next_collection_bytes;
bool free_entries;
+ int times_collected;
+ int last_freed;
/* Prototypes */
@@ -55,6 +57,8 @@ namespace rubinius {
void collect(Roots &roots, CallFrameLocationList& call_frame);
void after_marked();
+ void profile();
+
ObjectPosition validate_object(Object* obj);
};
};
View
@@ -7,6 +7,7 @@
#include "gc/baker.hpp"
#include "gc/immix.hpp"
#include "gc/inflated_headers.hpp"
+#include "gc/walker.hpp"
#include "config_parser.hpp"
@@ -490,7 +491,10 @@ namespace rubinius {
std::cout << "baker: " << baker << "\n";
std::cout << "immix: " << immix << "\n";
- std::cout << "large: " << large << "\n";
+ std::cout << "large: " << large << "\n"
+ << " objects: " << mark_sweep_->allocated_objects << "\n"
+ << " times: " << mark_sweep_->times_collected << "\n"
+ << " last_freed: " << mark_sweep_->last_freed << "\n";
std::cout << " code: " << code << "\n";
std::cout << "shared: " << shared << "\n";
@@ -502,13 +506,166 @@ namespace rubinius {
std::cout << " total allocated: " << code_manager_.total_allocated() << "\n";
std::cout << " total freed: " << code_manager_.total_freed() << "\n";
}
+
+ class RefererFinder : public GarbageCollector {
+ Object* target_;
+ bool found_;
+
+ public:
+ RefererFinder(ObjectMemory* om, Object* obj)
+ : GarbageCollector(om)
+ , target_(obj)
+ , found_(false)
+ {}
+
+ virtual Object* saw_object(Object* obj) {
+ if(obj == target_) {
+ found_ = true;
+ }
+
+ return obj;
+ }
+
+ void reset() {
+ found_ = false;
+ }
+
+ bool found_p() {
+ return found_;
+ }
+ };
+
+ void ObjectMemory::find_referers(Object* target, ObjectArray& result) {
+ ObjectMemory::GCInhibit inhibitor(state->om);
+
+ ObjectWalker walker(state->om);
+ GCData gc_data(state);
+
+ // Seed it with the root objects.
+ walker.seed(gc_data);
+
+ Object* obj = walker.next();
+
+ RefererFinder rf(this, target);
+
+ while(obj) {
+ rf.reset();
+
+ rf.scan_object(obj);
+
+ if(rf.found_p()) {
+ result.push_back(obj);
+ }
+
+ obj = walker.next();
+ }
+ }
+
+ void ObjectMemory::snapshot() {
+ // Assign all objects an object id...
+ ObjectMemory::GCInhibit inhibitor(state->om);
+
+ // Walk the heap over and over until we don't create
+ // any more objects...
+
+ size_t last_seen = 0;
+
+ while(last_object_id != last_seen) {
+ last_seen = last_object_id;
+
+ ObjectWalker walker(state->om);
+ GCData gc_data(state);
+
+ // Seed it with the root objects.
+ walker.seed(gc_data);
+
+ Object* obj = walker.next();
+
+ while(obj) {
+ obj->id(state);
+ obj = walker.next();
+ }
+ }
+
+ // Now, save the current value of last_object_id, since thats
+ // so we can compare later to find all new objects.
+ last_snapshot_id = last_object_id;
+
+ std::cout << "Snapshot taken: " << last_snapshot_id << "\n";
+ }
+
+ void ObjectMemory::print_new_since_snapshot() {
+ // Assign all objects an object id...
+ ObjectMemory::GCInhibit inhibitor(state->om);
+
+ ObjectWalker walker(state->om);
+ GCData gc_data(state);
+
+ // Seed it with the root objects.
+ walker.seed(gc_data);
+
+ Object* obj = walker.next();
+
+ // All reference ids are shifted up
+ native_int check_id = (native_int)last_snapshot_id << 1;
+
+ int count = 0;
+ int bytes = 0;
+
+ while(obj) {
+ if(!obj->has_id(state) || obj->id(state)->to_native() > check_id) {
+ count++;
+ bytes += obj->size_in_bytes(state);
+
+ if(kind_of<String>(obj)) {
+ std::cout << "#<String:" << obj << ">\n";
+ } else {
+ std::cout << obj->to_s(state, true)->c_str() << "\n";
+ }
+ }
+
+ obj = walker.next();
+ }
+
+ std::cout << count << " objects since snapshot.\n";
+ std::cout << bytes << " bytes since snapshot.\n";
+ }
+
+ void ObjectMemory::print_references(Object* obj) {
+ ObjectArray ary;
+
+ find_referers(obj, ary);
+
+ int count = 0;
+
+ std::cout << ary.size() << " total references:\n";
+ for(ObjectArray::iterator i = ary.begin();
+ i != ary.end();
+ i++) {
+ std::cout << " " << (*i)->to_s(state, true)->c_str() << "\n";
+
+ if(++count == 100) break;
+ }
+ }
};
// Used in gdb
void x_memstat() {
rubinius::VM::current_state()->om->memstats();
}
+void print_references(void* obj) {
+ rubinius::VM::current_state()->om->print_references((rubinius::Object*)obj);
+}
+
+void x_snapshot() {
+ rubinius::VM::current_state()->om->snapshot();
+}
+
+void x_print_snapshot() {
+ rubinius::VM::current_state()->om->print_new_since_snapshot();
+}
+
#define DEFAULT_MALLOC_THRESHOLD 10000000
static long bytes_until_collection = DEFAULT_MALLOC_THRESHOLD;
Oops, something went wrong.

0 comments on commit 3671d9e

Please sign in to comment.