cleanup and unit test for alloc, also lock protection statements.

git-svn-id: file:///svn/unbound/trunk@168 be551aaa-1e26-0410-a405-d3ace91eadb9
This commit is contained in:
Wouter Wijngaards 2007-03-09 13:37:57 +00:00
parent 138aa8eebc
commit 1ea78ab032
13 changed files with 148 additions and 31 deletions

View file

@ -114,8 +114,9 @@ daemon_init()
if(!daemon) if(!daemon)
return NULL; return NULL;
signal_handling_record(); signal_handling_record();
lock_basic_init(&daemon->lock); checklock_start();
daemon->need_to_exit = 0; daemon->need_to_exit = 0;
alloc_init(&daemon->superalloc, NULL);
return daemon; return daemon;
} }
@ -302,8 +303,9 @@ daemon_delete(struct daemon* daemon)
if(!daemon) if(!daemon)
return; return;
listening_ports_free(daemon->ports); listening_ports_free(daemon->ports);
lock_basic_destroy(&daemon->lock); alloc_clear(&daemon->superalloc);
free(daemon->cwd); free(daemon->cwd);
free(daemon->pidfile); free(daemon->pidfile);
free(daemon); free(daemon);
checklock_stop();
} }

View file

@ -43,6 +43,7 @@
#define DAEMON_H #define DAEMON_H
#include "util/locks.h" #include "util/locks.h"
#include "util/alloc.h"
struct config_file; struct config_file;
struct worker; struct worker;
struct listen_port; struct listen_port;
@ -52,8 +53,6 @@ struct listen_port;
* Holds globally visible information. * Holds globally visible information.
*/ */
struct daemon { struct daemon {
/** mutex for exclusive access to this structure. */
lock_basic_t lock;
/** The config settings */ /** The config settings */
struct config_file* cfg; struct config_file* cfg;
/** current working directory */ /** current working directory */
@ -70,6 +69,8 @@ struct daemon {
struct worker** workers; struct worker** workers;
/** do we need to exit unbound (or is it only a reload?) */ /** do we need to exit unbound (or is it only a reload?) */
int need_to_exit; int need_to_exit;
/** master allocation cache */
struct alloc_cache superalloc;
}; };
/** /**

View file

@ -44,6 +44,7 @@
#include "util/net_help.h" #include "util/net_help.h"
#include "util/random.h" #include "util/random.h"
#include "daemon/worker.h" #include "daemon/worker.h"
#include "daemon/daemon.h"
#include "util/netevent.h" #include "util/netevent.h"
#include "util/config_file.h" #include "util/config_file.h"
#include "services/listen_dnsport.h" #include "services/listen_dnsport.h"
@ -387,6 +388,7 @@ worker_init(struct worker* worker, struct config_file *cfg,
fatal_exit("could not set forwarder address"); fatal_exit("could not set forwarder address");
} }
} }
alloc_init(&worker->alloc, &worker->daemon->superalloc);
return 1; return 1;
} }
@ -416,6 +418,7 @@ worker_delete(struct worker* worker)
if(worker->cmd_recv_fd != -1) if(worker->cmd_recv_fd != -1)
close(worker->cmd_recv_fd); close(worker->cmd_recv_fd);
worker->cmd_recv_fd = -1; worker->cmd_recv_fd = -1;
alloc_clear(&worker->alloc);
free(worker); free(worker);
} }

View file

@ -46,6 +46,7 @@
#include "config.h" #include "config.h"
#include "util/netevent.h" #include "util/netevent.h"
#include "util/locks.h" #include "util/locks.h"
#include "util/alloc.h"
struct listen_dnsport; struct listen_dnsport;
struct outside_network; struct outside_network;
struct config_file; struct config_file;
@ -105,6 +106,8 @@ struct worker {
struct ub_randstate* rndstate; struct ub_randstate* rndstate;
/** do we need to restart (instead of exit) ? */ /** do we need to restart (instead of exit) ? */
int need_to_restart; int need_to_restart;
/** allocation cache for this thread */
struct alloc_cache alloc;
}; };
/** /**

View file

@ -2,6 +2,9 @@
- added rwlock writelock checking. - added rwlock writelock checking.
So it will keep track of the writelock, and readlocks are enforced So it will keep track of the writelock, and readlocks are enforced
to not change protected memory areas. to not change protected memory areas.
- log_hex function to dump hex strings to the logfile.
- checklocks zeroes its destroyed lock after checking memory areas.
- unit test for alloc.
8 March 2007: Wouter 8 March 2007: Wouter
- Reviewed checklock code. - Reviewed checklock code.

View file

@ -71,7 +71,8 @@ static void lock_error(struct checked_lock* lock,
log_err("At %s %s:%d", func, file, line); log_err("At %s %s:%d", func, file, line);
log_err("Error for %s lock: %s", log_err("Error for %s lock: %s",
(lock->type==check_lock_mutex)?"mutex": ( (lock->type==check_lock_mutex)?"mutex": (
(lock->type==check_lock_spinlock)?"spinlock": "rwlock"), err); (lock->type==check_lock_spinlock)?"spinlock": (
(lock->type==check_lock_rwlock)?"rwlock": "badtype")), err);
fatal_exit("bailing out"); fatal_exit("bailing out");
} }
@ -111,8 +112,9 @@ acquire_locklock(struct checked_lock* lock,
/** add protected region */ /** add protected region */
void void
lock_protect(struct checked_lock* lock, void* area, size_t size) lock_protect(void *p, void* area, size_t size)
{ {
struct checked_lock* lock = *(struct checked_lock**)p;
struct protected_area* e = (struct protected_area*)malloc( struct protected_area* e = (struct protected_area*)malloc(
sizeof(struct protected_area)); sizeof(struct protected_area));
if(!e) if(!e)
@ -144,6 +146,8 @@ prot_check(struct checked_lock* lock,
struct protected_area* p = lock->prot; struct protected_area* p = lock->prot;
while(p) { while(p) {
if(memcmp(p->hold, p->region, p->size) != 0) { if(memcmp(p->hold, p->region, p->size) != 0) {
log_hex("memory prev", p->hold, p->size);
log_hex("memory here", p->region, p->size);
lock_error(lock, func, file, line, lock_error(lock, func, file, line,
"protected area modified"); "protected area modified");
} }
@ -211,6 +215,9 @@ static void
checktype(enum check_lock_type type, struct checked_lock* lock, checktype(enum check_lock_type type, struct checked_lock* lock,
const char* func, const char* file, int line) const char* func, const char* file, int line)
{ {
if(!lock)
fatal_exit("use of null/deleted lock at %s %s:%d",
func, file, line);
if(type != lock->type) { if(type != lock->type) {
lock_error(lock, func, file, line, "wrong lock type"); lock_error(lock, func, file, line, "wrong lock type");
} }
@ -232,12 +239,12 @@ checklock_destroy(enum check_lock_type type, struct checked_lock** lock,
/* check if delete is OK */ /* check if delete is OK */
acquire_locklock(e, func, file, line); acquire_locklock(e, func, file, line);
*lock = NULL; /* use after free will fail */
if(e->hold_count != 0) if(e->hold_count != 0)
lock_error(e, func, file, line, "delete while locked."); lock_error(e, func, file, line, "delete while locked.");
if(e->wait_count != 0) if(e->wait_count != 0)
lock_error(e, func, file, line, "delete while waited on."); lock_error(e, func, file, line, "delete while waited on.");
prot_check(e, func, file, line); prot_check(e, func, file, line);
*lock = NULL; /* use after free will fail */
LOCKRET(pthread_mutex_unlock(&e->lock)); LOCKRET(pthread_mutex_unlock(&e->lock));
/* contention */ /* contention */
@ -521,6 +528,7 @@ static void* checklock_main(void* arg)
thr->id = pthread_self(); thr->id = pthread_self();
/* Hack to get same numbers as in log file */ /* Hack to get same numbers as in log file */
thr->num = *(int*)(thr->arg); thr->num = *(int*)(thr->arg);
log_assert(thr->num < THRDEBUG_MAX_THREADS);
log_assert(thread_infos[thr->num] == NULL); log_assert(thread_infos[thr->num] == NULL);
thread_infos[thr->num] = thr; thread_infos[thr->num] = thr;
LOCKRET(pthread_setspecific(thr_debug_key, thr)); LOCKRET(pthread_setspecific(thr_debug_key, thr));
@ -530,6 +538,36 @@ static void* checklock_main(void* arg)
return ret; return ret;
} }
/** init the main thread */
void checklock_start()
{
if(!key_created) {
struct thr_check* thisthr = (struct thr_check*)calloc(1,
sizeof(struct thr_check));
if(!thisthr)
fatal_exit("thrcreate: out of memory");
key_created = 1;
LOCKRET(pthread_key_create(&thr_debug_key, NULL));
LOCKRET(pthread_setspecific(thr_debug_key, thisthr));
thread_infos[0] = thisthr;
}
}
/** stop checklocks */
void checklock_stop()
{
if(key_created) {
int i;
free(thread_infos[0]);
thread_infos[0] = NULL;
for(i = 0; i < THRDEBUG_MAX_THREADS; i++)
log_assert(thread_infos[i] == NULL);
/* should have been cleaned up. */
LOCKRET(pthread_key_delete(thr_debug_key));
key_created = 0;
}
}
/** allocate debug info and create thread */ /** allocate debug info and create thread */
void void
checklock_thrcreate(pthread_t* id, void* (*func)(void*), void* arg) checklock_thrcreate(pthread_t* id, void* (*func)(void*), void* arg)
@ -539,14 +577,7 @@ checklock_thrcreate(pthread_t* id, void* (*func)(void*), void* arg)
if(!thr) if(!thr)
fatal_exit("thrcreate: out of memory"); fatal_exit("thrcreate: out of memory");
if(!key_created) { if(!key_created) {
struct thr_check* thisthr = (struct thr_check*)calloc(1, checklock_start();
sizeof(struct thr_check));
if(!thisthr)
fatal_exit("thrcreate: out of memory");
key_created = 1;
LOCKRET(pthread_key_create(&thr_debug_key, NULL));
LOCKRET(pthread_setspecific(thr_debug_key, thisthr));
thread_infos[0] = thisthr;
} }
thr->func = func; thr->func = func;
thr->arg = arg; thr->arg = arg;

View file

@ -171,7 +171,8 @@ struct checked_lock {
/** /**
* Additional call for the user to specify what areas are protected * Additional call for the user to specify what areas are protected
* @param lock: the lock that protects the area. It can be inside the area. * @param lock: the lock that protects the area. It can be inside the area.
* The lock must be inited. * The lock must be inited. Call with user lock. (any type).
* It demangles the lock itself (struct checked_lock**).
* @param area: ptr to mem. * @param area: ptr to mem.
* @param size: length of area. * @param size: length of area.
* You can call it multiple times with the same lock to give several areas. * You can call it multiple times with the same lock to give several areas.
@ -179,7 +180,17 @@ struct checked_lock {
* at this time and protected right away against unauthorised changes until * at this time and protected right away against unauthorised changes until
* the next lock() call is done. * the next lock() call is done.
*/ */
void lock_protect(struct checked_lock* lock, void* area, size_t size); void lock_protect(void* lock, void* area, size_t size);
/**
* Initialise checklock. Sets up internal debug structures.
*/
void checklock_start();
/**
* Cleanup internal debug state.
*/
void checklock_stop();
/** /**
* Init locks. * Init locks.

View file

@ -47,6 +47,46 @@ int testcount = 0;
/** test bool x, exits on failure, increases testcount. */ /** test bool x, exits on failure, increases testcount. */
#define unit_assert(x) testcount++; log_assert(x); #define unit_assert(x) testcount++; log_assert(x);
#include "util/alloc.h"
/** test alloc code */
static void
alloc_test() {
alloc_special_t *t1, *t2;
struct alloc_cache major, minor1, minor2;
int i;
checklock_start();
alloc_init(&major, NULL);
alloc_init(&minor1, &major);
alloc_init(&minor2, &major);
t1 = alloc_special_obtain(&minor1);
alloc_clear(&minor1);
alloc_special_release(&minor2, t1);
t2 = alloc_special_obtain(&minor2);
unit_assert( t1 == t2 ); /* reused */
alloc_special_release(&minor2, t1);
for(i=0; i<100; i++) {
t1 = alloc_special_obtain(&minor1);
alloc_special_release(&minor2, t1);
}
if(0) {
alloc_stats(&minor1);
alloc_stats(&minor2);
alloc_stats(&major);
}
/* reuse happened */
unit_assert(minor1.num_quar + minor2.num_quar + major.num_quar == 11);
alloc_clear(&minor1);
alloc_clear(&minor2);
unit_assert(major.num_quar == 11);
alloc_clear(&major);
checklock_stop();
}
#include "util/net_help.h" #include "util/net_help.h"
/** test net code */ /** test net code */
static void static void
@ -78,6 +118,7 @@ main(int argc, char* argv[])
} }
printf("Start of %s unit test.\n", PACKAGE_STRING); printf("Start of %s unit test.\n", PACKAGE_STRING);
net_test(); net_test();
alloc_test();
printf("%d tests succeeded\n", testcount); printf("%d tests succeeded\n", testcount);
return 0; return 0;
} }

View file

@ -65,16 +65,20 @@ alloc_init(struct alloc_cache* alloc, struct alloc_cache* super)
{ {
memset(alloc, 0, sizeof(*alloc)); memset(alloc, 0, sizeof(*alloc));
alloc->super = super; alloc->super = super;
lock_quick_init(&alloc->lock); if(!alloc->super) {
lock_quick_init(&alloc->lock);
lock_protect(&alloc->lock, alloc, sizeof(*alloc));
}
} }
void void
alloc_delete(struct alloc_cache* alloc) alloc_clear(struct alloc_cache* alloc)
{ {
alloc_special_t* p, *np; alloc_special_t* p, *np;
if(!alloc) if(!alloc)
return; return;
lock_quick_destroy(&alloc->lock); if(!alloc->super)
lock_quick_destroy(&alloc->lock);
if(alloc->super && alloc->quar) { if(alloc->super && alloc->quar) {
/* push entire list into super */ /* push entire list into super */
p = alloc->quar; p = alloc->quar;
@ -108,7 +112,6 @@ alloc_special_obtain(struct alloc_cache* alloc)
p = alloc->quar; p = alloc->quar;
alloc->quar = alloc_special_next(p); alloc->quar = alloc_special_next(p);
alloc->num_quar--; alloc->num_quar--;
alloc->special_allocated++;
alloc_special_clean(p); alloc_special_clean(p);
return p; return p;
} }
@ -123,7 +126,6 @@ alloc_special_obtain(struct alloc_cache* alloc)
} }
lock_quick_unlock(&alloc->super->lock); lock_quick_unlock(&alloc->super->lock);
if(p) { if(p) {
alloc->special_allocated++;
alloc_special_clean(p); alloc_special_clean(p);
return p; return p;
} }
@ -132,7 +134,6 @@ alloc_special_obtain(struct alloc_cache* alloc)
prealloc(alloc); prealloc(alloc);
if(!(p = (alloc_special_t*)malloc(sizeof(alloc_special_t)))) if(!(p = (alloc_special_t*)malloc(sizeof(alloc_special_t))))
fatal_exit("alloc_special_obtain: out of memory"); fatal_exit("alloc_special_obtain: out of memory");
alloc->special_allocated++;
alloc_special_clean(p); alloc_special_clean(p);
return p; return p;
} }
@ -172,7 +173,6 @@ alloc_special_release(struct alloc_cache* alloc, alloc_special_t* mem)
alloc_special_clean(mem); alloc_special_clean(mem);
if(alloc->super && alloc->num_quar >= ALLOC_SPECIAL_MAX) { if(alloc->super && alloc->num_quar >= ALLOC_SPECIAL_MAX) {
/* push it to the super structure */ /* push it to the super structure */
alloc->special_allocated --;
pushintosuper(alloc, mem); pushintosuper(alloc, mem);
return; return;
} }
@ -180,12 +180,11 @@ alloc_special_release(struct alloc_cache* alloc, alloc_special_t* mem)
alloc_special_next(mem) = alloc->quar; alloc_special_next(mem) = alloc->quar;
alloc->quar = mem; alloc->quar = mem;
alloc->num_quar++; alloc->num_quar++;
alloc->special_allocated--;
} }
void void
alloc_stats(struct alloc_cache* alloc) alloc_stats(struct alloc_cache* alloc)
{ {
log_info("%salloc: %d allocated, %d in cache.", alloc->super?"":"sup", log_info("%salloc: %d in cache.", alloc->super?"":"sup",
(int)alloc->special_allocated, (int)alloc->num_quar); (int)alloc->num_quar);
} }

View file

@ -73,9 +73,6 @@ struct alloc_cache {
alloc_special_t* quar; alloc_special_t* quar;
/** number of items in quarantine. */ /** number of items in quarantine. */
size_t num_quar; size_t num_quar;
/** number of special type allocated */
size_t special_allocated;
}; };
/** /**
@ -92,7 +89,7 @@ void alloc_init(struct alloc_cache* alloc, struct alloc_cache* super);
* Does not free the alloc struct itself (it was also allocated by caller). * Does not free the alloc struct itself (it was also allocated by caller).
* @param alloc: is almost zeroed on exit (except some stats). * @param alloc: is almost zeroed on exit (except some stats).
*/ */
void alloc_delete(struct alloc_cache* alloc); void alloc_clear(struct alloc_cache* alloc);
/** /**
* Get a new special_t element. * Get a new special_t element.

View file

@ -77,6 +77,8 @@
#else /* USE_THREAD_DEBUG */ #else /* USE_THREAD_DEBUG */
#define lock_protect(lock, area, size) /* nop */ #define lock_protect(lock, area, size) /* nop */
#define checklock_start() /* nop */
#define checklock_stop() /* nop */
#ifdef HAVE_PTHREAD #ifdef HAVE_PTHREAD
#include <pthread.h> #include <pthread.h>

View file

@ -166,3 +166,18 @@ verbose(enum verbosity_value level, const char* format, ...)
va_end(args); va_end(args);
} }
void
log_hex(const char* msg, void* data, size_t length)
{
size_t i;
uint8_t* data8 = (uint8_t*)data;
const char const* hexchar = "0123456789ABCDEF";
char* buf = malloc(length*2 + 1); /* alloc hex chars + \0 */
for(i=0; i<length; i++) {
buf[i*2] = hexchar[ data8[i] >> 4 ];
buf[i*2 + 1] = hexchar[ data8[i] & 0xF ];
}
buf[length*2] = 0;
log_info("%s[%d] %s", msg, length, buf);
free(buf);
}

View file

@ -109,6 +109,15 @@ void log_err(const char* format, ...) ATTR_FORMAT(printf, 1, 2);
*/ */
void log_warn(const char* format, ...) ATTR_FORMAT(printf, 1, 2); void log_warn(const char* format, ...) ATTR_FORMAT(printf, 1, 2);
/**
* Log a hex-string to the log. Can be any length.
* performs mallocs to do so, slow. But debug useful.
* @param msg: string desc to accompany the hexdump.
* @param data: data to dump in hex format.
* @param length: length of data.
*/
void log_hex(const char* msg, void* data, size_t length);
/** /**
* Log fatal error message, and exit the current process. * Log fatal error message, and exit the current process.
* Pass printf formatted arguments. No trailing newline is needed. * Pass printf formatted arguments. No trailing newline is needed.