bind9/tests/libtest/qp.c
Tony Finch c319ccd4c9 Fixes for liburcu-qsbr
Move registration and deregistration of the main thread from
`isc_loopmgr_run()` into `isc__initialize()` / `isc__shutdown()`:
liburcu-qsbr fails an assertion if we try to use it from an
unregistered thread, and we need to be able to use it when the
event loops are not running.

Use `rcu_assign_pointer()` and `rcu_dereference()` in qp-trie
transactions so that they properly mark threads as online. The
RCU-protected pointer is no longer declared atomic because
liburcu does not (yet) use standard C atomics.

Fix the definition of `isc_qsbr_rcu_dereference()` to return
the referenced value, and to call the right function inside
liburcu.

Change the thread sanitizer suppressions to match any variant of
`rcu_*_barrier()`
2023-05-15 20:49:42 +00:00

411 lines
10 KiB
C

/*
* Copyright (C) Internet Systems Consortium, Inc. ("ISC")
*
* SPDX-License-Identifier: MPL-2.0
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*
* See the COPYRIGHT file distributed with this work for additional
* information regarding copyright ownership.
*/
#include <assert.h>
#include <stdint.h>
#include <stdio.h>
#include <isc/buffer.h>
#include <isc/loop.h>
#include <isc/magic.h>
#include <isc/refcount.h>
#include <isc/rwlock.h>
#include <isc/urcu.h>
#include <isc/util.h>
#include <dns/name.h>
#include <dns/qp.h>
#include <dns/types.h>
#include "qp_p.h"
#include <tests/qp.h>
/***********************************************************************
*
* key reverse conversions
*/
uint8_t
qp_test_bittoascii(qp_shift_t bit) {
uint8_t byte = dns_qp_byte_for_bit[bit];
if (bit == SHIFT_NOBYTE) {
return ('.');
} else if (qp_common_character(byte)) {
return (byte);
} else if (byte < '-') {
return ('#');
} else if (byte < '_') {
return ('@');
} else {
return ('~' - SHIFT_OFFSET + bit);
}
}
const char *
qp_test_keytoascii(dns_qpkey_t key, size_t len) {
for (size_t offset = 0; offset < len; offset++) {
key[offset] = qp_test_bittoascii(key[offset]);
}
key[len] = '\0';
return ((const char *)key);
}
void
qp_test_keytoname(const dns_qpkey_t key, size_t keylen, dns_name_t *name) {
size_t locs[DNS_NAME_MAXLABELS];
size_t loc = 0, opos = 0;
size_t offset;
REQUIRE(ISC_MAGIC_VALID(name, DNS_NAME_MAGIC));
REQUIRE(name->buffer != NULL);
REQUIRE(name->offsets != NULL);
isc_buffer_clear(name->buffer);
/* Scan the key looking for label boundaries */
for (offset = 0; offset <= keylen; offset++) {
INSIST(key[offset] >= SHIFT_NOBYTE &&
key[offset] < SHIFT_OFFSET);
INSIST(loc < DNS_NAME_MAXLABELS);
if (qpkey_bit(key, keylen, offset) == SHIFT_NOBYTE) {
if (qpkey_bit(key, keylen, offset + 1) == SHIFT_NOBYTE)
{
locs[loc] = offset + 1;
goto scanned;
}
locs[loc++] = offset + 1;
} else if (offset == 0) {
/* This happens for a relative name */
locs[loc++] = offset;
}
}
UNREACHABLE();
scanned:
/*
* In the key the labels are encoded in reverse order, so
* we step backward through the label boundaries, then forward
* through the labels, to create the DNS wire format data.
*/
name->labels = loc;
while (loc-- > 0) {
uint8_t len = 0, *lenp = NULL;
/* Add a length byte to the name data and set an offset */
lenp = isc_buffer_used(name->buffer);
isc_buffer_putuint8(name->buffer, 0);
name->offsets[opos++] = name->length++;
/* Convert from escaped byte ranges to ASCII */
for (offset = locs[loc]; offset < locs[loc + 1] - 1; offset++) {
uint8_t bit = qpkey_bit(key, keylen, offset);
uint8_t byte = dns_qp_byte_for_bit[bit];
if (qp_common_character(byte)) {
isc_buffer_putuint8(name->buffer, byte);
} else {
byte += key[++offset] - SHIFT_BITMAP;
isc_buffer_putuint8(name->buffer, byte);
}
len++;
}
name->length += len;
*lenp = len;
}
/* Add a root label for absolute names */
if (key[0] == SHIFT_NOBYTE) {
name->attributes.absolute = true;
isc_buffer_putuint8(name->buffer, 0);
name->length++;
name->labels++;
}
name->ndata = isc_buffer_base(name->buffer);
}
/***********************************************************************
*
* trie properties
*/
static size_t
getheight(dns_qp_t *qp, qp_node_t *n) {
if (node_tag(n) == LEAF_TAG) {
return (0);
}
size_t max_height = 0;
qp_weight_t size = branch_twigs_size(n);
qp_node_t *twigs = branch_twigs_vector(qp, n);
for (qp_weight_t pos = 0; pos < size; pos++) {
size_t height = getheight(qp, &twigs[pos]);
max_height = ISC_MAX(max_height, height);
}
return (max_height + 1);
}
size_t
qp_test_getheight(dns_qp_t *qp) {
qp_node_t *root = get_root(qp);
return (root == NULL ? 0 : getheight(qp, root));
}
static size_t
maxkeylen(dns_qp_t *qp, qp_node_t *n) {
if (node_tag(n) == LEAF_TAG) {
dns_qpkey_t key;
return (leaf_qpkey(qp, n, key));
}
size_t max_len = 0;
qp_weight_t size = branch_twigs_size(n);
qp_node_t *twigs = branch_twigs_vector(qp, n);
for (qp_weight_t pos = 0; pos < size; pos++) {
size_t len = maxkeylen(qp, &twigs[pos]);
max_len = ISC_MAX(max_len, len);
}
return (max_len);
}
size_t
qp_test_maxkeylen(dns_qp_t *qp) {
qp_node_t *root = get_root(qp);
return (root == NULL ? 0 : maxkeylen(qp, root));
}
/***********************************************************************
*
* dump to stdout
*/
static void
dumpread(dns_qpreadable_t qpr, const char *type, const char *tail) {
dns_qpreader_t *qp = dns_qpreader(qpr);
printf("%s %p root %u %u:%u base %p methods %p%s", type, qp,
qp->root_ref, ref_chunk(qp->root_ref), ref_cell(qp->root_ref),
qp->base, qp->methods, tail);
}
static void
dumpqp(dns_qp_t *qp, const char *type) {
dumpread(qp, type, " mctx ");
printf("%p\n", qp->mctx);
printf("%s %p usage %p chunk_max %u bump %u fender %u\n", type, qp,
qp->usage, qp->chunk_max, qp->bump, qp->fender);
printf("%s %p leaf %u live %u used %u free %u hold %u\n", type, qp,
qp->leaf_count, qp->used_count - qp->free_count, qp->used_count,
qp->free_count, qp->hold_count);
printf("%s %p compact_all=%d transaction_mode=%d write_protect=%d\n",
type, qp, qp->compact_all, qp->transaction_mode,
qp->write_protect);
}
void
qp_test_dumpread(dns_qpreadable_t qp) {
dumpread(qp, "qpread", "\n");
fflush(stdout);
}
void
qp_test_dumpsnap(dns_qpsnap_t *qp) {
dumpread(qp, "qpsnap", " whence ");
printf("%p\n", qp->whence);
fflush(stdout);
}
void
qp_test_dumpqp(dns_qp_t *qp) {
dumpqp(qp, "qp");
fflush(stdout);
}
void
qp_test_dumpmulti(dns_qpmulti_t *multi) {
dns_qpreader_t qpr;
qp_node_t *reader = rcu_dereference(multi->reader);
dns_qpmulti_t *whence = unpack_reader(&qpr, reader);
dumpqp(&multi->writer, "qpmulti->writer");
printf("qpmulti->reader %p root_ref %u %u:%u base %p\n", reader,
qpr.root_ref, ref_chunk(qpr.root_ref), ref_cell(qpr.root_ref),
qpr.base);
printf("qpmulti->reader %p whence %p\n", reader, whence);
unsigned int snapshots = 0;
for (dns_qpsnap_t *snap = ISC_LIST_HEAD(multi->snapshots); //
snap != NULL; snap = ISC_LIST_NEXT(snap, link), snapshots++)
{
}
printf("qpmulti %p snapshots %u\n", multi, snapshots);
fflush(stdout);
}
void
qp_test_dumpchunks(dns_qp_t *qp) {
qp_cell_t used = 0;
qp_cell_t free = 0;
dumpqp(qp, "qp");
for (qp_chunk_t c = 0; c < qp->chunk_max; c++) {
printf("qp %p chunk %u base %p "
"used %u free %u immutable %u discounted %u\n",
qp, c, qp->base->ptr[c], qp->usage[c].used,
qp->usage[c].free, qp->usage[c].immutable,
qp->usage[c].discounted);
used += qp->usage[c].used;
free += qp->usage[c].free;
}
printf("qp %p total used %u free %u\n", qp, used, free);
fflush(stdout);
}
void
qp_test_dumptrie(dns_qpreadable_t qpr) {
dns_qpreader_t *qp = dns_qpreader(qpr);
struct {
qp_ref_t ref;
qp_shift_t max, pos;
} stack[512];
size_t sp = 0;
qp_cell_t leaf_count = 0;
/*
* fake up a sentinel stack entry corresponding to the root
* node; the ref is deliberately out of bounds, and pos == max
* so we will immediately stop scanning it
*/
stack[sp].ref = INVALID_REF;
stack[sp].max = 0;
stack[sp].pos = 0;
qp_node_t *n = get_root(qp);
if (n == NULL) {
printf("%p EMPTY\n", n);
fflush(stdout);
return;
} else {
printf("%p ROOT qp %p base %p\n", n, qp, qp->base);
}
for (;;) {
if (is_branch(n)) {
qp_ref_t ref = branch_twigs_ref(n);
qp_weight_t max = branch_twigs_size(n);
qp_node_t *twigs = ref_ptr(qp, ref);
/* brief list of twigs */
dns_qpkey_t bits;
size_t len = 0;
for (qp_shift_t bit = SHIFT_NOBYTE; bit < SHIFT_OFFSET;
bit++)
{
if (branch_has_twig(n, bit)) {
bits[len++] = bit;
}
}
assert(len == max);
qp_test_keytoascii(bits, len);
printf("%*s%p BRANCH %p %u %u:%u %zu %s\n", (int)sp * 2,
"", n, twigs, ref, ref_chunk(ref), ref_cell(ref),
branch_key_offset(n), bits);
++sp;
stack[sp].ref = ref;
stack[sp].max = max;
stack[sp].pos = 0;
} else {
dns_qpkey_t key;
qp_test_keytoascii(key, leaf_qpkey(qp, n, key));
printf("%*s%p LEAF %p %d %s\n", (int)sp * 2, "", n,
leaf_pval(n), leaf_ival(n), key);
leaf_count++;
}
while (stack[sp].pos == stack[sp].max) {
if (sp == 0) {
printf("LEAVES %d\n", leaf_count);
fflush(stdout);
return;
}
--sp;
}
n = ref_ptr(qp, stack[sp].ref) + stack[sp].pos;
stack[sp].pos++;
}
}
static void
dumpdot_name(qp_node_t *n) {
if (n == NULL) {
printf("empty");
} else if (is_branch(n)) {
qp_ref_t ref = branch_twigs_ref(n);
printf("c%dn%d", ref_chunk(ref), ref_cell(ref));
} else {
printf("v%p", leaf_pval(n));
}
}
static void
dumpdot_twig(dns_qp_t *qp, qp_node_t *n) {
if (n == NULL) {
printf("empty [shape=oval, label=\"\\N EMPTY\"];\n");
} else if (is_branch(n)) {
dumpdot_name(n);
printf(" [shape=record, label=\"{ \\N\\noff %zu | ",
branch_key_offset(n));
char sep = '{';
for (qp_shift_t bit = SHIFT_NOBYTE; bit < SHIFT_OFFSET; bit++) {
if (branch_has_twig(n, bit)) {
printf("%c <t%d> %c ", sep,
branch_twig_pos(n, bit),
qp_test_bittoascii(bit));
sep = '|';
}
}
printf("}}\"];\n");
qp_weight_t size = branch_twigs_size(n);
qp_node_t *twigs = branch_twigs_vector(qp, n);
for (qp_weight_t pos = 0; pos < size; pos++) {
dumpdot_name(n);
printf(":t%d:e -> ", pos);
dumpdot_name(&twigs[pos]);
printf(":w;\n");
}
for (qp_weight_t pos = 0; pos < size; pos++) {
dumpdot_twig(qp, &twigs[pos]);
}
} else {
dns_qpkey_t key;
const char *str;
str = qp_test_keytoascii(key, leaf_qpkey(qp, n, key));
printf("v%p [shape=oval, label=\"\\N ival %d\\n%s\"];\n",
leaf_pval(n), leaf_ival(n), str);
}
}
void
qp_test_dumpdot(dns_qp_t *qp) {
REQUIRE(QP_VALID(qp));
qp_node_t *n = get_root(qp);
printf("strict digraph {\nrankdir = \"LR\"; ranksep = 1.0;\n");
printf("ROOT [shape=point]; ROOT -> ");
dumpdot_name(n);
printf(":w;\n");
dumpdot_twig(qp, n);
printf("}\n");
}
/**********************************************************************/