/*
- * $Id: cbdata.cc,v 1.65 2005/11/05 00:08:32 wessels Exp $
+ * $Id$
*
* DEBUG: section 45 Callback Data Registry
* ORIGINAL AUTHOR: Duane Wessels
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
- *
+ *
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
- *
+ *
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
*
*/
-/*
+/**
+ \defgroup CBDATAInternal Callback Data Allocator Internals
+ \ingroup CBDATAAPI
+ *
* These routines manage a set of registered callback data pointers.
- * One of the easiest ways to make Squid coredump is to issue a
+ * One of the easiest ways to make Squid coredump is to issue a
* callback to for some data structure which has previously been
* freed. With these routines, we register (add) callback data
* pointers, lock them just before registering the callback function,
* when finished.
*/
-#include "squid.h"
+#include "cbdata.h"
+#include "CacheManager.h"
#include "Store.h"
#if CBDATA_DEBUG
#include "Stack.h"
#endif
#include "Generic.h"
-/* XXX Remove me */
-#include "PeerSelectState.h"
+#if WITH_VALGRIND
+#define HASHED_CBDATA 1
+#endif
static int cbdataCount = 0;
#if CBDATA_DEBUG
{
public:
- CBDataCall (char const *callLabel, char const *aFile, int aLine) : label(callLabel), file(aFile), line(aLine){}
+ CBDataCall (char const *callLabel, char const *aFile, int aLine) : label(callLabel), file(aFile), line(aLine) {}
char const *label;
char const *file;
#endif
+/// \ingroup CBDATAInternal
#define OFFSET_OF(TYPE, MEMBER) ((size_t) &(((TYPE) *)0)->(MEMBER))
+/// \ingroup CBDATAInternal
class cbdata
{
-
+ /** \todo examine making cbdata templated on this - so we get type
+ * safe access to data - RBC 20030902 */
public:
+#if HASHED_CBDATA
+ hash_link hash; // Must be first
+#endif
+
#if CBDATA_DEBUG
void dump(StoreEntry *)const;
#endif
+#if !HASHED_CBDATA
void *operator new(size_t size, void *where);
void operator delete(void *where, void *where2);
+#else
+ MEMPROXY_CLASS(cndata);
+#endif
~cbdata();
int valid;
cbdata_type type;
#if CBDATA_DEBUG
- void addHistory(char const *label, char const *file, int line)
- {
+ void addHistory(char const *label, char const *file, int line) {
if (calls.size() > 1000)
return;
/* cookie used while debugging */
long cookie;
- /* MUST be the last per-instance member */
- /* TODO: examine making cbdata templated on this - so we get type
- * safe access to data - RBC 20030902 */
- void *data;
-void check(int line) const {assert(cookie == ((long)this ^ Cookie));}
+ void check(int line) const {assert(cookie == ((long)this ^ Cookie));}
+ static const long Cookie;
+#if !HASHED_CBDATA
size_t dataSize() const { return sizeof(data);}
-
- static const long Cookie;
static long MakeOffset();
static const long Offset;
+ /* MUST be the last per-instance member */
+ void *data;
+#endif
+
};
const long cbdata::Cookie((long)0xDEADBEEF);
+#if !HASHED_CBDATA
const long cbdata::Offset(MakeOffset());
void *
return where;
}
+/**
+ * Only ever invoked when placement new throws
+ * an exception. Used to prevent an incorrect
+ * free.
+ */
void
cbdata::operator delete(void *where, void *where2)
{
- /* Only ever invoked when placement new throws
- * an exception. Used to prevent an incorrect
- * free.
- */
+ ; // empty.
}
long
void **dataOffset = &zero->data;
return (long)dataOffset;
}
+#else
+MEMPROXY_CLASS_INLINE(cbdata);
+#endif
static OBJH cbdataDump;
#ifdef CBDATA_DEBUG
static OBJH cbdataDumpHistory;
#endif
-struct CBDataIndex
-{
- MemAllocatorProxy *pool;
+/// \ingroup CBDATAInternal
+struct CBDataIndex {
+ MemAllocator *pool;
FREE *free_func;
}
-
*cbdata_index = NULL;
+
+/// \ingroup CBDATAInternal
int cbdata_types = 0;
+#if HASHED_CBDATA
+static hash_table *cbdata_htable = NULL;
+
+static int
+cbdata_cmp(const void *p1, const void *p2)
+{
+ return (char *) p1 - (char *) p2;
+}
+
+static unsigned int
+cbdata_hash(const void *p, unsigned int mod)
+{
+ return ((unsigned long) p >> 8) % mod;
+}
+#endif
+
+
cbdata::~cbdata()
{
#if CBDATA_DEBUG
FREE *free_func = cbdata_index[type].free_func;
+#if HASHED_CBDATA
+ void *p = hash.key;
+#else
+ void *p = &data;
+#endif
+
if (free_func)
- free_func(&data);
+ free_func(p);
}
static void
cbdataInternalInitType(cbdata_type type, const char *name, int size, FREE * free_func)
{
char *label;
+ assert (type == cbdata_types + 1);
- if (type >= cbdata_types) {
- cbdata_index = (CBDataIndex *)xrealloc(cbdata_index, (type + 1) * sizeof(*cbdata_index));
- memset(&cbdata_index[cbdata_types], 0,
- (type + 1 - cbdata_types) * sizeof(*cbdata_index));
- cbdata_types = type + 1;
- }
-
- if (cbdata_index[type].pool)
- return;
+ cbdata_index = (CBDataIndex *)xrealloc(cbdata_index, (type + 1) * sizeof(*cbdata_index));
+ memset(&cbdata_index[type], 0, sizeof(*cbdata_index));
+ cbdata_types = type;
label = (char *)xmalloc(strlen(name) + 20);
snprintf(label, strlen(name) + 20, "cbdata %s (%d)", name, (int) type);
+#if !HASHED_CBDATA
assert((size_t)cbdata::Offset == (sizeof(cbdata) - ((cbdata *)NULL)->dataSize()));
+ size += cbdata::Offset;
+#endif
- cbdata_index[type].pool = new MemAllocatorProxy(label, size + cbdata::Offset);
+ cbdata_index[type].pool = memPoolCreate(label, size);
cbdata_index[type].free_func = free_func;
+
+#if HASHED_CBDATA
+ if (!cbdata_htable)
+ cbdata_htable = hash_create(cbdata_cmp, 1 << 12, cbdata_hash);
+#endif
}
cbdata_type
if (type)
return type;
- type = (cbdata_type)cbdata_types;
+ type = (cbdata_type)(cbdata_types + 1);
cbdataInternalInitType(type, name, size, free_func);
}
void
-cbdataInit(void)
+cbdataRegisterWithCacheManager(void)
{
- debug(45, 3) ("cbdataInit\n");
- cachemgrRegister("cbdata",
- "Callback Data Registry Contents",
- cbdataDump, 0, 1);
+ CacheManager *manager=CacheManager::GetInstance();
+ manager->registerAction("cbdata",
+ "Callback Data Registry Contents",
+ cbdataDump, 0, 1);
#if CBDATA_DEBUG
- cachemgrRegister("cbdatahistory",
- "Detailed call history for all current cbdata contents",
- cbdataDumpHistory, 0, 1);
+ manager->registerAction("cbdatahistory",
+ "Detailed call history for all current cbdata contents",
+ cbdataDumpHistory, 0, 1);
#endif
-#define CREATE_CBDATA(type) cbdataInternalInitType(CBDATA_##type, #type, sizeof(type), NULL)
-#define CREATE_CBDATA_FREE(type, free_func) cbdataInternalInitType(CBDATA_##type, #type, sizeof(type), free_func)
- /* XXX
- * most of these should be moved out to their respective module.
- */
- CREATE_CBDATA(ErrorState);
- CREATE_CBDATA(FwdState);
- CREATE_CBDATA(generic_cbdata);
- CREATE_CBDATA(helper);
- CREATE_CBDATA(helper_server);
- CREATE_CBDATA(statefulhelper);
- CREATE_CBDATA(helper_stateful_server);
- CREATE_CBDATA(ps_state);
- CREATE_CBDATA(RemovalPolicy);
- CREATE_CBDATA(RemovalPolicyWalker);
- CREATE_CBDATA(RemovalPurgeWalker);
}
void *
cbdataInternalAlloc(cbdata_type type)
#endif
{
- cbdata *p;
- assert(type > 0 && type < cbdata_types);
- p = new (cbdata_index[type].pool->alloc()) cbdata;
- // p = (cbdata *)cbdata_index[type].pool->alloc();
-
- p->type = type;
- p->valid = 1;
- p->locks = 0;
- p->cookie = (long) p ^ cbdata::Cookie;
+ cbdata *c;
+ void *p;
+ assert(type > 0 && type <= cbdata_types);
+ /* placement new: the pool alloc gives us cbdata + user type memory space
+ * and we init it with cbdata at the start of it
+ */
+#if HASHED_CBDATA
+ c = new cbdata;
+ p = cbdata_index[type].pool->alloc();
+ c->hash.key = p;
+ hash_join(cbdata_htable, &c->hash);
+#else
+ c = new (cbdata_index[type].pool->alloc()) cbdata;
+ p = (void *)&c->data;
+#endif
+
+ c->type = type;
+ c->valid = 1;
+ c->locks = 0;
+ c->cookie = (long) c ^ cbdata::Cookie;
cbdataCount++;
#if CBDATA_DEBUG
- p->file = file;
- p->line = line;
- p->calls = Stack<CBDataCall *> ();
- p->addHistory("Alloc", file, line);
- dlinkAdd(p, &p->link, &cbdataEntries);
- debug(45, 3) ("cbdataAlloc: %p %s:%d\n", &p->data, file, line);
+ c->file = file;
+ c->line = line;
+ c->calls = Stack<CBDataCall *> ();
+ c->addHistory("Alloc", file, line);
+ dlinkAdd(c, &c->link, &cbdataEntries);
+ debugs(45, 3, "cbdataAlloc: " << p << " " << file << ":" << line);
#endif
- return (void *) &p->data;
+ return p;
}
void *
#endif
{
cbdata *c;
+#if HASHED_CBDATA
+ c = (cbdata *) hash_lookup(cbdata_htable, p);
+#else
c = (cbdata *) (((char *) p) - cbdata::Offset);
+#endif
#if CBDATA_DEBUG
- debug(45, 3) ("cbdataFree: %p %s:%d\n", p, file, line);
+ debugs(45, 3, "cbdataFree: " << p << " " << file << ":" << line);
#else
- debug(45, 3) ("cbdataFree: %p\n", p);
+ debugs(45, 9, "cbdataFree: " << p);
#endif
c->check(__LINE__);
#endif
if (c->locks) {
- debug(45, 3) ("cbdataFree: %p has %d locks, not freeing\n",
- p, c->locks);
+ debugs(45, 9, "cbdataFree: " << p << " has " << c->locks << " locks, not freeing");
return NULL;
}
cbdataCount--;
- debug(45, 3) ("cbdataFree: Freeing %p\n", p);
+ debugs(45, 9, "cbdataFree: Freeing " << p);
#if CBDATA_DEBUG
dlinkDelete(&c->link, &cbdataEntries);
#endif
- cbdata_type theType = c->type;
- c->cbdata::~cbdata();
-
/* This is ugly. But: operator delete doesn't get
- * the type parameter, so we can't use that
+ * the type parameter, so we can't use that
* to free the memory.
* So, we free it ourselves.
- * Note that this means a non-placement
+ * Note that this means a non-placement
* new would be a seriously bad idea.
* Lastly, if we where a templated class,
* we could use the normal delete operator
* and it would Just Work. RBC 20030902
*/
+ cbdata_type theType = c->type;
+#if HASHED_CBDATA
+ hash_remove_link(cbdata_htable, &c->hash);
+ delete c;
+ cbdata_index[theType].pool->free((void *)p);
+#else
+ c->cbdata::~cbdata();
cbdata_index[theType].pool->free(c);
+#endif
return NULL;
}
if (p == NULL)
return;
+#if HASHED_CBDATA
+ c = (cbdata *) hash_lookup(cbdata_htable, p);
+#else
c = (cbdata *) (((char *) p) - cbdata::Offset);
+#endif
#if CBDATA_DEBUG
- debug(45, 3) ("cbdataLock: %p=%d %s:%d\n", p, c ? c->locks + 1 : -1, file, line);
+ debugs(45, 3, "cbdataLock: " << p << "=" << (c ? c->locks + 1 : -1) << " " << file << ":" << line);
c->addHistory("Reference", file, line);
#else
- debug(45, 3) ("cbdataLock: %p=%d\n", p, c ? c->locks + 1 : -1);
+ debugs(45, 9, "cbdataLock: " << p << "=" << (c ? c->locks + 1 : -1));
#endif
if (p == NULL)
return;
+#if HASHED_CBDATA
+ c = (cbdata *) hash_lookup(cbdata_htable, p);
+#else
c = (cbdata *) (((char *) p) - cbdata::Offset);
+#endif
#if CBDATA_DEBUG
- debug(45, 3) ("cbdataUnlock: %p=%d %s:%d\n", p, c ? c->locks - 1 : -1, file, line);
+ debugs(45, 3, "cbdataUnlock: " << p << "=" << (c ? c->locks - 1 : -1) << " " << file << ":" << line);
c->addHistory("Dereference", file, line);
#else
- debug(45, 3) ("cbdataUnlock: %p=%d\n", p, c ? c->locks - 1 : -1);
+ debugs(45, 9, "cbdataUnlock: " << p << "=" << (c ? c->locks - 1 : -1));
#endif
cbdataCount--;
- debug(45, 3) ("cbdataUnlock: Freeing %p\n", p);
+ debugs(45, 9, "cbdataUnlock: Freeing " << p);
#if CBDATA_DEBUG
#endif
- cbdata_type theType = c->type;
-
- c->cbdata::~cbdata();
-
/* This is ugly. But: operator delete doesn't get
- * the type parameter, so we can't use that
+ * the type parameter, so we can't use that
* to free the memory.
* So, we free it ourselves.
- * Note that this means a non-placement
+ * Note that this means a non-placement
* new would be a seriously bad idea.
* Lastly, if we where a templated class,
* we could use the normal delete operator
* and it would Just Work. RBC 20030902
*/
+ cbdata_type theType = c->type;
+#if HASHED_CBDATA
+ hash_remove_link(cbdata_htable, &c->hash);
+ delete c;
+ cbdata_index[theType].pool->free((void *)p);
+#else
+ c->cbdata::~cbdata();
cbdata_index[theType].pool->free(c);
+#endif
}
int
if (p == NULL)
return 1; /* A NULL pointer cannot become invalid */
- debug(45, 3) ("cbdataReferenceValid: %p\n", p);
+ debugs(45, 9, "cbdataReferenceValid: " << p);
+#if HASHED_CBDATA
+ c = (cbdata *) hash_lookup(cbdata_htable, p);
+#else
c = (cbdata *) (((char *) p) - cbdata::Offset);
+#endif
c->check(__LINE__);
void
cbdata::dump(StoreEntry *sentry) const
{
+#if HASHED_CBDATA
+ void *p = (void *)hash.key;
+#else
+ void *p = (void *)&data;
+#endif
storeAppendPrintf(sentry, "%c%p\t%d\t%d\t%20s:%-5d\n", valid ? ' ' :
- '!', &data, type, locks, file, line);
+ '!', p, type, locks, file, line);
}
-struct CBDataDumper : public unary_function<cbdata, void>
-{
- CBDataDumper(StoreEntry *anEntry):where(anEntry){}
+struct CBDataDumper : public unary_function<cbdata, void> {
+ CBDataDumper(StoreEntry *anEntry):where(anEntry) {}
- void operator()(cbdata const &x)
- {
+ void operator()(cbdata const &x) {
x.dump(where);
}
storeAppendPrintf(sentry, "types\tsize\tallocated\ttotal\n");
for (int i = 1; i < cbdata_types; i++) {
- MemAllocatorProxy *pool = cbdata_index[i].pool;
+ MemAllocator *pool = cbdata_index[i].pool;
if (pool) {
+#if HASHED_CBDATA
+ int obj_size = pool->objectSize();
+#else
int obj_size = pool->objectSize() - cbdata::Offset;
- storeAppendPrintf(sentry, "%s\t%d\t%d\t%d\n", pool->objectType() + 7, obj_size, pool->getMeter().inuse.level, obj_size * pool->getMeter().inuse.level);
+#endif
+ storeAppendPrintf(sentry, "%s\t%d\t%ld\t%ld\n", pool->objectType() + 7, obj_size, (long int)pool->getMeter().inuse.level, (long int)obj_size * pool->getMeter().inuse.level);
}
}
storeAppendPrintf(sentry, "\nsee also \"Memory utilization\" for detailed per type statistics\n");
}
+CBDATA_CLASS_INIT(generic_cbdata);
+
#if CBDATA_DEBUG
-struct CBDataCallDumper : public unary_function<CBDataCall, void>
-{
- CBDataCallDumper (StoreEntry *anEntry):where(anEntry){}
+struct CBDataCallDumper : public unary_function<CBDataCall, void> {
+ CBDataCallDumper (StoreEntry *anEntry):where(anEntry) {}
- void operator()(CBDataCall const &x)
- {
+ void operator()(CBDataCall const &x) {
storeAppendPrintf(where, "%s\t%s\t%d\n", x.label, x.file, x.line);
}
StoreEntry *where;
};
-struct CBDataHistoryDumper : public CBDataDumper
-{
- CBDataHistoryDumper(StoreEntry *anEntry):CBDataDumper(anEntry),where(anEntry), callDumper(anEntry){}
+struct CBDataHistoryDumper : public CBDataDumper {
+ CBDataHistoryDumper(StoreEntry *anEntry):CBDataDumper(anEntry),where(anEntry), callDumper(anEntry) {}
- void operator()(cbdata const &x)
- {
+ void operator()(cbdata const &x) {
CBDataDumper::operator()(x);
storeAppendPrintf(where, "\n");
storeAppendPrintf(where, "Action\tFile\tLine\n");