Hey lazyweb, how do you debug memory leaks in C++?
I’ve hit on a really, really horrible solution, and I’d like an opinion on what I can do instead. All of my objects in my project are subclasses of this class:
typedef const char *ClassName;
struct live_object {
ClassName isa;
utime_t created;
};
std::map<void *, live_object> *liveObjects();
class base {
private:
const char *isa;
static void __a(base *ptr) {
std::map<void *, live_object> *objs = liveObjects();
std::map<void *, live_object>::iterator it = objs->find(ptr);
if (it != objs->end())
(*it).second.isa = ptr->isa;
}
static void __d(base *ptr) {
std::map<void *, live_object> *objs = liveObjects();
std::map<void *, live_object>::iterator it = objs->find(ptr);
if (it != objs->end()) {
log deletion of a class
objs->erase(it);
}
}
public:
base(ClassName isa) : isa(isa) { __r(this); }
base() : isa("UNKNOWN CLASS") { __r(this); }
virtual ~base() { __d(this); }
static void *operator new(size_t sz) throw(std::bad_alloc) {
void *ptr = malloc(sz);
live_object o;
o.created = now();
std::map<void *, live_object> *objs = liveObjects();
(*objs)[ptr] = o;
return ptr;
}
};
And yes, every subclass of this needs to call base("class-name") in the constructor, to get a meaningful name for the class.
I have a periodic event that dumps out all live, dynamically-allocated objects, along with their class name and how long they’ve been alive. It looks like this:
-- BEGIN DUMP OF LIVE OBJECTS --
0x8090d60 -- isa: comms::MethodCall alive for 1200253ms
0x8090ec0 -- isa: util::Authenticator alive for 1200253ms
0x8091f30 -- isa: UNKNOWN CLASS alive for 22017ms
0x8099928 -- isa: UNKNOWN CLASS alive for 19969ms
0x80a4608 -- isa: util::Event alive for 1140090ms
0x80a59c0 -- isa: UNKNOWN CLASS alive for 22017ms
0x80be650 -- isa: HealthReporter alive for 1155229ms
0x80bf608 -- isa: UNKNOWN CLASS alive for 19969ms
0x80e3068 -- isa: UNKNOWN CLASS alive for 19969ms
0x80e6060 -- isa: UNKNOWN CLASS alive for 19969ms
0x80e8550 -- isa: UNKNOWN CLASS alive for 19969ms
-- END DUMP OF LIVE OBJECTS --
Now, this may be useful, but it’s such a pain in the ass to get working; there’s got to be a better way, hasn’t there?
Update: that was easy, I suppose. Valgrind on x86 is awesome, though it gives me a ton of errors I can do nothing about. It turns out I was using a library in the wrong — but, kind of the obvious — way, which was leaking a tiny bit of memory. The program I’m working on is awesome, by the way: it runs lots of different things in one thread, something I’ve wanted to do ever since I’ve had to debug multithreaded programs, but never got the chance to do it right until now.

Loading...
Lazyweb | 22-May-07 at 7:36 pm | Permalink
Go go Gadget Valgrind!
fr3@K | 22-May-07 at 7:39 pm | Permalink
Inheritance is really not the bast approach to tackle this problem. Inheritance is the single feature that introduces highest coupling possible in C++, which is not necessary in this case. Your usage dose not map to `Is-A’ relationships.
There is a heap tracking mechanism in reSIProcate. It’s implemented as macros. Instead of inheritance, it inserts code in class definitions those needed to be tracked. It may not be the best practice, but probably better than yours. You can find macro definitions in this header and look for RESIP_HeapCount in the class definition of class Data for usage.
Cheers
fr3@K | 22-May-07 at 7:40 pm | Permalink
Header file for class Data.
csm | 22-May-07 at 7:51 pm | Permalink
No valgrind love for ARM, but I also have an x86 system to test this on.
csm | 22-May-07 at 7:59 pm | Permalink
It’s also apparently none of my objects being leaked, so code-based solutions will likely lead nowhere :-/
daney | 22-May-07 at 10:22 pm | Permalink
Boehm GC can be used to detect memory leaks. We have done it on MIPS, it probably works on ARM as well.
csm | 22-May-07 at 10:32 pm | Permalink
I’ve looked at Boehm a little. I tried using a GC’d object as the root of my objects, and it seemed to detect that a leak was happening, but it doesn’t give much info (other than “yup, you’re leaking memory”), and segfaults too easily.
I haven’t tried LD_PRELOAD’ing libgc in yet.
The ARM system I’m working with has terrible backtrace support, too. Even getting gdb to work is proving difficult.
I’m also stack-allocating (or whatever it’s called in C++) lots and lots of things (most things, actually; I heap-allocate only what I have to). I don’t know how Boehm handles that.
csm | 22-May-07 at 10:33 pm | Permalink
Also, fuck wordpress. Adding a comment deletes the tags of this post!
Hugo Vincent | 23-May-07 at 12:51 am | Permalink
Although I don’t know what’s going on in your code (can’t really see why it’s necessary), I can offer you this: Ever since I started aggressively using TR1 shared pointers (usable in g++ with #include
as std::tr1::shared_ptr — these will be in the official C++ standard library in C++0x) I have had only a few (simple and easily solved) memory leaks. See http://libpapyrus.sourceforge.net/ for a nice example of their use.
Finally: I know you can’t run it on ARM, but if you can (cross-)compile your code for x86 Linux you’ll get the benefit of Valgrind, not to mention complete gdb support, strace, and all the other awesome open source debugging tools the community has created over the years. When I’m doing embedded development, I find it indispensable that my code (as much as possible) can be compiled and tested on x86. The compile-test-debug cycle is just so much faster, and even if it means writing test harnesses or whatever to emulate any hardware you might be talking to, you still end up with a much more productive development environment.
Good luck!
csm | 23-May-07 at 6:38 am | Permalink
I’m just a reforming Java nerd, and am too much in love with reflection and other debugging aids.
I can’t agree more that debugging on x86 is much easier.
Tom Tromey | 23-May-07 at 12:13 pm | Permalink
The Boehm GC handles stack allocated objects just fine. I haven’t used valgrind for leak checking much, mostly for other memory checks — great tool, definitely use it.
When writing C++ I generally try to design my programs to be leak free by using smart pointers and the like. But it sounds like you are using some 3rd party code so this approach may not work out so well.
csm | 27-May-07 at 1:23 pm | Permalink
Yeah, TinyXML++ has some weird object ownership rules. I finally figured out how to use it right.
I do indeed love smart pointers and stack allocation; it’s what makes C++ bearable.