Browse Source

tdb2: TDB_ATTRIBUTE_STATS access via tdb_get_attribute.

Now we have tdb_get_attribute, it makes sense to make that the method
of accessing statistics.  That way they are always available, and it's
probably cheaper doing the direct increment than even the unlikely()
branch.
Rusty Russell 15 years ago
parent
commit
8cca0397ef

+ 11 - 11
ccan/tdb2/free.c

@@ -342,7 +342,7 @@ static tdb_bool_err coalesce(struct tdb_context *tdb,
 	struct tdb_free_record rec;
 	struct tdb_free_record rec;
 	enum TDB_ERROR ecode;
 	enum TDB_ERROR ecode;
 
 
-	add_stat(tdb, alloc_coalesce_tried, 1);
+	tdb->stats.alloc_coalesce_tried++;
 	end = off + sizeof(struct tdb_used_record) + data_len;
 	end = off + sizeof(struct tdb_used_record) + data_len;
 
 
 	while (end < tdb->file->map_size) {
 	while (end < tdb->file->map_size) {
@@ -376,7 +376,7 @@ static tdb_bool_err coalesce(struct tdb_context *tdb,
 		/* We may be violating lock order here, so best effort. */
 		/* We may be violating lock order here, so best effort. */
 		if (tdb_lock_free_bucket(tdb, nb_off, TDB_LOCK_NOWAIT)
 		if (tdb_lock_free_bucket(tdb, nb_off, TDB_LOCK_NOWAIT)
 		    != TDB_SUCCESS) {
 		    != TDB_SUCCESS) {
-			add_stat(tdb, alloc_coalesce_lockfail, 1);
+			tdb->stats.alloc_coalesce_lockfail++;
 			break;
 			break;
 		}
 		}
 
 
@@ -388,14 +388,14 @@ static tdb_bool_err coalesce(struct tdb_context *tdb,
 		}
 		}
 
 
 		if (unlikely(frec_magic(&rec) != TDB_FREE_MAGIC)) {
 		if (unlikely(frec_magic(&rec) != TDB_FREE_MAGIC)) {
-			add_stat(tdb, alloc_coalesce_race, 1);
+			tdb->stats.alloc_coalesce_race++;
 			tdb_unlock_free_bucket(tdb, nb_off);
 			tdb_unlock_free_bucket(tdb, nb_off);
 			break;
 			break;
 		}
 		}
 
 
 		if (unlikely(frec_ftable(&rec) != ftable)
 		if (unlikely(frec_ftable(&rec) != ftable)
 		    || unlikely(size_to_bucket(frec_len(&rec)) != bucket)) {
 		    || unlikely(size_to_bucket(frec_len(&rec)) != bucket)) {
-			add_stat(tdb, alloc_coalesce_race, 1);
+			tdb->stats.alloc_coalesce_race++;
 			tdb_unlock_free_bucket(tdb, nb_off);
 			tdb_unlock_free_bucket(tdb, nb_off);
 			break;
 			break;
 		}
 		}
@@ -409,7 +409,7 @@ static tdb_bool_err coalesce(struct tdb_context *tdb,
 
 
 		end += sizeof(struct tdb_used_record) + frec_len(&rec);
 		end += sizeof(struct tdb_used_record) + frec_len(&rec);
 		tdb_unlock_free_bucket(tdb, nb_off);
 		tdb_unlock_free_bucket(tdb, nb_off);
-		add_stat(tdb, alloc_coalesce_num_merged, 1);
+		tdb->stats.alloc_coalesce_num_merged++;
 	}
 	}
 
 
 	/* Didn't find any adjacent free? */
 	/* Didn't find any adjacent free? */
@@ -446,7 +446,7 @@ static tdb_bool_err coalesce(struct tdb_context *tdb,
 		goto err;
 		goto err;
 	}
 	}
 
 
-	add_stat(tdb, alloc_coalesce_succeeded, 1);
+	tdb->stats.alloc_coalesce_succeeded++;
 	tdb_unlock_free_bucket(tdb, b_off);
 	tdb_unlock_free_bucket(tdb, b_off);
 
 
 	ecode = add_free_record(tdb, off, end - off);
 	ecode = add_free_record(tdb, off, end - off);
@@ -476,7 +476,7 @@ static tdb_off_t lock_and_alloc(struct tdb_context *tdb,
 	size_t size = adjust_size(keylen, datalen);
 	size_t size = adjust_size(keylen, datalen);
 	enum TDB_ERROR ecode;
 	enum TDB_ERROR ecode;
 
 
-	add_stat(tdb, allocs, 1);
+	tdb->stats.allocs++;
 again:
 again:
 	b_off = bucket_off(ftable_off, bucket);
 	b_off = bucket_off(ftable_off, bucket);
 
 
@@ -596,7 +596,7 @@ again:
 		/* Bucket of leftover will be <= current bucket, so nested
 		/* Bucket of leftover will be <= current bucket, so nested
 		 * locking is allowed. */
 		 * locking is allowed. */
 		if (leftover) {
 		if (leftover) {
-			add_stat(tdb, alloc_leftover, 1);
+			tdb->stats.alloc_leftover++;
 			ecode = add_free_record(tdb,
 			ecode = add_free_record(tdb,
 						best_off + sizeof(rec)
 						best_off + sizeof(rec)
 						+ frec_len(&best) - leftover,
 						+ frec_len(&best) - leftover,
@@ -649,9 +649,9 @@ static tdb_off_t get_free(struct tdb_context *tdb,
 				return off;
 				return off;
 			if (off != 0) {
 			if (off != 0) {
 				if (b == start_b)
 				if (b == start_b)
-					add_stat(tdb, alloc_bucket_exact, 1);
+					tdb->stats.alloc_bucket_exact++;
 				if (b == TDB_FREE_BUCKETS - 1)
 				if (b == TDB_FREE_BUCKETS - 1)
-					add_stat(tdb, alloc_bucket_max, 1);
+					tdb->stats.alloc_bucket_max++;
 				/* Worked?  Stay using this list. */
 				/* Worked?  Stay using this list. */
 				tdb->ftable_off = ftable_off;
 				tdb->ftable_off = ftable_off;
 				tdb->ftable = ftable;
 				tdb->ftable = ftable;
@@ -758,7 +758,7 @@ static enum TDB_ERROR tdb_expand(struct tdb_context *tdb, tdb_len_t size)
 	/* We need to drop this lock before adding free record. */
 	/* We need to drop this lock before adding free record. */
 	tdb_unlock_expand(tdb, F_WRLCK);
 	tdb_unlock_expand(tdb, F_WRLCK);
 
 
-	add_stat(tdb, expands, 1);
+	tdb->stats.expands++;
 	return add_free_record(tdb, old_size, wanted);
 	return add_free_record(tdb, old_size, wanted);
 }
 }
 
 

+ 8 - 8
ccan/tdb2/hash.c

@@ -72,7 +72,7 @@ static tdb_bool_err key_matches(struct tdb_context *tdb,
 	const char *rkey;
 	const char *rkey;
 
 
 	if (rec_key_length(rec) != key->dsize) {
 	if (rec_key_length(rec) != key->dsize) {
-		add_stat(tdb, compare_wrong_keylen, 1);
+		tdb->stats.compare_wrong_keylen++;
 		return ret;
 		return ret;
 	}
 	}
 
 
@@ -83,7 +83,7 @@ static tdb_bool_err key_matches(struct tdb_context *tdb,
 	if (memcmp(rkey, key->dptr, key->dsize) == 0)
 	if (memcmp(rkey, key->dptr, key->dsize) == 0)
 		ret = true;
 		ret = true;
 	else
 	else
-		add_stat(tdb, compare_wrong_keycmp, 1);
+		tdb->stats.compare_wrong_keycmp++;
 	tdb_access_release(tdb, rkey);
 	tdb_access_release(tdb, rkey);
 	return ret;
 	return ret;
 }
 }
@@ -98,10 +98,10 @@ static tdb_bool_err match(struct tdb_context *tdb,
 	tdb_off_t off;
 	tdb_off_t off;
 	enum TDB_ERROR ecode;
 	enum TDB_ERROR ecode;
 
 
-	add_stat(tdb, compares, 1);
+	tdb->stats.compares++;
 	/* Desired bucket must match. */
 	/* Desired bucket must match. */
 	if (h->home_bucket != (val & TDB_OFF_HASH_GROUP_MASK)) {
 	if (h->home_bucket != (val & TDB_OFF_HASH_GROUP_MASK)) {
-		add_stat(tdb, compare_wrong_bucket, 1);
+		tdb->stats.compare_wrong_bucket++;
 		return false;
 		return false;
 	}
 	}
 
 
@@ -109,7 +109,7 @@ static tdb_bool_err match(struct tdb_context *tdb,
 	if (bits_from(val, TDB_OFF_HASH_EXTRA_BIT, TDB_OFF_UPPER_STEAL_EXTRA)
 	if (bits_from(val, TDB_OFF_HASH_EXTRA_BIT, TDB_OFF_UPPER_STEAL_EXTRA)
 	    != bits_from(h->h, 64 - h->hash_used - TDB_OFF_UPPER_STEAL_EXTRA,
 	    != bits_from(h->h, 64 - h->hash_used - TDB_OFF_UPPER_STEAL_EXTRA,
 		    TDB_OFF_UPPER_STEAL_EXTRA)) {
 		    TDB_OFF_UPPER_STEAL_EXTRA)) {
-		add_stat(tdb, compare_wrong_offsetbits, 1);
+		tdb->stats.compare_wrong_offsetbits++;
 		return false;
 		return false;
 	}
 	}
 
 
@@ -120,7 +120,7 @@ static tdb_bool_err match(struct tdb_context *tdb,
 	}
 	}
 
 
 	if ((h->h & ((1 << 11)-1)) != rec_hash(rec)) {
 	if ((h->h & ((1 << 11)-1)) != rec_hash(rec)) {
-		add_stat(tdb, compare_wrong_rechash, 1);
+		tdb->stats.compare_wrong_rechash++;
 		return false;
 		return false;
 	}
 	}
 
 
@@ -492,11 +492,11 @@ static enum TDB_ERROR expand_group(struct tdb_context *tdb, struct hash_info *h)
 	bucket = fullest_bucket(tdb, h->group, h->home_bucket);
 	bucket = fullest_bucket(tdb, h->group, h->home_bucket);
 
 
 	if (h->hash_used == 64) {
 	if (h->hash_used == 64) {
-		add_stat(tdb, alloc_chain, 1);
+		tdb->stats.alloc_chain++;
 		subsize = sizeof(struct tdb_chain);
 		subsize = sizeof(struct tdb_chain);
 		magic = TDB_CHAIN_MAGIC;
 		magic = TDB_CHAIN_MAGIC;
 	} else {
 	} else {
-		add_stat(tdb, alloc_subhash, 1);
+		tdb->stats.alloc_subhash++;
 		subsize = (sizeof(tdb_off_t) << TDB_SUBLEVEL_HASH_BITS);
 		subsize = (sizeof(tdb_off_t) << TDB_SUBLEVEL_HASH_BITS);
 		magic = TDB_HTABLE_MAGIC;
 		magic = TDB_HTABLE_MAGIC;
 	}
 	}

+ 0 - 6
ccan/tdb2/io.c

@@ -592,12 +592,6 @@ void tdb_inc_seqnum(struct tdb_context *tdb)
 	}
 	}
 }
 }
 
 
-void add_stat_(struct tdb_context *tdb, uint64_t *s, size_t val)
-{
-	if ((uintptr_t)s < (uintptr_t)tdb->stats + tdb->stats->size)
-		*s += val;
-}
-
 static const struct tdb_methods io_methods = {
 static const struct tdb_methods io_methods = {
 	tdb_read,
 	tdb_read,
 	tdb_write,
 	tdb_write,

+ 4 - 4
ccan/tdb2/lock.c

@@ -104,9 +104,9 @@ static int lock(struct tdb_context *tdb,
 		tdb->file->locker = getpid();
 		tdb->file->locker = getpid();
 	}
 	}
 
 
-	add_stat(tdb, lock_lowlevel, 1);
+	tdb->stats.lock_lowlevel++;
 	if (!waitflag)
 	if (!waitflag)
-		add_stat(tdb, lock_nonblock, 1);
+		tdb->stats.lock_nonblock++;
 	return tdb->lock_fn(tdb->file->fd, rw, off, len, waitflag,
 	return tdb->lock_fn(tdb->file->fd, rw, off, len, waitflag,
 			    tdb->lock_data);
 			    tdb->lock_data);
 }
 }
@@ -359,7 +359,7 @@ static enum TDB_ERROR tdb_nest_lock(struct tdb_context *tdb,
 		return TDB_ERR_LOCK;
 		return TDB_ERR_LOCK;
 	}
 	}
 
 
-	add_stat(tdb, locks, 1);
+	tdb->stats.locks++;
 
 
 	new_lck = find_nestlock(tdb, offset, NULL);
 	new_lck = find_nestlock(tdb, offset, NULL);
 	if (new_lck) {
 	if (new_lck) {
@@ -567,7 +567,7 @@ enum TDB_ERROR tdb_allrecord_lock(struct tdb_context *tdb, int ltype,
 				  " can't upgrade a write lock");
 				  " can't upgrade a write lock");
 	}
 	}
 
 
-	add_stat(tdb, locks, 1);
+	tdb->stats.locks++;
 again:
 again:
 	/* Lock hashes, gradually. */
 	/* Lock hashes, gradually. */
 	ecode = tdb_lock_gradual(tdb, ltype, flags, TDB_HASH_LOCK_START,
 	ecode = tdb_lock_gradual(tdb, ltype, flags, TDB_HASH_LOCK_START,

+ 20 - 28
ccan/tdb2/open.c

@@ -203,6 +203,12 @@ enum TDB_ERROR tdb_set_attribute(struct tdb_context *tdb,
 				     : attr->base.attr == TDB_ATTRIBUTE_SEED
 				     : attr->base.attr == TDB_ATTRIBUTE_SEED
 				     ? "TDB_ATTRIBUTE_SEED"
 				     ? "TDB_ATTRIBUTE_SEED"
 				     : "TDB_ATTRIBUTE_OPENHOOK");
 				     : "TDB_ATTRIBUTE_OPENHOOK");
+	case TDB_ATTRIBUTE_STATS:
+		return tdb->last_error
+			= tdb_logerr(tdb, TDB_ERR_EINVAL,
+				     TDB_LOG_USE_ERROR,
+				     "tdb_set_attribute:"
+				     " cannot set TDB_ATTRIBUTE_STATS");
 	case TDB_ATTRIBUTE_FLOCK:
 	case TDB_ATTRIBUTE_FLOCK:
 		tdb->lock_fn = attr->flock.lock;
 		tdb->lock_fn = attr->flock.lock;
 		tdb->unlock_fn = attr->flock.unlock;
 		tdb->unlock_fn = attr->flock.unlock;
@@ -252,9 +258,13 @@ enum TDB_ERROR tdb_get_attribute(struct tdb_context *tdb,
 				     TDB_LOG_USE_ERROR,
 				     TDB_LOG_USE_ERROR,
 				     "tdb_get_attribute:"
 				     "tdb_get_attribute:"
 				     " cannot get TDB_ATTRIBUTE_OPENHOOK");
 				     " cannot get TDB_ATTRIBUTE_OPENHOOK");
-	case TDB_ATTRIBUTE_STATS:
-		/* FIXME */
-		return TDB_ERR_EINVAL;
+	case TDB_ATTRIBUTE_STATS: {
+		size_t size = attr->stats.size;
+		if (size > tdb->stats.size)
+			size = tdb->stats.size;
+		memcpy(&attr->stats, &tdb->stats, size);
+		break;
+	}
 	case TDB_ATTRIBUTE_FLOCK:
 	case TDB_ATTRIBUTE_FLOCK:
 		attr->flock.lock = tdb->lock_fn;
 		attr->flock.lock = tdb->lock_fn;
 		attr->flock.unlock = tdb->unlock_fn;
 		attr->flock.unlock = tdb->unlock_fn;
@@ -291,7 +301,10 @@ void tdb_unset_attribute(struct tdb_context *tdb,
 			   : "TDB_ATTRIBUTE_OPENHOOK");
 			   : "TDB_ATTRIBUTE_OPENHOOK");
 		break;
 		break;
 	case TDB_ATTRIBUTE_STATS:
 	case TDB_ATTRIBUTE_STATS:
-		/* FIXME */
+		tdb_logerr(tdb, TDB_ERR_EINVAL,
+			   TDB_LOG_USE_ERROR,
+			   "tdb_unset_attribute:"
+			   "cannot unset TDB_ATTRIBUTE_STATS");
 		break;
 		break;
 	case TDB_ATTRIBUTE_FLOCK:
 	case TDB_ATTRIBUTE_FLOCK:
 		tdb->lock_fn = tdb_fcntl_lock;
 		tdb->lock_fn = tdb_fcntl_lock;
@@ -337,13 +350,15 @@ struct tdb_context *tdb_open(const char *name, int tdb_flags,
 	tdb->flags = tdb_flags;
 	tdb->flags = tdb_flags;
 	tdb->log_fn = NULL;
 	tdb->log_fn = NULL;
 	tdb->transaction = NULL;
 	tdb->transaction = NULL;
-	tdb->stats = NULL;
 	tdb->access = NULL;
 	tdb->access = NULL;
 	tdb->last_error = TDB_SUCCESS;
 	tdb->last_error = TDB_SUCCESS;
 	tdb->file = NULL;
 	tdb->file = NULL;
 	tdb->lock_fn = tdb_fcntl_lock;
 	tdb->lock_fn = tdb_fcntl_lock;
 	tdb->unlock_fn = tdb_fcntl_unlock;
 	tdb->unlock_fn = tdb_fcntl_unlock;
 	tdb->hash_fn = jenkins_hash;
 	tdb->hash_fn = jenkins_hash;
+	memset(&tdb->stats, 0, sizeof(tdb->stats));
+	tdb->stats.base.attr = TDB_ATTRIBUTE_STATS;
+	tdb->stats.size = sizeof(tdb->stats);
 	tdb_io_init(tdb);
 	tdb_io_init(tdb);
 
 
 	while (attr) {
 	while (attr) {
@@ -355,12 +370,6 @@ struct tdb_context *tdb_open(const char *name, int tdb_flags,
 		case TDB_ATTRIBUTE_SEED:
 		case TDB_ATTRIBUTE_SEED:
 			seed = &attr->seed;
 			seed = &attr->seed;
 			break;
 			break;
-		case TDB_ATTRIBUTE_STATS:
-			tdb->stats = &attr->stats;
-			/* They have stats we don't know about?  Tell them. */
-			if (tdb->stats->size > sizeof(attr->stats))
-				tdb->stats->size = sizeof(attr->stats);
-			break;
 		case TDB_ATTRIBUTE_OPENHOOK:
 		case TDB_ATTRIBUTE_OPENHOOK:
 			openhook = &attr->openhook;
 			openhook = &attr->openhook;
 			break;
 			break;
@@ -407,16 +416,6 @@ struct tdb_context *tdb_open(const char *name, int tdb_flags,
 		if (ecode != TDB_SUCCESS) {
 		if (ecode != TDB_SUCCESS) {
 			goto fail;
 			goto fail;
 		}
 		}
-		if (name) {
-			tdb->name = strdup(name);
-			if (!tdb->name) {
-				ecode = tdb_logerr(tdb, TDB_ERR_OOM,
-						   TDB_LOG_ERROR,
-						   "tdb_open: failed to"
-						   " allocate name");
-				goto fail;
-			}
-		}
 		tdb_convert(tdb, &hdr.hash_seed, sizeof(hdr.hash_seed));
 		tdb_convert(tdb, &hdr.hash_seed, sizeof(hdr.hash_seed));
 		tdb->hash_seed = hdr.hash_seed;
 		tdb->hash_seed = hdr.hash_seed;
 		tdb_ftable_init(tdb);
 		tdb_ftable_init(tdb);
@@ -525,13 +524,6 @@ struct tdb_context *tdb_open(const char *name, int tdb_flags,
 		goto fail;
 		goto fail;
 	}
 	}
 
 
-	tdb->name = strdup(name);
-	if (!tdb->name) {
-		ecode = tdb_logerr(tdb, TDB_ERR_OOM, TDB_LOG_ERROR,
-				   "tdb_open: failed to allocate name");
-		goto fail;
-	}
-
 	/* Clear any features we don't understand. */
 	/* Clear any features we don't understand. */
  	if ((open_flags & O_ACCMODE) != O_RDONLY) {
  	if ((open_flags & O_ACCMODE) != O_RDONLY) {
 		hdr.features_used &= TDB_FEATURE_MASK;
 		hdr.features_used &= TDB_FEATURE_MASK;

+ 2 - 12
ccan/tdb2/private.h

@@ -358,9 +358,6 @@ struct tdb_context {
 	/* mmap read only? */
 	/* mmap read only? */
 	int mmap_flags;
 	int mmap_flags;
 
 
-	/* Error code for last tdb error. */
-	enum TDB_ERROR ecode;
-
 	/* the flags passed to tdb_open, for tdb_reopen. */
 	/* the flags passed to tdb_open, for tdb_reopen. */
 	uint32_t flags;
 	uint32_t flags;
 
 
@@ -391,7 +388,8 @@ struct tdb_context {
 	/* IO methods: changes for transactions. */
 	/* IO methods: changes for transactions. */
 	const struct tdb_methods *methods;
 	const struct tdb_methods *methods;
 
 
-	struct tdb_attribute_stats *stats;
+	/* Our statistics. */
+	struct tdb_attribute_stats stats;
 
 
 	/* Direct access information */
 	/* Direct access information */
 	struct tdb_access_hdr *access;
 	struct tdb_access_hdr *access;
@@ -535,14 +533,6 @@ enum TDB_ERROR tdb_read_convert(struct tdb_context *tdb, tdb_off_t off,
 /* Bump the seqnum (caller checks for tdb->flags & TDB_SEQNUM) */
 /* Bump the seqnum (caller checks for tdb->flags & TDB_SEQNUM) */
 void tdb_inc_seqnum(struct tdb_context *tdb);
 void tdb_inc_seqnum(struct tdb_context *tdb);
 
 
-/* Adds a stat, if it's in range. */
-void add_stat_(struct tdb_context *tdb, uint64_t *stat, size_t val);
-#define add_stat(tdb, statname, val)					\
-	do {								\
-		if (unlikely((tdb)->stats))				\
-			add_stat_((tdb), &(tdb)->stats->statname, (val)); \
-	} while (0)
-
 /* lock.c: */
 /* lock.c: */
 /* Lock/unlock a range of hashes. */
 /* Lock/unlock a range of hashes. */
 enum TDB_ERROR tdb_lock_hashes(struct tdb_context *tdb,
 enum TDB_ERROR tdb_lock_hashes(struct tdb_context *tdb,

+ 2 - 2
ccan/tdb2/tdb.c

@@ -38,7 +38,7 @@ static enum TDB_ERROR replace_data(struct tdb_context *tdb,
 
 
 	/* We didn't like the existing one: remove it. */
 	/* We didn't like the existing one: remove it. */
 	if (old_off) {
 	if (old_off) {
-		add_stat(tdb, frees, 1);
+		tdb->stats.frees++;
 		ecode = add_free_record(tdb, old_off,
 		ecode = add_free_record(tdb, old_off,
 					sizeof(struct tdb_used_record)
 					sizeof(struct tdb_used_record)
 					+ key.dsize + old_room);
 					+ key.dsize + old_room);
@@ -285,7 +285,7 @@ enum TDB_ERROR tdb_delete(struct tdb_context *tdb, struct tdb_data key)
 	}
 	}
 
 
 	/* Free the deleted entry. */
 	/* Free the deleted entry. */
-	add_stat(tdb, frees, 1);
+	tdb->stats.frees++;
 	ecode = add_free_record(tdb, off,
 	ecode = add_free_record(tdb, off,
 				sizeof(struct tdb_used_record)
 				sizeof(struct tdb_used_record)
 				+ rec_key_length(&rec)
 				+ rec_key_length(&rec)

+ 5 - 5
ccan/tdb2/tdb2.h

@@ -729,13 +729,13 @@ struct tdb_attribute_seed {
  * struct tdb_attribute_stats - tdb operational statistics
  * struct tdb_attribute_stats - tdb operational statistics
  *
  *
  * This attribute records statistics of various low-level TDB operations.
  * This attribute records statistics of various low-level TDB operations.
- * This can be used to assist performance evaluation.
+ * This can be used to assist performance evaluation.  This is only
+ * useful for tdb_get_attribute().
  *
  *
  * New fields will be added at the end, hence the "size" argument which
  * New fields will be added at the end, hence the "size" argument which
- * indicates how large your structure is.  If your size is larger than
- * that known about by this version of tdb, the size will be reduced to
- * the known structure size.  Thus you can detect older versions, and
- * thus know that newer stats will not be updated.
+ * indicates how large your structure is: it must be filled in before
+ * calling tdb_get_attribute(), which will overwrite it with the size
+ * tdb knows about.
  */
  */
 struct tdb_attribute_stats {
 struct tdb_attribute_stats {
 	struct tdb_attribute_base base; /* .attr = TDB_ATTRIBUTE_STATS */
 	struct tdb_attribute_base base; /* .attr = TDB_ATTRIBUTE_STATS */

+ 1 - 1
ccan/tdb2/test/failtest_helper.h

@@ -4,7 +4,7 @@
 #include <stdbool.h>
 #include <stdbool.h>
 
 
 /* FIXME: Check these! */
 /* FIXME: Check these! */
-#define INITIAL_TDB_MALLOC	"open.c", 324, FAILTEST_MALLOC
+#define INITIAL_TDB_MALLOC	"open.c", 337, FAILTEST_MALLOC
 #define URANDOM_OPEN		"open.c", 45, FAILTEST_OPEN
 #define URANDOM_OPEN		"open.c", 45, FAILTEST_OPEN
 #define URANDOM_READ		"open.c", 25, FAILTEST_READ
 #define URANDOM_READ		"open.c", 25, FAILTEST_READ
 
 

+ 59 - 0
ccan/tdb2/test/run-91-get-stats.c

@@ -0,0 +1,59 @@
+#include <ccan/tdb2/tdb.c>
+#include <ccan/tdb2/open.c>
+#include <ccan/tdb2/free.c>
+#include <ccan/tdb2/lock.c>
+#include <ccan/tdb2/io.c>
+#include <ccan/tdb2/hash.c>
+#include <ccan/tdb2/check.c>
+#include <ccan/tdb2/transaction.c>
+#include <ccan/tdb2/traverse.c>
+#include <ccan/tap/tap.h>
+#include "logging.h"
+
+int main(int argc, char *argv[])
+{
+	unsigned int i;
+	struct tdb_context *tdb;
+	int flags[] = { TDB_DEFAULT, TDB_NOMMAP,
+			TDB_CONVERT, TDB_NOMMAP|TDB_CONVERT };
+
+	plan_tests(sizeof(flags) / sizeof(flags[0]) * 11);
+
+	for (i = 0; i < sizeof(flags) / sizeof(flags[0]); i++) {
+		union tdb_attribute *attr;
+		struct tdb_data key = tdb_mkdata("key", 3);
+
+		tdb = tdb_open("run-91-get-stats.tdb", flags[i],
+			       O_RDWR|O_CREAT|O_TRUNC, 0600, &tap_log_attr);
+		ok1(tdb);
+		ok1(tdb_store(tdb, key, key, TDB_REPLACE) == 0);
+
+		/* Use malloc so valgrind will catch overruns. */
+		attr = malloc(sizeof *attr);
+		attr->stats.base.attr = TDB_ATTRIBUTE_STATS;
+		attr->stats.size = sizeof(*attr);
+
+		ok1(tdb_get_attribute(tdb, attr) == 0);
+		ok1(attr->stats.size == sizeof(*attr));
+		ok1(attr->stats.allocs > 0);
+		ok1(attr->stats.expands > 0);
+		ok1(attr->stats.locks > 0);
+		free(attr);
+
+		/* Try short one. */
+		attr = malloc(offsetof(struct tdb_attribute_stats, allocs)
+			      + sizeof(attr->stats.allocs));
+		attr->stats.base.attr = TDB_ATTRIBUTE_STATS;
+		attr->stats.size = offsetof(struct tdb_attribute_stats, allocs)
+			+ sizeof(attr->stats.allocs);
+		ok1(tdb_get_attribute(tdb, attr) == 0);
+		ok1(attr->stats.size == sizeof(*attr));
+		ok1(attr->stats.allocs > 0);
+		free(attr);
+		ok1(tap_log_messages == 0);
+
+		tdb_close(tdb);
+
+	}
+	return exit_status();
+}

+ 53 - 45
ccan/tdb2/tools/speed.c

@@ -43,55 +43,67 @@ static int count_record(struct tdb_context *tdb,
 	return 0;
 	return 0;
 }
 }
 
 
-static void dump_and_clear_stats(struct tdb_attribute_stats *stats)
+static void dump_and_clear_stats(struct tdb_context **tdb,
+				 int flags,
+				 union tdb_attribute *attr)
 {
 {
+	union tdb_attribute stats;
+	enum TDB_ERROR ecode;
+
+	stats.base.attr = TDB_ATTRIBUTE_STATS;
+	stats.stats.size = sizeof(stats.stats);
+	ecode = tdb_get_attribute(*tdb, &stats);
+	if (ecode != TDB_SUCCESS)
+		errx(1, "Getting stats: %s", tdb_errorstr(ecode));
+
 	printf("allocs = %llu\n",
 	printf("allocs = %llu\n",
-	       (unsigned long long)stats->allocs);
+	       (unsigned long long)stats.stats.allocs);
 	printf("  alloc_subhash = %llu\n",
 	printf("  alloc_subhash = %llu\n",
-	       (unsigned long long)stats->alloc_subhash);
+	       (unsigned long long)stats.stats.alloc_subhash);
 	printf("  alloc_chain = %llu\n",
 	printf("  alloc_chain = %llu\n",
-	       (unsigned long long)stats->alloc_chain);
+	       (unsigned long long)stats.stats.alloc_chain);
 	printf("  alloc_bucket_exact = %llu\n",
 	printf("  alloc_bucket_exact = %llu\n",
-	       (unsigned long long)stats->alloc_bucket_exact);
+	       (unsigned long long)stats.stats.alloc_bucket_exact);
 	printf("  alloc_bucket_max = %llu\n",
 	printf("  alloc_bucket_max = %llu\n",
-	       (unsigned long long)stats->alloc_bucket_max);
+	       (unsigned long long)stats.stats.alloc_bucket_max);
 	printf("  alloc_leftover = %llu\n",
 	printf("  alloc_leftover = %llu\n",
-	       (unsigned long long)stats->alloc_leftover);
+	       (unsigned long long)stats.stats.alloc_leftover);
 	printf("  alloc_coalesce_tried = %llu\n",
 	printf("  alloc_coalesce_tried = %llu\n",
-	       (unsigned long long)stats->alloc_coalesce_tried);
+	       (unsigned long long)stats.stats.alloc_coalesce_tried);
 	printf("    alloc_coalesce_lockfail = %llu\n",
 	printf("    alloc_coalesce_lockfail = %llu\n",
-	       (unsigned long long)stats->alloc_coalesce_lockfail);
+	       (unsigned long long)stats.stats.alloc_coalesce_lockfail);
 	printf("    alloc_coalesce_race = %llu\n",
 	printf("    alloc_coalesce_race = %llu\n",
-	       (unsigned long long)stats->alloc_coalesce_race);
+	       (unsigned long long)stats.stats.alloc_coalesce_race);
 	printf("    alloc_coalesce_succeeded = %llu\n",
 	printf("    alloc_coalesce_succeeded = %llu\n",
-	       (unsigned long long)stats->alloc_coalesce_succeeded);
+	       (unsigned long long)stats.stats.alloc_coalesce_succeeded);
 	printf("       alloc_coalesce_num_merged = %llu\n",
 	printf("       alloc_coalesce_num_merged = %llu\n",
-	       (unsigned long long)stats->alloc_coalesce_num_merged);
+	       (unsigned long long)stats.stats.alloc_coalesce_num_merged);
 	printf("compares = %llu\n",
 	printf("compares = %llu\n",
-	       (unsigned long long)stats->compares);
+	       (unsigned long long)stats.stats.compares);
 	printf("  compare_wrong_bucket = %llu\n",
 	printf("  compare_wrong_bucket = %llu\n",
-	       (unsigned long long)stats->compare_wrong_bucket);
+	       (unsigned long long)stats.stats.compare_wrong_bucket);
 	printf("  compare_wrong_offsetbits = %llu\n",
 	printf("  compare_wrong_offsetbits = %llu\n",
-	       (unsigned long long)stats->compare_wrong_offsetbits);
+	       (unsigned long long)stats.stats.compare_wrong_offsetbits);
 	printf("  compare_wrong_keylen = %llu\n",
 	printf("  compare_wrong_keylen = %llu\n",
-	       (unsigned long long)stats->compare_wrong_keylen);
+	       (unsigned long long)stats.stats.compare_wrong_keylen);
 	printf("  compare_wrong_rechash = %llu\n",
 	printf("  compare_wrong_rechash = %llu\n",
-	       (unsigned long long)stats->compare_wrong_rechash);
+	       (unsigned long long)stats.stats.compare_wrong_rechash);
 	printf("  compare_wrong_keycmp = %llu\n",
 	printf("  compare_wrong_keycmp = %llu\n",
-	       (unsigned long long)stats->compare_wrong_keycmp);
+	       (unsigned long long)stats.stats.compare_wrong_keycmp);
 	printf("expands = %llu\n",
 	printf("expands = %llu\n",
-	       (unsigned long long)stats->expands);
+	       (unsigned long long)stats.stats.expands);
 	printf("frees = %llu\n",
 	printf("frees = %llu\n",
-	       (unsigned long long)stats->frees);
+	       (unsigned long long)stats.stats.frees);
 	printf("locks = %llu\n",
 	printf("locks = %llu\n",
-	       (unsigned long long)stats->locks);
+	       (unsigned long long)stats.stats.locks);
 	printf("   lock_lowlevel = %llu\n",
 	printf("   lock_lowlevel = %llu\n",
-	       (unsigned long long)stats->lock_lowlevel);
+	       (unsigned long long)stats.stats.lock_lowlevel);
 	printf("   lock_nonblock = %llu\n",
 	printf("   lock_nonblock = %llu\n",
-	       (unsigned long long)stats->lock_nonblock);
+	       (unsigned long long)stats.stats.lock_nonblock);
 
 
 	/* Now clear. */
 	/* Now clear. */
-	memset(&stats->allocs, 0, (char *)(stats+1) - (char *)&stats->allocs);
+	tdb_close(*tdb);
+	*tdb = tdb_open("/tmp/speed.tdb", flags, O_RDWR, 0, attr);
 }
 }
 
 
 static void tdb_log(struct tdb_context *tdb, enum tdb_log_level level,
 static void tdb_log(struct tdb_context *tdb, enum tdb_log_level level,
@@ -109,7 +121,8 @@ int main(int argc, char *argv[])
 	TDB_DATA key, data;
 	TDB_DATA key, data;
 	struct tdb_context *tdb;
 	struct tdb_context *tdb;
 	struct timeval start, stop;
 	struct timeval start, stop;
-	union tdb_attribute seed, stats, log;
+	union tdb_attribute seed, log;
+	bool do_stats = false;
 	enum TDB_ERROR ecode;
 	enum TDB_ERROR ecode;
 
 
 	/* Try to keep benchmarks even. */
 	/* Try to keep benchmarks even. */
@@ -121,11 +134,6 @@ int main(int argc, char *argv[])
 	log.base.next = &seed;
 	log.base.next = &seed;
 	log.log.fn = tdb_log;
 	log.log.fn = tdb_log;
 
 
-	memset(&stats, 0, sizeof(stats));
-	stats.base.attr = TDB_ATTRIBUTE_STATS;
-	stats.base.next = NULL;
-	stats.stats.size = sizeof(stats);
-
 	if (argv[1] && strcmp(argv[1], "--internal") == 0) {
 	if (argv[1] && strcmp(argv[1], "--internal") == 0) {
 		flags = TDB_INTERNAL;
 		flags = TDB_INTERNAL;
 		argc--;
 		argc--;
@@ -147,7 +155,7 @@ int main(int argc, char *argv[])
 		argv++;
 		argv++;
 	}
 	}
 	if (argv[1] && strcmp(argv[1], "--stats") == 0) {
 	if (argv[1] && strcmp(argv[1], "--stats") == 0) {
-		seed.base.next = &stats;
+		do_stats = true;
 		argc--;
 		argc--;
 		argv++;
 		argv++;
 	}
 	}
@@ -196,8 +204,8 @@ int main(int argc, char *argv[])
 		printf("%s\n", sumstr);
 		printf("%s\n", sumstr);
 		free(sumstr);
 		free(sumstr);
 	}
 	}
-	if (seed.base.next)
-		dump_and_clear_stats(&stats.stats);
+	if (do_stats)
+		dump_and_clear_stats(&tdb, flags, &log);
 
 
 	if (++stage == stopat)
 	if (++stage == stopat)
 		exit(0);
 		exit(0);
@@ -228,8 +236,8 @@ int main(int argc, char *argv[])
 		printf("%s\n", sumstr);
 		printf("%s\n", sumstr);
 		free(sumstr);
 		free(sumstr);
 	}
 	}
-	if (seed.base.next)
-		dump_and_clear_stats(&stats.stats);
+	if (do_stats)
+		dump_and_clear_stats(&tdb, flags, &log);
 	if (++stage == stopat)
 	if (++stage == stopat)
 		exit(0);
 		exit(0);
 
 
@@ -258,8 +266,8 @@ int main(int argc, char *argv[])
 		printf("%s\n", sumstr);
 		printf("%s\n", sumstr);
 		free(sumstr);
 		free(sumstr);
 	}
 	}
-	if (seed.base.next)
-		dump_and_clear_stats(&stats.stats);
+	if (do_stats)
+		dump_and_clear_stats(&tdb, flags, &log);
 	if (++stage == stopat)
 	if (++stage == stopat)
 		exit(0);
 		exit(0);
 
 
@@ -286,8 +294,8 @@ int main(int argc, char *argv[])
 		printf("%s\n", sumstr);
 		printf("%s\n", sumstr);
 		free(sumstr);
 		free(sumstr);
 	}
 	}
-	if (seed.base.next)
-		dump_and_clear_stats(&stats.stats);
+	if (do_stats)
+		dump_and_clear_stats(&tdb, flags, &log);
 	if (++stage == stopat)
 	if (++stage == stopat)
 		exit(0);
 		exit(0);
 
 
@@ -315,8 +323,8 @@ int main(int argc, char *argv[])
 		printf("%s\n", sumstr);
 		printf("%s\n", sumstr);
 		free(sumstr);
 		free(sumstr);
 	}
 	}
-	if (seed.base.next)
-		dump_and_clear_stats(&stats.stats);
+	if (do_stats)
+		dump_and_clear_stats(&tdb, flags, &log);
 	if (++stage == stopat)
 	if (++stage == stopat)
 		exit(0);
 		exit(0);
 
 
@@ -344,8 +352,8 @@ int main(int argc, char *argv[])
 		printf("%s\n", sumstr);
 		printf("%s\n", sumstr);
 		free(sumstr);
 		free(sumstr);
 	}
 	}
-	if (seed.base.next)
-		dump_and_clear_stats(&stats.stats);
+	if (do_stats)
+		dump_and_clear_stats(&tdb, flags, &log);
 	if (++stage == stopat)
 	if (++stage == stopat)
 		exit(0);
 		exit(0);
 
 
@@ -403,8 +411,8 @@ int main(int argc, char *argv[])
 		printf("%s\n", sumstr);
 		printf("%s\n", sumstr);
 		free(sumstr);
 		free(sumstr);
 	}
 	}
-	if (seed.base.next)
-		dump_and_clear_stats(&stats.stats);
+	if (do_stats)
+		dump_and_clear_stats(&tdb, flags, &log);
 	if (++stage == stopat)
 	if (++stage == stopat)
 		exit(0);
 		exit(0);
 
 

+ 1 - 1
ccan/tdb2/transaction.c

@@ -682,7 +682,7 @@ static enum TDB_ERROR tdb_recovery_allocate(struct tdb_context *tdb,
 	   us an area that is being currently used (as of the start of
 	   us an area that is being currently used (as of the start of
 	   the transaction) */
 	   the transaction) */
 	if (recovery_head != 0) {
 	if (recovery_head != 0) {
-		add_stat(tdb, frees, 1);
+		tdb->stats.frees++;
 		ecode = add_free_record(tdb, recovery_head,
 		ecode = add_free_record(tdb, recovery_head,
 					sizeof(rec) + rec.max_len);
 					sizeof(rec) + rec.max_len);
 		if (ecode != TDB_SUCCESS) {
 		if (ecode != TDB_SUCCESS) {