struct tdb_free_record rec;
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;
while (end < tdb->file->map_size) {
/* We may be violating lock order here, so best effort. */
if (tdb_lock_free_bucket(tdb, nb_off, TDB_LOCK_NOWAIT)
!= TDB_SUCCESS) {
- add_stat(tdb, alloc_coalesce_lockfail, 1);
+ tdb->stats.alloc_coalesce_lockfail++;
break;
}
}
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);
break;
}
if (unlikely(frec_ftable(&rec) != ftable)
|| 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);
break;
}
end += sizeof(struct tdb_used_record) + frec_len(&rec);
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? */
goto err;
}
- add_stat(tdb, alloc_coalesce_succeeded, 1);
+ tdb->stats.alloc_coalesce_succeeded++;
tdb_unlock_free_bucket(tdb, b_off);
ecode = add_free_record(tdb, off, end - off);
size_t size = adjust_size(keylen, datalen);
enum TDB_ERROR ecode;
- add_stat(tdb, allocs, 1);
+ tdb->stats.allocs++;
again:
b_off = bucket_off(ftable_off, bucket);
/* Bucket of leftover will be <= current bucket, so nested
* locking is allowed. */
if (leftover) {
- add_stat(tdb, alloc_leftover, 1);
+ tdb->stats.alloc_leftover++;
ecode = add_free_record(tdb,
best_off + sizeof(rec)
+ frec_len(&best) - leftover,
return off;
if (off != 0) {
if (b == start_b)
- add_stat(tdb, alloc_bucket_exact, 1);
+ tdb->stats.alloc_bucket_exact++;
if (b == TDB_FREE_BUCKETS - 1)
- add_stat(tdb, alloc_bucket_max, 1);
+ tdb->stats.alloc_bucket_max++;
/* Worked? Stay using this list. */
tdb->ftable_off = ftable_off;
tdb->ftable = ftable;
/* We need to drop this lock before adding free record. */
tdb_unlock_expand(tdb, F_WRLCK);
- add_stat(tdb, expands, 1);
+ tdb->stats.expands++;
return add_free_record(tdb, old_size, wanted);
}
const char *rkey;
if (rec_key_length(rec) != key->dsize) {
- add_stat(tdb, compare_wrong_keylen, 1);
+ tdb->stats.compare_wrong_keylen++;
return ret;
}
if (memcmp(rkey, key->dptr, key->dsize) == 0)
ret = true;
else
- add_stat(tdb, compare_wrong_keycmp, 1);
+ tdb->stats.compare_wrong_keycmp++;
tdb_access_release(tdb, rkey);
return ret;
}
tdb_off_t off;
enum TDB_ERROR ecode;
- add_stat(tdb, compares, 1);
+ tdb->stats.compares++;
/* Desired bucket must match. */
if (h->home_bucket != (val & TDB_OFF_HASH_GROUP_MASK)) {
- add_stat(tdb, compare_wrong_bucket, 1);
+ tdb->stats.compare_wrong_bucket++;
return false;
}
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,
TDB_OFF_UPPER_STEAL_EXTRA)) {
- add_stat(tdb, compare_wrong_offsetbits, 1);
+ tdb->stats.compare_wrong_offsetbits++;
return false;
}
}
if ((h->h & ((1 << 11)-1)) != rec_hash(rec)) {
- add_stat(tdb, compare_wrong_rechash, 1);
+ tdb->stats.compare_wrong_rechash++;
return false;
}
bucket = fullest_bucket(tdb, h->group, h->home_bucket);
if (h->hash_used == 64) {
- add_stat(tdb, alloc_chain, 1);
+ tdb->stats.alloc_chain++;
subsize = sizeof(struct tdb_chain);
magic = TDB_CHAIN_MAGIC;
} else {
- add_stat(tdb, alloc_subhash, 1);
+ tdb->stats.alloc_subhash++;
subsize = (sizeof(tdb_off_t) << TDB_SUBLEVEL_HASH_BITS);
magic = TDB_HTABLE_MAGIC;
}
}
}
-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 = {
tdb_read,
tdb_write,
tdb->file->locker = getpid();
}
- add_stat(tdb, lock_lowlevel, 1);
+ tdb->stats.lock_lowlevel++;
if (!waitflag)
- add_stat(tdb, lock_nonblock, 1);
+ tdb->stats.lock_nonblock++;
return tdb->lock_fn(tdb->file->fd, rw, off, len, waitflag,
tdb->lock_data);
}
return TDB_ERR_LOCK;
}
- add_stat(tdb, locks, 1);
+ tdb->stats.locks++;
new_lck = find_nestlock(tdb, offset, NULL);
if (new_lck) {
" can't upgrade a write lock");
}
- add_stat(tdb, locks, 1);
+ tdb->stats.locks++;
again:
/* Lock hashes, gradually. */
ecode = tdb_lock_gradual(tdb, ltype, flags, TDB_HASH_LOCK_START,
: attr->base.attr == TDB_ATTRIBUTE_SEED
? "TDB_ATTRIBUTE_SEED"
: "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:
tdb->lock_fn = attr->flock.lock;
tdb->unlock_fn = attr->flock.unlock;
TDB_LOG_USE_ERROR,
"tdb_get_attribute:"
" 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:
attr->flock.lock = tdb->lock_fn;
attr->flock.unlock = tdb->unlock_fn;
: "TDB_ATTRIBUTE_OPENHOOK");
break;
case TDB_ATTRIBUTE_STATS:
- /* FIXME */
+ tdb_logerr(tdb, TDB_ERR_EINVAL,
+ TDB_LOG_USE_ERROR,
+ "tdb_unset_attribute:"
+ "cannot unset TDB_ATTRIBUTE_STATS");
break;
case TDB_ATTRIBUTE_FLOCK:
tdb->lock_fn = tdb_fcntl_lock;
tdb->flags = tdb_flags;
tdb->log_fn = NULL;
tdb->transaction = NULL;
- tdb->stats = NULL;
tdb->access = NULL;
tdb->last_error = TDB_SUCCESS;
tdb->file = NULL;
tdb->lock_fn = tdb_fcntl_lock;
tdb->unlock_fn = tdb_fcntl_unlock;
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);
while (attr) {
case TDB_ATTRIBUTE_SEED:
seed = &attr->seed;
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:
openhook = &attr->openhook;
break;
if (ecode != TDB_SUCCESS) {
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->hash_seed = hdr.hash_seed;
tdb_ftable_init(tdb);
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. */
if ((open_flags & O_ACCMODE) != O_RDONLY) {
hdr.features_used &= TDB_FEATURE_MASK;
/* mmap read only? */
int mmap_flags;
- /* Error code for last tdb error. */
- enum TDB_ERROR ecode;
-
/* the flags passed to tdb_open, for tdb_reopen. */
uint32_t flags;
/* IO methods: changes for transactions. */
const struct tdb_methods *methods;
- struct tdb_attribute_stats *stats;
+ /* Our statistics. */
+ struct tdb_attribute_stats stats;
/* Direct access information */
struct tdb_access_hdr *access;
/* Bump the seqnum (caller checks for tdb->flags & TDB_SEQNUM) */
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/unlock a range of hashes. */
enum TDB_ERROR tdb_lock_hashes(struct tdb_context *tdb,
/* We didn't like the existing one: remove it. */
if (old_off) {
- add_stat(tdb, frees, 1);
+ tdb->stats.frees++;
ecode = add_free_record(tdb, old_off,
sizeof(struct tdb_used_record)
+ key.dsize + old_room);
}
/* Free the deleted entry. */
- add_stat(tdb, frees, 1);
+ tdb->stats.frees++;
ecode = add_free_record(tdb, off,
sizeof(struct tdb_used_record)
+ rec_key_length(&rec)
* struct tdb_attribute_stats - tdb operational statistics
*
* 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
- * 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_base base; /* .attr = TDB_ATTRIBUTE_STATS */
#include <stdbool.h>
/* 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_READ "open.c", 25, FAILTEST_READ
--- /dev/null
+#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();
+}
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",
- (unsigned long long)stats->allocs);
+ (unsigned long long)stats.stats.allocs);
printf(" alloc_subhash = %llu\n",
- (unsigned long long)stats->alloc_subhash);
+ (unsigned long long)stats.stats.alloc_subhash);
printf(" alloc_chain = %llu\n",
- (unsigned long long)stats->alloc_chain);
+ (unsigned long long)stats.stats.alloc_chain);
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",
- (unsigned long long)stats->alloc_bucket_max);
+ (unsigned long long)stats.stats.alloc_bucket_max);
printf(" alloc_leftover = %llu\n",
- (unsigned long long)stats->alloc_leftover);
+ (unsigned long long)stats.stats.alloc_leftover);
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",
- (unsigned long long)stats->alloc_coalesce_lockfail);
+ (unsigned long long)stats.stats.alloc_coalesce_lockfail);
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",
- (unsigned long long)stats->alloc_coalesce_succeeded);
+ (unsigned long long)stats.stats.alloc_coalesce_succeeded);
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",
- (unsigned long long)stats->compares);
+ (unsigned long long)stats.stats.compares);
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",
- (unsigned long long)stats->compare_wrong_offsetbits);
+ (unsigned long long)stats.stats.compare_wrong_offsetbits);
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",
- (unsigned long long)stats->compare_wrong_rechash);
+ (unsigned long long)stats.stats.compare_wrong_rechash);
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",
- (unsigned long long)stats->expands);
+ (unsigned long long)stats.stats.expands);
printf("frees = %llu\n",
- (unsigned long long)stats->frees);
+ (unsigned long long)stats.stats.frees);
printf("locks = %llu\n",
- (unsigned long long)stats->locks);
+ (unsigned long long)stats.stats.locks);
printf(" lock_lowlevel = %llu\n",
- (unsigned long long)stats->lock_lowlevel);
+ (unsigned long long)stats.stats.lock_lowlevel);
printf(" lock_nonblock = %llu\n",
- (unsigned long long)stats->lock_nonblock);
+ (unsigned long long)stats.stats.lock_nonblock);
/* 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,
TDB_DATA key, data;
struct tdb_context *tdb;
struct timeval start, stop;
- union tdb_attribute seed, stats, log;
+ union tdb_attribute seed, log;
+ bool do_stats = false;
enum TDB_ERROR ecode;
/* Try to keep benchmarks even. */
log.base.next = &seed;
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) {
flags = TDB_INTERNAL;
argc--;
argv++;
}
if (argv[1] && strcmp(argv[1], "--stats") == 0) {
- seed.base.next = &stats;
+ do_stats = true;
argc--;
argv++;
}
printf("%s\n", 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)
exit(0);
printf("%s\n", 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)
exit(0);
printf("%s\n", 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)
exit(0);
printf("%s\n", 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)
exit(0);
printf("%s\n", 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)
exit(0);
printf("%s\n", 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)
exit(0);
printf("%s\n", 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)
exit(0);
us an area that is being currently used (as of the start of
the transaction) */
if (recovery_head != 0) {
- add_stat(tdb, frees, 1);
+ tdb->stats.frees++;
ecode = add_free_record(tdb, recovery_head,
sizeof(rec) + rec.max_len);
if (ecode != TDB_SUCCESS) {