-----BEGIN PGP SIGNED MESSAGE----- Hash: SHA1 Hello Ian,
> Does anyone else see problems like this?
I think Jim Healy saw growth in memory quite a while back.
If you are suspecting that the memory consumption is due to C++ code
(ie Carpet other then you can try the attached memory tracer [there is
a main() function at the end to show how to use it] to tag all calls
to new/delete to the code). For Carpet I suspect that you may also
want to change the malloc() call in mem<T>'s constructor (in
CarpetLib/src/mem.cc) and mempool (same file) to eg new char[blah] or
something similar so that they are also tracked.
The tracking is not thread safe at this point, you could likely add a
pthread mutex though if you need that (I'd just add them around the
new/delete implementations if I can get away with it).
Tracking is done by calling MemTagger::TagStack::PushTag("foo") which
will tag all memory allocation until the next PushTag as coming from
"foo". You can use PopTag("foo") to remove the current one and use the
previous one (same as the hierarchical timers). I way to tag a
fraction of the Cactus code would be to have the timers push and pop
the tags.
Yours,
Roland
- --
My email is as private as my paper mail. I therefore support encrypting
and signing email messages. Get my PGP key from http://keys.gnupg.net.
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1
Comment: Using GnuPG with Icedove - http://www.enigmail.net/
iEYEARECAAYFAlN2kiIACgkQTiFSTN7SboXsgwCguMZFSS2CJm5dKDo49vQIX7BC
SeAAnjMXAOFqRNeIAZ2v8jeockOkvJtK
=PbpQ
-----END PGP SIGNATURE-----
#include <new>
#include <memory>
#include <search.h>
#include <cstdlib>
#include <cstring>
#include <cstdio>
#include <cassert>
namespace MemTagger {
/// tracks to which memory tag a given allocation belongs
// Nothing in this file may use C++ style new/delete since it almost certainly
// leads to an infinite loop.
#define STACK_INCREMENT 100 /// get memory for tag stack in chunks of this size
#define HAVE_FORWARD_ITERATOR 0
#define TEST 1
/// a doubly linked list, used to keep track of the currently allocated chunks
/// of memory
struct dlist
{
public:
/// a single doubly linked node
struct dnode {
/// linkage
dnode *succ, *pred;
/// amount of memory recorded by this node
size_t size;
};
enum nocopytag_t {nocopy};
dlist(const char *nm) {
init(strdup(nm), true);
};
dlist(const char *nm, nocopytag_t tag) {
init(nm,false);
};
~dlist() {
if(owns_name)
free(const_cast<void*>(static_cast<const void*>(name)));
owns_name = false;
name = NULL;
// unhook nodes so that remove does not fail
head->pred = tail;
tail->succ = head;
};
dlist(const dlist& dl) {
if(&dl == this) return;
init(strdup(dl.name),true);
if(dl.head->succ) { // not empty list
this->head->pred = (dnode*)&this->headtail;
this->tail->pred = (dnode*)&this->head;
}
};
/// check if two lists track the same memory tag
static int cmp(const void* a, const void *b) {
return strcmp(((const dlist*)a)->name, ((const dlist*)b)->name);
}
/// the tag this list tracks
const char* get_name() const {return this->name;};
// modify list
/// append node to front of list
void push_front(dnode *nd) {
nd->succ = this->head;
nd->pred = reinterpret_cast<dnode*>(&this->head);
this->head->pred = nd;
this->head = nd;
}
/// remove node from list
static void remove(dnode* nd) {
nd->pred->succ = nd->succ;
nd->succ->pred = nd->pred;
}
/// iterator interface
#if HAVE_FORWARD_ITERATOR
class iterator;
#endif
class const_iterator {
public:
~const_iterator() {};
const_iterator(const const_iterator& it) : current(*it) {};
#if HAVE_FORWARD_ITERATOR
const_iterator(const iterator& it) : current(it.current) {};
#endif
const dnode* operator*() const {return current;};
const_iterator& operator++() {current=current->succ;return *this;};
bool operator!=(const const_iterator& it) const {
return this->current!=it.current;
};
private:
const_iterator(const dnode* cr) : current(cr) {};
const_iterator();
friend class dlist;
private:
const dnode *current;
};
#if HAVE_FORWARD_ITERATOR
class iterator {
public:
~iterator() {};
iterator(const iterator& it) : current(it.current), succ(it.succ) {};
dnode* operator*() const {return current;};
iterator& operator++() {current=succ;succ=current->succ;return *this;};
bool operator!=(const iterator& it) const {
return this->current!=it.current;
};
private:
iterator(dnode* cr) : current(cr),succ(current->succ) {};
iterator();
friend class dlist;
private:
dnode *current, *succ;
};
#endif
const_iterator begin() const {
return const_iterator(this->head);
};
const_iterator end() const {
return const_iterator((dnode*)&this->headtail);
};
#if HAVE_FORWARD_ITERATOR
iterator begin() {
return iterator(this->head);
};
iterator end() {
return iterator((dnode*)&this->headtail);
};
#endif
private:
dlist& operator=(const dlist&);
/// set of an empty list
/// @param nm tag that the list tracks
void init(const char *nm, bool own) {
this->head = reinterpret_cast<dnode*>(&this->headtail);
this->headtail = NULL;
this->tail = reinterpret_cast<dnode*>(&this->head);
this->name = nm;
this->owns_name = own;
}
private:
const char *name; /// the tag we track
bool owns_name; /// do we need to call free() on name?
dnode *head,*headtail,*tail; /// conflated first and last node
};
/// holds the current stack of tracker tags
// there may be only one of these since new/delete are global operators
class TagStack {
public:
TagStack() {};
~TagStack() {
tdestroy(troot, free_node);
troot = NULL;
free(memlist);
memlist_Cap = 0;
memlist_Top = -1;
memlist = NULL;
};
/// start new memory tracker
/// @param name tag to track
static void PushTag(const char *name) {
dlist key(name, dlist::nocopy);
dlist ** ptn = (dlist**)tsearch(&key, &troot, dlist::cmp);
if(*ptn == &key) { // new item, clone it.
void *buf= malloc(sizeof(**ptn));
if(!buf) throw std::bad_alloc();
*ptn = new (buf) dlist(name);
}
assert(memlist_Top < 0 ||
strcmp(name, memlist[memlist_Top]->get_name()) != 0);
memlist_Top += 1;
if(memlist_Top >= memlist_Cap) {
memlist_Cap = memlist_Top+STACK_INCREMENT;
memlist = static_cast<dlist**>(realloc(memlist,
sizeof(*memlist)*memlist_Cap));
}
memlist[memlist_Top] = *ptn;
};
/// finish memory tracker. Aborst if name does not agree with top of stack
/// @param name tag to track
static void PopTag(const char *name) {
assert(strcmp(name, memlist[memlist_Top]->get_name()) == 0);
assert(memlist_Top >= 0);
memlist_Top -= 1;
};
/// generate output for all memory currently allocated
static void PrintLists() {
twalk(troot, print_lists_action);
};
/// get the top of the stack
/// @return dlist tracking current tag
static dlist* GetHead() { return memlist[memlist_Top]; };
private:
TagStack(const TagStack&);
TagStack& operator=(const TagStack&);
// unfortunately search.h does not provide a calldata pointer to pass in
// user information, so we have to resort to a static variable
static void print_lists_action(const void *nodep, const VISIT which,
const int depth) {
if(which == postorder || which == leaf) {
dlist *memlist = *(dlist**)nodep;
printf("In list %s:\n", memlist->get_name());
for(dlist::const_iterator it = memlist->begin();
it != memlist->end();
++it) {
printf(" %zu bytes\n",(*it)->size);
}
}
}
static void free_node(void *nodep) {
dlist *ml = (dlist*)nodep;
#if HAVE_FORWARD_ITERATOR
for(dlist::iterator it = ml->begin();
it != ml->end();
++it) {
dlist::remove(*it);
free(static_cast<void*>(*it));
}
#endif
ml->~dlist();
free(ml);
}
private:
static void* troot; /// list of all known tags
// the actual memory tracking new/delete operators
static int memlist_Top; /// depth of stack
static int memlist_Cap; /// max size of stack
static dlist **memlist; /// the stack of memory tag
}; // class TagStack
void* TagStack::troot = NULL;
int TagStack::memlist_Top = -1;
int TagStack::memlist_Cap = 0;
dlist** TagStack::memlist = NULL;
static TagStack Stack; // only here so that destructor is called
// can be safely removed since all that is done is free()
} // namespace MemTagger
// the memory tracking new/delete operators themselves
void * operator new(size_t size) throw(std::bad_alloc)
{
using namespace MemTagger;
dlist::dnode * retval = (dlist::dnode*)malloc(sizeof(dlist::dnode)+size);
if(!retval) throw std::bad_alloc();
retval->size = size;
TagStack::GetHead()->push_front(retval);
printf ("alloc: %zu into %s\n",size, TagStack::GetHead()->get_name());
return (void*)(retval+1);
}
void * operator new[](size_t size) throw(std::bad_alloc)
{
return operator new(size);
}
void operator delete(void* p) throw()
{
using namespace MemTagger;
dlist::dnode *nd = ((dlist::dnode*)p)-1;
dlist::remove(nd);
free((void*)nd);
}
void operator delete[](void* p) throw()
{
operator delete(p);
}
#if TEST
int main(void)
{
MemTagger::TagStack::PushTag("first");
double *a = new double(1);
double *c = new double[10];
MemTagger::TagStack::PushTag("second");
double *b = new double(1);
MemTagger::TagStack::PopTag("second");
MemTagger::TagStack::PopTag("first");
MemTagger::TagStack::PrintLists();
delete[] a;
delete[] b;
delete[] c;
return 0;
}
#endif
MemTagger.cpp.sig
Description: PGP signature
_______________________________________________ Users mailing list [email protected] http://lists.einsteintoolkit.org/mailman/listinfo/users
