make expensive checks in asserts optional (#125)

This adds an Internal/EnableExpensiveChecks setting for enabling some or all expensive run-time checks to avoid a massive slowdown when assertions are enabled at compile-time. Currently these cover only the writer and reader-history cache checking.

Signed-off-by: Erik Boasson <eb@ilities.com>
This commit is contained in:
Erik Boasson 2019-03-23 13:10:53 +01:00
parent a39701fc2e
commit 7540ac8229
4 changed files with 93 additions and 29 deletions

View file

@ -2625,6 +2625,9 @@ int dds_rhc_takecdr
#define CHECK_MAX_CONDS 64
static int rhc_check_counts_locked (struct rhc *rhc, bool check_conds, bool check_qcmask)
{
if (!(config.enabled_xchecks & DDS_XCHECK_RHC))
return 1;
const uint32_t ncheck = rhc->nconds < CHECK_MAX_CONDS ? rhc->nconds : CHECK_MAX_CONDS;
unsigned n_instances = 0, n_nonempty_instances = 0;
unsigned n_not_alive_disposed = 0, n_not_alive_no_writers = 0, n_new = 0;

View file

@ -258,6 +258,7 @@ static void check_whc (const struct whc_impl *whc)
assert (whc->maxseq_node == whc_findmax_procedurally (whc));
#if !defined(NDEBUG)
if (config.enabled_xchecks & DDS_XCHECK_WHC)
{
struct whc_intvnode *firstintv;
struct whc_node *cur;

View file

@ -218,10 +218,15 @@ struct ssl_min_version {
};
#endif
/* Expensive checks (compiled in when NDEBUG not defined, enabled only if flag set in xchecks) */
#define DDS_XCHECK_WHC 1u
#define DDS_XCHECK_RHC 2u
struct config
{
int valid;
uint32_t enabled_logcats;
uint32_t enabled_xchecks;
char *servicename;
char *pcap_file;

View file

@ -105,6 +105,14 @@ static const uint32_t logcat_codes[] = {
DDS_LC_FATAL, DDS_LC_ERROR, DDS_LC_WARNING, DDS_LC_INFO, DDS_LC_CONFIG, DDS_LC_DISCOVERY, DDS_LC_DATA, DDS_LC_RADMIN, DDS_LC_TIMING, DDS_LC_TRAFFIC, DDS_LC_TOPIC, DDS_LC_TCP, DDS_LC_PLIST, DDS_LC_WHC, DDS_LC_THROTTLE, DDS_LC_RHC, DDS_LC_ALL
};
/* "trace" is special: it enables (nearly) everything */
static const char *xcheck_names[] = {
"whc", "rhc", "all", NULL
};
static const uint32_t xcheck_codes[] = {
DDS_XCHECK_WHC, DDS_XCHECK_RHC, ~(uint32_t)0
};
/* We want the tracing/verbosity settings to be fixed while parsing
the configuration, so we update this variable instead. */
static unsigned enabled_logcats;
@ -131,6 +139,7 @@ DUPF(string);
DU(tracingOutputFileName);
DU(verbosity);
DUPF(logcat);
DUPF(xcheck);
DUPF(float);
DUPF(int);
DUPF(uint);
@ -632,6 +641,12 @@ static const struct cfgelem unsupp_cfgelems[] = {
"<p>Testing options.</p>" },
{ GROUP("Watermarks", unsupp_watermarks_cfgelems),
"<p>Watermarks for flow-control.</p>" },
{ LEAF("EnableExpensiveChecks"), 1, "", ABSOFF(enabled_xchecks), 0, uf_xcheck, 0, pf_xcheck,
"<p>This element enables expensive checks in builds with assertions enabled and is ignored otherwise. Recognised categories are:</p>\n\
<ul><li><i>whc</i>: writer history cache checking</li>\n\
<li><i>rhc</i>: reader history cache checking</li>\n\
<p>In addition, there is the keyword <i>all</i> that enables all checks.</p>" },
END_MARKER
};
@ -1348,24 +1363,33 @@ static void pf_boolean_default (struct cfgst *cfgst, void *parent, struct cfgele
}
#endif
static int uf_logcat(struct cfgst *cfgst, UNUSED_ARG(void *parent), UNUSED_ARG(struct cfgelem const * const cfgelem), UNUSED_ARG(int first), const char *value)
static int do_uint32_bitset(struct cfgst *cfgst, uint32_t *cats, const char **names, const uint32_t *codes, const char *value)
{
static const char **vs = logcat_names;
static const uint32_t *lc = logcat_codes;
char *copy = ddsrt_strdup(value), *cursor = copy, *tok;
while ( (tok = ddsrt_strsep(&cursor, ",")) != NULL ) {
int idx = list_index(vs, tok);
int idx = list_index(names, tok);
if ( idx < 0 ) {
int ret = cfg_error(cfgst, "'%s' in '%s' undefined", tok, value);
ddsrt_free(copy);
return ret;
}
enabled_logcats |= lc[idx];
*cats |= codes[idx];
}
ddsrt_free(copy);
return 1;
}
static int uf_logcat(struct cfgst *cfgst, UNUSED_ARG(void *parent), UNUSED_ARG(struct cfgelem const * const cfgelem), UNUSED_ARG(int first), const char *value)
{
return do_uint32_bitset (cfgst, &enabled_logcats, logcat_names, logcat_codes, value);
}
static int uf_xcheck(struct cfgst *cfgst, void *parent, struct cfgelem const * const cfgelem, UNUSED_ARG(int first), const char *value)
{
uint32_t *elem = cfg_address(cfgst, parent, cfgelem);
return do_uint32_bitset (cfgst, elem, xcheck_names, xcheck_codes, value);
}
static int uf_verbosity(struct cfgst *cfgst, UNUSED_ARG(void *parent), UNUSED_ARG(struct cfgelem const * const cfgelem), UNUSED_ARG(int first), const char *value)
{
static const char *vs[] = {
@ -2333,45 +2357,76 @@ static void pf_standards_conformance(struct cfgst *cfgst, void *parent, struct c
cfg_log(cfgst, "%s%s", str, is_default ? " [def]" : "");
}
static void pf_logcat(struct cfgst *cfgst, UNUSED_ARG(void *parent), UNUSED_ARG(struct cfgelem const * const cfgelem), UNUSED_ARG(int is_default))
static unsigned uint32_popcnt (uint32_t x)
{
unsigned n = 0;
while (x != 0)
{
n += ((x & 1u) != 0);
x >>= 1;
}
return n;
}
static void do_print_uint32_bitset (struct cfgst *cfgst, uint32_t mask, size_t ncodes, const char **names, const uint32_t *codes, const char *suffix)
{
uint32_t remaining = config.enabled_logcats;
char res[256] = "", *resp = res;
const char *prefix = "";
size_t i;
#ifndef NDEBUG
{
size_t max;
for ( i = 0, max = 0; i < sizeof(logcat_codes) / sizeof(*logcat_codes); i++ )
max += 1 + strlen(logcat_names[i]);
size_t max = 0;
for (size_t i = 0; i < ncodes; i++ )
max += 1 + strlen(names[i]);
max += 11; /* ,0x%x */
max += 1; /* \0 */
assert(max <= sizeof(res));
}
#endif
/* TRACE enables ALLCATS, all the others just one */
if ( (remaining & DDS_LC_ALL) == DDS_LC_ALL ) {
resp += snprintf(resp, 256, "%strace", prefix);
remaining &= ~DDS_LC_ALL;
prefix = ",";
}
for ( i = 0; i < sizeof(logcat_codes) / sizeof(*logcat_codes); i++ ) {
if ( remaining & logcat_codes[i] ) {
resp += snprintf(resp, 256, "%s%s", prefix, logcat_names[i]);
remaining &= ~logcat_codes[i];
while (mask) {
size_t i_best = 0;
unsigned pc_best = 0;
for (size_t i = 0; i < ncodes; i++) {
uint32_t m = mask & codes[i];
if (m == codes[i]) {
unsigned pc = uint32_popcnt (m);
if (pc > pc_best) {
i_best = i;
pc_best = pc;
}
}
}
if (pc_best != 0) {
resp += snprintf(resp, 256, "%s%s", prefix, names[i_best]);
mask &= ~codes[i_best];
prefix = ",";
} else {
resp += snprintf (resp, 256, "%s0x%x", prefix, (unsigned) mask);
mask = 0;
}
}
if ( remaining ) {
resp += snprintf(resp, 256, "%s0x%x", prefix, (unsigned) remaining);
}
assert(resp <= res + sizeof(res));
/* can't do default indicator: user may have specified Verbosity, in
which case EnableCategory is at default, but for these two
settings, I don't mind. */
cfg_log(cfgst, "%s", res);
assert (resp <= res + sizeof(res));
cfg_log (cfgst, "%s%s", res, suffix);
}
static void pf_logcat(struct cfgst *cfgst, UNUSED_ARG(void *parent), UNUSED_ARG(struct cfgelem const * const cfgelem), UNUSED_ARG(int is_default))
{
/* can't do default indicator: user may have specified Verbosity, in
which case EnableCategory is at default, but for these two
settings, I don't mind. */
do_print_uint32_bitset (cfgst, config.enabled_logcats, sizeof(logcat_codes) / sizeof(*logcat_codes), logcat_names, logcat_codes, "");
}
static void pf_xcheck(struct cfgst *cfgst, void *parent, struct cfgelem const * const cfgelem, int is_default)
{
const uint32_t *p = cfg_address(cfgst, parent, cfgelem);
#ifndef NDEBUG
const char *suffix = is_default ? " [def]" : "";
#else
const char *suffix = " [ignored]";
(void)is_default;
#endif
do_print_uint32_bitset (cfgst, *p, sizeof(xcheck_codes) / sizeof(*xcheck_codes), xcheck_names, xcheck_codes, suffix);
}
static void print_configitems(struct cfgst *cfgst, void *parent, int isattr, struct cfgelem const * const cfgelem, int unchecked)
{