if(!daemon)
return NULL;
signal_handling_record();
- lock_basic_init(&daemon->lock);
+ checklock_start();
daemon->need_to_exit = 0;
+ alloc_init(&daemon->superalloc, NULL);
return daemon;
}
if(!daemon)
return;
listening_ports_free(daemon->ports);
- lock_basic_destroy(&daemon->lock);
+ alloc_clear(&daemon->superalloc);
free(daemon->cwd);
free(daemon->pidfile);
free(daemon);
+ checklock_stop();
}
#define DAEMON_H
#include "util/locks.h"
+#include "util/alloc.h"
struct config_file;
struct worker;
struct listen_port;
* Holds globally visible information.
*/
struct daemon {
- /** mutex for exclusive access to this structure. */
- lock_basic_t lock;
/** The config settings */
struct config_file* cfg;
/** current working directory */
struct worker** workers;
/** do we need to exit unbound (or is it only a reload?) */
int need_to_exit;
+ /** master allocation cache */
+ struct alloc_cache superalloc;
};
/**
#include "util/net_help.h"
#include "util/random.h"
#include "daemon/worker.h"
+#include "daemon/daemon.h"
#include "util/netevent.h"
#include "util/config_file.h"
#include "services/listen_dnsport.h"
fatal_exit("could not set forwarder address");
}
}
+ alloc_init(&worker->alloc, &worker->daemon->superalloc);
return 1;
}
if(worker->cmd_recv_fd != -1)
close(worker->cmd_recv_fd);
worker->cmd_recv_fd = -1;
+ alloc_clear(&worker->alloc);
free(worker);
}
#include "config.h"
#include "util/netevent.h"
#include "util/locks.h"
+#include "util/alloc.h"
struct listen_dnsport;
struct outside_network;
struct config_file;
struct ub_randstate* rndstate;
/** do we need to restart (instead of exit) ? */
int need_to_restart;
+ /** allocation cache for this thread */
+ struct alloc_cache alloc;
};
/**
- added rwlock writelock checking.
So it will keep track of the writelock, and readlocks are enforced
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
- Reviewed checklock code.
log_err("At %s %s:%d", func, file, line);
log_err("Error for %s lock: %s",
(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");
}
/** add protected region */
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(
sizeof(struct protected_area));
if(!e)
struct protected_area* p = lock->prot;
while(p) {
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,
"protected area modified");
}
checktype(enum check_lock_type type, struct checked_lock* lock,
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) {
lock_error(lock, func, file, line, "wrong lock type");
}
/* check if delete is OK */
acquire_locklock(e, func, file, line);
- *lock = NULL; /* use after free will fail */
if(e->hold_count != 0)
lock_error(e, func, file, line, "delete while locked.");
if(e->wait_count != 0)
lock_error(e, func, file, line, "delete while waited on.");
prot_check(e, func, file, line);
+ *lock = NULL; /* use after free will fail */
LOCKRET(pthread_mutex_unlock(&e->lock));
/* contention */
thr->id = pthread_self();
/* Hack to get same numbers as in log file */
thr->num = *(int*)(thr->arg);
+ log_assert(thr->num < THRDEBUG_MAX_THREADS);
log_assert(thread_infos[thr->num] == NULL);
thread_infos[thr->num] = thr;
LOCKRET(pthread_setspecific(thr_debug_key, thr));
return ret;
}
-/** allocate debug info and create thread */
-void
-checklock_thrcreate(pthread_t* id, void* (*func)(void*), void* arg)
+/** init the main thread */
+void checklock_start()
{
- struct thr_check* thr = (struct thr_check*)calloc(1,
- sizeof(struct thr_check));
- if(!thr)
- fatal_exit("thrcreate: out of memory");
if(!key_created) {
struct thr_check* thisthr = (struct thr_check*)calloc(1,
sizeof(struct thr_check));
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 */
+void
+checklock_thrcreate(pthread_t* id, void* (*func)(void*), void* arg)
+{
+ struct thr_check* thr = (struct thr_check*)calloc(1,
+ sizeof(struct thr_check));
+ if(!thr)
+ fatal_exit("thrcreate: out of memory");
+ if(!key_created) {
+ checklock_start();
+ }
thr->func = func;
thr->arg = arg;
LOCKRET(pthread_create(id, NULL, checklock_main, thr));
/**
* 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.
- * 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 size: length of area.
* You can call it multiple times with the same lock to give several areas.
* at this time and protected right away against unauthorised changes until
* 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.
/** test bool x, exits on failure, increases testcount. */
#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"
/** test net code */
static void
}
printf("Start of %s unit test.\n", PACKAGE_STRING);
net_test();
+ alloc_test();
printf("%d tests succeeded\n", testcount);
return 0;
}
{
memset(alloc, 0, sizeof(*alloc));
alloc->super = super;
- lock_quick_init(&alloc->lock);
+ if(!alloc->super) {
+ lock_quick_init(&alloc->lock);
+ lock_protect(&alloc->lock, alloc, sizeof(*alloc));
+ }
}
void
-alloc_delete(struct alloc_cache* alloc)
+alloc_clear(struct alloc_cache* alloc)
{
alloc_special_t* p, *np;
if(!alloc)
return;
- lock_quick_destroy(&alloc->lock);
+ if(!alloc->super)
+ lock_quick_destroy(&alloc->lock);
if(alloc->super && alloc->quar) {
/* push entire list into super */
p = alloc->quar;
p = alloc->quar;
alloc->quar = alloc_special_next(p);
alloc->num_quar--;
- alloc->special_allocated++;
alloc_special_clean(p);
return p;
}
}
lock_quick_unlock(&alloc->super->lock);
if(p) {
- alloc->special_allocated++;
alloc_special_clean(p);
return p;
}
prealloc(alloc);
if(!(p = (alloc_special_t*)malloc(sizeof(alloc_special_t))))
fatal_exit("alloc_special_obtain: out of memory");
- alloc->special_allocated++;
alloc_special_clean(p);
return p;
}
alloc_special_clean(mem);
if(alloc->super && alloc->num_quar >= ALLOC_SPECIAL_MAX) {
/* push it to the super structure */
- alloc->special_allocated --;
pushintosuper(alloc, mem);
return;
}
alloc_special_next(mem) = alloc->quar;
alloc->quar = mem;
alloc->num_quar++;
- alloc->special_allocated--;
}
void
alloc_stats(struct alloc_cache* alloc)
{
- log_info("%salloc: %d allocated, %d in cache.", alloc->super?"":"sup",
- (int)alloc->special_allocated, (int)alloc->num_quar);
+ log_info("%salloc: %d in cache.", alloc->super?"":"sup",
+ (int)alloc->num_quar);
}
alloc_special_t* quar;
/** number of items in quarantine. */
size_t num_quar;
-
- /** number of special type allocated */
- size_t special_allocated;
};
/**
* Does not free the alloc struct itself (it was also allocated by caller).
* @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.
#else /* USE_THREAD_DEBUG */
#define lock_protect(lock, area, size) /* nop */
+#define checklock_start() /* nop */
+#define checklock_stop() /* nop */
#ifdef HAVE_PTHREAD
#include <pthread.h>
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);
+}
*/
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.
* Pass printf formatted arguments. No trailing newline is needed.