easier.
Also adds a new environment variable MEMPOOLS replacing the -mc command line option
for disabling the use of memory pools. A lot of the pools is created
automatically before main() is started so command line options was
a bit too late.
dnl Configuration input file for Squid
dnl
-dnl $Id: configure.in,v 1.432 2006/09/02 16:18:22 serassio Exp $
+dnl $Id: configure.in,v 1.433 2006/09/03 04:09:35 hno Exp $
dnl
dnl
dnl
AC_CONFIG_AUX_DIR(cfgaux)
AC_CONFIG_SRCDIR([src/main.cc])
AM_INIT_AUTOMAKE([tar-ustar])
-AC_REVISION($Revision: 1.432 $)dnl
+AC_REVISION($Revision: 1.433 $)dnl
AC_PREFIX_DEFAULT(/usr/local/squid)
AM_MAINTAINER_MODE
fi
AC_SUBST(EXTERNAL_ACL_HELPERS)
+AC_ARG_WITH(valgrind,
+[ --with-valgrind Include debug instrumentation for use with valgrind],
+[ case $withval in
+ yes)
+ valgrind=1
+ ;;
+ no)
+ valgrind=
+ ;;
+ *)
+ CPPFLAGS="$CPPFLAGS -I${enableval}/include"
+ valgrind=1
+ ;;
+ esac
+ if test $valgrind; then
+ AC_DEFINE(WITH_VALGRIND, 1, [Valgrind memory debugger support])
+ echo "Valgrind debug support enabled"
+ fi
+])
+
dnl Disable "memPools" code
AC_DEFINE(DISABLE_POOLS, 0, [Define if you have problems with memPools and want to disable Pools.])
AC_ARG_ENABLE(mempools,
/*
- * $Id: config.h,v 1.22 2006/09/02 13:30:54 serassio Exp $
+ * $Id: config.h,v 1.23 2006/09/03 04:09:35 hno Exp $
*
* AUTHOR: Duane Wessels
*
#define PRINTF_FORMAT_ARG3
#endif
+/*
+ * Determine if this is a leak check build or standard
+ */
+#if PURIFY
+#define LEAK_CHECK_MODE 1
+#elif WITH_VALGRIND
+#define LEAK_CHECK_MODE 1
+#elif XMALLOC_TRACE
+#define LEAK_CHECK_MODE 1
+#endif
+
+/*
+ * valgrind debug support
+ */
+#if WITH_VALGRIND
+#include <valgrind/memcheck.h>
+#else
+#define VALGRIND_MAKE_NOACCESS(a,b) (0)
+#define VALGRIND_MAKE_WRITABLE(a,b) (0)
+#define VALGRIND_MAKE_READABLE(a,b) (0)
+#define VALGRIND_CHECK_WRITABLE(a,b) (0)
+#define VALGRIND_CHECK_READABLE(a,b) (0)
+#define VALGRIND_MALLOCLIKE_BLOCK(a,b,c,d)
+#define VALGRIND_FREELIKE_BLOCK(a,b)
+#define RUNNING_ON_VALGRIND 0
+#endif /* WITH_VALGRIND */
+
#endif /* SQUID_CONFIG_H */
/*
- * $Id: MemPool.cc,v 1.3 2006/06/18 08:56:32 serassio Exp $
+ * $Id: MemPool.cc,v 1.4 2006/09/03 04:09:35 hno Exp $
*
* DEBUG: section 63 Low Level Memory Pool Management
* AUTHOR: Alex Rousskov, Andres Kroonmaa, Robert Collins
for (int i = 1; i < pool->chunk_capacity; i++) {
*Free = (void *) ((char *) Free + pool->obj_size);
- Free = (void **)*Free;
+ void **nextFree = (void **)*Free;
+ (void) VALGRIND_MAKE_NOACCESS(Free, pool->obj_size);
+ Free = nextFree;
}
nextFreeChunk = pool->nextFreeChunk;
pool->nextFreeChunk = this;
Free = (void **)obj;
*Free = freeCache;
freeCache = obj;
+ (void) VALGRIND_MAKE_NOACCESS(obj, obj_size);
}
/*
/* first, try cache */
if (freeCache) {
Free = (void **)freeCache;
+ (void) VALGRIND_MAKE_READABLE(Free, obj_size);
freeCache = *Free;
*Free = NULL;
return Free;
/* last free in this chunk, so remove us from perchunk freelist chain */
nextFreeChunk = chunk->nextFreeChunk;
}
+ (void) VALGRIND_MAKE_READABLE(Free, obj_size);
return Free;
}
* MemPools::GetInstance().setDefaultPoolChunking() can be called.
*/
MemPools::MemPools() : pools(NULL), mem_idle_limit(2 * MB),
- poolCount (0), defaultIsChunked (!DISABLE_POOLS)
+ poolCount (0), defaultIsChunked (!DISABLE_POOLS && !RUNNING_ON_VALGRIND)
{
+ char *cfg = getenv("MEMPOOLS");
+ if (cfg)
+ defaultIsChunked = atoi(cfg);
#if HAVE_MALLOPT && M_MMAP_MAX
mallopt(M_MMAP_MAX, MEM_MAX_MMAP_CHUNKS);
#endif
MemImplementingAllocator::free(void *obj)
{
assert(obj != NULL);
+ (void) VALGRIND_CHECK_WRITABLE(obj, obj_size);
deallocate(obj);
++free_calls;
}
assert(splayLastResult == 0);
assert(chunk->inuse_count > 0);
chunk->inuse_count--;
+ (void) VALGRIND_MAKE_READABLE(Free, sizeof(void *));
freeCache = *(void **)Free; /* remove from global cache */
*(void **)Free = chunk->freeList; /* stuff into chunks freelist */
+ (void) VALGRIND_MAKE_NOACCESS(Free, sizeof(void *));
chunk->freeList = Free;
chunk->lastref = squid_curtime;
}
/*
- * $Id: cbdata.cc,v 1.72 2006/08/21 00:50:41 robertc Exp $
+ * $Id: cbdata.cc,v 1.73 2006/09/03 04:09:36 hno Exp $
*
* DEBUG: section 45 Callback Data Registry
* ORIGINAL AUTHOR: Duane Wessels
#endif
#include "Generic.h"
+#if WITH_VALGRIND
+#define HASHED_CBDATA 1
+#endif
+
static int cbdataCount = 0;
#if CBDATA_DEBUG
dlink_list cbdataEntries;
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;
/* 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 *
void **dataOffset = &zero->data;
return (long)dataOffset;
}
+#else
+MEMPROXY_CLASS_INLINE(cbdata)
+#endif
static OBJH cbdataDump;
#ifdef CBDATA_DEBUG
struct CBDataIndex
{
- MemAllocatorProxy *pool;
+ MemAllocator *pool;
FREE *free_func;
}
*cbdata_index = NULL;
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
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 = MemPools::GetInstance().create(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
cbdataInternalAlloc(cbdata_type type)
#endif
{
- cbdata *p;
+ 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
*/
- p = new (cbdata_index[type].pool->alloc()) cbdata;
+#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
- p->type = type;
- p->valid = 1;
- p->locks = 0;
- p->cookie = (long) p ^ cbdata::Cookie;
+ 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);
+ debug(45, 3) ("cbdataAlloc: %p %s:%d\n", 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);
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
* to free the memory.
* we could use the normal delete operator
* and it would Just Work. RBC 20030902
*/
- assert ( cbdata_index[theType].pool );
+ 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
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
#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
* to free the memory.
* 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
debug(45, 9) ("cbdataReferenceValid: %p\n", 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 = hash.key;
+#else
+ void *p = &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>
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;
+#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);
}
}
/*
- * $Id: comm_epoll.cc,v 1.12 2006/09/02 10:39:53 adrian Exp $
+ * $Id: comm_epoll.cc,v 1.13 2006/09/03 04:09:36 hno Exp $
*
* DEBUG: section 5 Socket functions
*
debug(5, DEBUG_EPOLL ? 0 : 8) ("commSetSelect(FD %d,type=%u,handler=%p,client_data=%p,timeout=%ld)\n",
fd,type,handler,client_data,timeout);
+ if (RUNNING_ON_VALGRIND) {
+ /* Keep valgrind happy.. complains about uninitialized bytes otherwise */
+ memset(&ev, 0, sizeof(ev));
+ }
ev.events = 0;
ev.data.fd = fd;
/*
- * $Id: main.cc,v 1.433 2006/09/02 08:43:37 serassio Exp $
+ * $Id: main.cc,v 1.434 2006/09/03 04:09:36 hno Exp $
*
* DEBUG: section 1 Startup and Main Loop
* AUTHOR: Harvest Derived
case 'm':
if (optarg) {
- if (*optarg == 'c') {
- MemPools::GetInstance().setDefaultPoolChunking(0);
- } else {
#if MALLOC_DBG
malloc_debug_level = atoi(optarg);
#else
fatal("Need to add -DMALLOC_DBG when compiling to use -mX option");
#endif
- }
-
} else {
#if XMALLOC_TRACE
xmalloc_trace = !xmalloc_trace;
Store::Root().sync(); /* Flush log close */
StoreFileSystem::FreeAllFs();
DiskIOModule::FreeAllModules();
-#if PURIFY || XMALLOC_TRACE
+#if LEAK_CHECK_MODE
configFreeMemory();
storeFreeMemory();
/*
- * $Id: mem.cc,v 1.99 2006/08/07 02:28:22 robertc Exp $
+ * $Id: mem.cc,v 1.100 2006/09/03 04:09:36 hno Exp $
*
* DEBUG: section 13 High Level Memory Pool Management
* AUTHOR: Harvest Derived
Report(stream);
memStringStats(stream);
memBufStats(stream);
+#if WITH_VALGRIND
+ if (RUNNING_ON_VALGRIND) {
+ long int leaked = 0, dubious = 0, reachable = 0, suppressed = 0;
+ stream << "Valgrind Report:\n";
+ stream << "Type\tAmount\n";
+ debug(13, 1) ("Asking valgrind for memleaks\n");
+ VALGRIND_DO_LEAK_CHECK;
+ debug(13, 1) ("Getting valgrind statistics\n");
+ VALGRIND_COUNT_LEAKS(leaked, dubious, reachable, suppressed);
+ stream << "Leaked\t" << leaked << "\n";
+ stream << "Dubious\t" << dubious << "\n";
+ stream << "Reachable\t" << reachable << "\n";
+ stream << "Suppressed\t" << suppressed << "\n";
+ }
+#endif
stream.flush();
}
/*
- * $Id: squid.h,v 1.258 2006/09/02 15:37:29 serassio Exp $
+ * $Id: squid.h,v 1.259 2006/09/03 04:09:36 hno Exp $
*
* AUTHOR: Duane Wessels
*
#define SA_RESETHAND SA_ONESHOT
#endif
-#if PURIFY
+#if LEACK_CHECK_MODE
#define LOCAL_ARRAY(type,name,size) \
static type *local_##name=NULL; \
type *name = local_##name ? local_##name : \