From: Alan T. DeKok Date: Mon, 12 Jan 2026 16:39:33 +0000 (-0500) Subject: print out more stats, and start defining statistics structs and APIs X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=100fc6b6f138cb5c87146f02bc8d9a201b6fbdb3;p=thirdparty%2Ffreeradius-server.git print out more stats, and start defining statistics structs and APIs the plan is to: * define the stats in the dictionaries * have radict load up a statistics structure, and * print out variable definitions for dict autoload * print out dict autoload entries * print out a C structure which holds the statistics * print out a "linking" structure which ties the C structure to the autoloaded DAs * glue all of this into the build with GNU Make magic. Right now, we can / have: * define an "instance" of a statistics thing, which can be linked to other instances of the same struct type * allow instance data to be merged, so that we can collate all of the similar statistics. * functions to convert the C struct to pairs, and back * functions to iterate over statistics --- diff --git a/src/bin/radict.c b/src/bin/radict.c index bd9f914eabc..9029918f079 100644 --- a/src/bin/radict.c +++ b/src/bin/radict.c @@ -339,31 +339,65 @@ static void da_normalize_name(fr_dict_attr_t const *da, char buffer[static FR_DI *q = '\0'; } -static void da_print_name(fr_dict_attr_t const *da) +static void da_print_name(FILE *fp, fr_dict_attr_t const *da) { char buffer[FR_DICT_ATTR_MAX_NAME_LEN + 1]; da_normalize_name(da, buffer); - printf("%s", buffer); + fprintf(fp, "%s", buffer); } +static const bool type_allowed[FR_TYPE_MAX] = { + [FR_TYPE_STRING] = true, + [FR_TYPE_OCTETS] = true, + + [FR_TYPE_UINT16] = true, + [FR_TYPE_UINT32] = true, + [FR_TYPE_UINT64] = true, + + [FR_TYPE_IPV4_ADDR] = true, + [FR_TYPE_IPV6_ADDR] = true, + + [FR_TYPE_DATE] = true, + [FR_TYPE_TIME_DELTA] = true, + +}; + +static bool children_ok(fr_dict_attr_t const *parent) +{ + int i; + fr_dict_attr_t const *da; + + for (i = 1; (da = fr_dict_attr_child_by_num(parent, i)) != NULL; i++) { + if (!type_allowed[da->type]) return false; + } + + return true; +} + +#define CHECK_TYPE(_parent) \ +do { \ + if ((parent->type != FR_TYPE_STRUCT) && (parent->type != FR_TYPE_TLV)) { \ + fprintf(stderr, "%s is not a struct or tlv\n", parent->name); \ + return; \ + } \ + if (!children_ok(parent)) fr_exit(EXIT_FAILURE); \ +} while (0) + /** Print structures and mappings, mainly for statistics. */ -static void da_print_struct(fr_dict_attr_t const *parent) +static void da_print_struct(FILE *fp, fr_dict_attr_t const *parent) { int i; fr_dict_attr_t const *da; - if (parent->type != FR_TYPE_STRUCT) { - fprintf(stderr, "%s is not a struct\n", parent->name); - return; - } + CHECK_TYPE(parent); /* * @todo - print full OID path and filename? */ - printf("/*\n *\t%s\n */\n", parent->name); - printf("typedef struct {\n"); + fprintf(fp, "/*\n *\t%s\n */\n", parent->name); + fprintf(fp, "typedef struct {\n"); for (i = 1; (da = fr_dict_attr_child_by_num(parent, i)) != NULL; i++) { unsigned int length = 0; @@ -380,96 +414,71 @@ static void da_print_struct(fr_dict_attr_t const *parent) * this is via a separate function that we call. But this shouldn't be necessary * for statistics structures, as they shouldn't contain bitfields. */ - printf("\tunsigned int : %u\t", da->flags.length); + fprintf(fp, "\tunsigned int : %u\t", da->flags.length); } else switch (da->type) { case FR_TYPE_STRING: + if ((parent->type == FR_TYPE_TLV) && !da->flags.length) { + fprintf(fp, "\t%s\t*", type_to_c_type[da->type]); + break; + } + FALL_THROUGH; + case FR_TYPE_OCTETS: fr_assert(da->flags.length > 0); length = da->flags.length; - printf("\t%s\t", type_to_c_type[da->type]); + fprintf(fp, "\t%s\t", type_to_c_type[da->type]); break; case FR_TYPE_DATE: fr_assert(da->flags.length <= 8); fr_assert(length_to_c_type[da->flags.length] != NULL); - printf("\t%s\t", length_to_c_type[da->flags.length]); + fprintf(fp, "\t%s\t", length_to_c_type[da->flags.length]); break; default: fr_assert(type_to_c_type[da->type] != NULL); - printf("\t%s\t", type_to_c_type[da->type]); + fprintf(fp, "\t%s\t", type_to_c_type[da->type]); break; } - da_print_name(da); + da_print_name(fp, da); if (length) { - printf("[%u]", length); + fprintf(fp, "[%u]", length); } - printf(";\n"); - } - - printf("} "); - - printf("fr_"); - da_print_name(fr_dict_root(parent->dict)); - printf("_"); - da_print_name(parent); - printf("_t;\n"); -} - -static void da_print_stats_link(fr_dict_attr_t const *parent) -{ - int i; - fr_dict_attr_t const *da; - char dict_name[FR_DICT_ATTR_MAX_NAME_LEN + 1]; - char parent_name[FR_DICT_ATTR_MAX_NAME_LEN + 1]; - - if (parent->type != FR_TYPE_STRUCT) { - fprintf(stderr, "%s is not a struct\n", parent->name); - return; + fprintf(fp, ";\n"); } - da_normalize_name(fr_dict_root(parent->dict), dict_name); - da_normalize_name(parent, parent_name); - - printf("fr_stats_link_t const fr_%s_%s_stats_link_t[] = {\n", dict_name, parent_name); - - for (i = 1; (da = fr_dict_attr_child_by_num(parent, i)) != NULL; i++) { - printf("\t{ &attr_%s_", parent_name); - da_print_name(da); - printf(", offsetof(fr_%s_%s_t, ", dict_name, parent_name); - da_print_name(da); - printf(") },\n"); - } + fprintf(fp, "} "); - printf("};\n\n"); + fprintf(fp, "fr_"); + da_print_name(fp, fr_dict_root(parent->dict)); + fprintf(fp, "_"); + da_print_name(fp, parent); + fprintf(fp, "_t;\n"); } -static void da_print_attr_def(fr_dict_attr_t const *parent) +static void da_print_attr_def(FILE *fp, fr_dict_attr_t const *parent) { int i; fr_dict_attr_t const *da; char parent_name[FR_DICT_ATTR_MAX_NAME_LEN + 1]; - if (parent->type != FR_TYPE_STRUCT) { - fprintf(stderr, "%s is not a struct\n", parent->name); - return; - } + CHECK_TYPE(parent); da_normalize_name(parent, parent_name); - printf("static fr_dict_attr_t const *attr_%s;\n", parent_name); + fprintf(fp, "static fr_dict_attr_t const *attr_%s;\n", parent_name); for (i = 1; (da = fr_dict_attr_child_by_num(parent, i)) != NULL; i++) { - printf("static fr_dict_attr_t const *attr_%s_", parent_name); - da_print_name(da); - printf(";\n"); + fprintf(fp, "static fr_dict_attr_t const *attr_%s_", parent_name); + da_print_name(fp, da); + fprintf(fp, ";\n"); } - printf("\n\n"); + fprintf(fp, "\n\n"); } /** Map data types to enum names representing those types @@ -521,18 +530,65 @@ static char const *fr_type_to_enum_name[] = { ENUM_NAME(FR_TYPE_ATTR), }; -static void da_print_attr_autoload(fr_dict_attr_t const *parent) +static void da_print_stats_link(FILE *fp, fr_dict_attr_t const *parent) { - int i; + int i, num_elements = 0; fr_dict_attr_t const *da; char dict_name[FR_DICT_ATTR_MAX_NAME_LEN + 1]; char parent_name[FR_DICT_ATTR_MAX_NAME_LEN + 1]; - if (parent->type != FR_TYPE_STRUCT) { - fprintf(stderr, "%s is not a struct\n", parent->name); - return; + CHECK_TYPE(parent); + + da_normalize_name(fr_dict_root(parent->dict), dict_name); + da_normalize_name(parent, parent_name); + + fprintf(fp, "fr_stats_link_t const fr_%s_%s_stats_link_t[] = {\n", dict_name, parent_name); + + fprintf(fp, "\t.name = \"fr_%s_%s_t\",\n", dict_name, parent_name); + fprintf(fp, "\t.da_p = &attr_%s,\n", parent_name); + fprintf(fp, "\t.size = sizeof(fr_%s_%s_t),\n", dict_name, parent_name); + + for (i = 1; fr_dict_attr_child_by_num(parent, i) != NULL; i++) { + num_elements = i; + } + fprintf(fp, "\t.num_elements = %d,\n", num_elements); + + fprintf(fp, "\t.entry = {\n"); + + /* + * For locality, also print out data type and size. That way we _can_ dereference the da, but we + * don't _need_ to. + */ + for (i = 1; (da = fr_dict_attr_child_by_num(parent, i)) != NULL; i++) { + fprintf(fp, "\t\t{\n"); + fprintf(fp, "\t\t\t.da_p = &attr_%s_", parent_name); + da_print_name(fp, da); + fprintf(fp, ",\n"); + + fprintf(fp, "\t\t\t.type = %s,\n", fr_type_to_enum_name[da->type]); + + fprintf(fp, "\t\t\t.offset = offsetof(fr_%s_%s_t, ", dict_name, parent_name); + da_print_name(fp, da); + fprintf(fp, "),\n"); + + fprintf(fp, "\t\t\t.size = %u,\n", da->flags.length); + + fprintf(fp, "\t\t},\n"); } + fprintf(fp, "\t};\n"); + fprintf(fp, "};\n\n"); +} + +static void da_print_attr_autoload(FILE *fp, fr_dict_attr_t const *parent) +{ + int i; + fr_dict_attr_t const *da; + char dict_name[FR_DICT_ATTR_MAX_NAME_LEN + 1]; + char parent_name[FR_DICT_ATTR_MAX_NAME_LEN + 1]; + + CHECK_TYPE(parent); + da_normalize_name(fr_dict_root(parent->dict), dict_name); da_normalize_name(parent, parent_name); @@ -541,17 +597,17 @@ static void da_print_attr_autoload(fr_dict_attr_t const *parent) * * @todo - print the enum for the data type */ - printf("{ .out = &attr_%s, .name = \"%s\", .type = %s, .dict = &dict_%s },\n", + fprintf(fp, "{ .out = &attr_%s, .name = \"%s\", .type = %s, .dict = &dict_%s },\n", parent_name, parent->name, fr_type_to_enum_name[parent->type], dict_name); for (i = 1; (da = fr_dict_attr_child_by_num(parent, i)) != NULL; i++) { - printf("{ .out = &attr_%s_", parent_name); - da_print_name(da); - printf(", .name = \".%s\", .type = %s, .dict = &dict_%s },\n", + fprintf(fp, "{ .out = &attr_%s_", parent_name); + da_print_name(fp, da); + fprintf(fp, ", .name = \".%s\", .type = %s, .dict = &dict_%s },\n", da->name, fr_type_to_enum_name[da->type], dict_name); } - printf("\n\n"); + fprintf(fp, "\n\n"); } static void _raddict_export(fr_dict_t const *dict, uint64_t *count, uintptr_t *low, uintptr_t *high, fr_dict_attr_t const *da, unsigned int lvl) @@ -834,19 +890,19 @@ int main(int argc, char *argv[]) break; case RADICT_OUT_STRUCT: - da_print_struct(da); + da_print_struct(stdout, da); break; case RADICT_OUT_STATS_LINK: - da_print_stats_link(da); + da_print_stats_link(stdout, da); break; case RADICT_OUT_ATTR_DEF: - da_print_attr_def(da); + da_print_attr_def(stdout, da); break; case RADICT_OUT_ATTR_AUTOLOAD: - da_print_attr_autoload(da); + da_print_attr_autoload(stdout, da); break; } found = true; diff --git a/src/lib/util/stats.c b/src/lib/util/stats.c index ac83ec7d7fb..aac8850cbaa 100644 --- a/src/lib/util/stats.c +++ b/src/lib/util/stats.c @@ -24,68 +24,309 @@ RCSID("$Id$") #include -/** Define dictionary attributes for a given statistics structure. +// convert a set of pairs to a struct + +/** Convert a statistics structure to #fr_pair_t * - * @param parent the parent attribute under which statistics are defined - * @param stats the statistics mapping structure. - * @return - * - <0 on error - * - 0 on success */ -int fr_stats_attr_init(fr_dict_attr_t *parent, fr_stats_struct_t const *stats) +int fr_stats_to_pairs(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_stats_instance_t const *inst) { - fr_stats_entry_t const *entry; - fr_dict_t *dict; + size_t i; + fr_stats_link_t const *def = inst->def; + uint8_t const *in = inst->stats; + fr_pair_t *parent, *vp; + + parent = fr_pair_afrom_da(ctx, *(def->root_p)); + if (!parent) return -1; + + for (i = 0; i < def->num_elements; i++) { + uint8_t const *field; + + vp = fr_pair_afrom_da(parent, *(def->entry[i].da_p)); + if (!vp) goto fail; + + field = ((uint8_t const *) in) + def->entry[i].offset; + + /* + * Strings of length 0 are "const char *". + * + * Everything else is an in-line field. + */ + switch (def->entry[i].type) { + case FR_TYPE_STRING: + if (!def->entry[i].size) { + char const *str; + + memcpy(&str, field, sizeof(str)); + + if (fr_pair_value_strdup(vp, str, false) < 0) goto fail; + break; + } + FALL_THROUGH; + + case FR_TYPE_OCTETS: + case FR_TYPE_UINT16: + case FR_TYPE_UINT32: + case FR_TYPE_UINT64: + case FR_TYPE_IPV4_ADDR: + case FR_TYPE_IPV6_ADDR: + fr_assert(def->entry[i].size > 0); + + if (fr_value_box_from_network(vp, &vp->data, def->entry[i].type, vp->da, + &FR_DBUFF_TMP(field, def->entry[i].size), def->entry[i].size, false) < 0) { + goto fail; + } + break; - dict = fr_dict_unconst(fr_dict_by_da(parent)); + case FR_TYPE_DATE: + memcpy(&vp->vp_date, field, sizeof(vp->vp_date)); + break; - for (entry = &stats->table[0]; entry->name != NULL; entry++) { - fr_dict_attr_flags_t flags = { - .internal = true, - .counter = entry->flags.counter, - }; + case FR_TYPE_TIME_DELTA: + memcpy(&vp->vp_time_delta, field, sizeof(vp->vp_time_delta)); + break; - fr_assert(entry->number > 0); - fr_assert((entry->type == FR_TYPE_TIME_DELTA) || (fr_type_is_integer(entry->type))); + default: + fr_strerror_printf("Unsupported data type '%s'", fr_type_to_str(def->entry[i].type)); + return -1; + } - if (fr_dict_attr_add(dict, parent, entry->name, entry->number, entry->type, &flags) < 0) return -1; + if (fr_pair_append(&parent->vp_group, vp) < 0) goto fail; + } + + if (fr_pair_append(out, parent) < 0) { + fail: + talloc_free(parent); + return -1; } return 0; } -/** Add statistics VPs for a particular struct / context +int fr_stats_from_pairs(TALLOC_CTX *ctx, fr_stats_instance_t *inst, fr_pair_list_t *in) +{ + size_t i; + fr_stats_link_t const *def = inst->def; + uint8_t *out = inst->stats; + fr_pair_t const *parent, *vp; + + parent = fr_pair_find_by_da(in, NULL, *(def->root_p)); + if (!parent) return -1; + + memset(out, 0, inst->def->size); + + for (i = 0; i < def->num_elements; i++) { + uint8_t *field; + + vp = fr_pair_find_by_da(&parent->vp_group, NULL, *(def->entry[i].da_p)); + if (!vp) continue; + + field = ((uint8_t *) out) + def->entry[i].offset; + + /* + * Strings of length 0 are "const char *". + * + * Everything else is an in-line field. + */ + switch (def->entry[i].type) { + case FR_TYPE_STRING: + if (!def->entry[i].size) { + char const *str; + + str = talloc_bstrndup(ctx, vp->vp_strvalue, vp->vp_length); + if (!str) return -1; + + memcpy(&field, &str, sizeof(str)); + break; + } + FALL_THROUGH; + + case FR_TYPE_OCTETS: + if (vp->vp_length <= def->entry[i].size) { + memcpy(field, vp->vp_ptr, vp->vp_length); + } else { + memcpy(field, vp->vp_ptr, def->entry[i].size); + } + break; + +#undef COPY +#define COPY(_type, _field) \ + case _type: \ + memcpy(field, &vp->vp_ ## _field, sizeof(vp->vp_ ##_field)); \ + break + + COPY(FR_TYPE_UINT16, uint16); + COPY(FR_TYPE_UINT32, uint32); + COPY(FR_TYPE_UINT64, uint64); + + COPY(FR_TYPE_IPV4_ADDR, ipv4addr); /* struct in_addr, and not vp_ip */ + COPY(FR_TYPE_IPV6_ADDR, ipv6addr); /* struct in6_addr, and not vp_ip */ + + COPY(FR_TYPE_DATE, date); + COPY(FR_TYPE_TIME_DELTA, time_delta); + + default: + fr_strerror_printf("Unsupported data type '%s'", fr_type_to_str(def->entry[i].type)); + return -1; + } + } + + return 0; +} + + +#undef add +#define add(_type, _out, _in) \ +do { \ + _type _a, _b, _sum; \ + memcpy(&_a, &_out, sizeof(_a)); \ + memcpy(&_b, &_in, sizeof(_b)); \ + _sum = _a + _b; \ + memcpy(&_out, &_sum, sizeof(_sum)); \ +} while (0) + +/** Merge to statistics structures + * + * @todo - ensure that the struct magic is the same as the def magic * - * @param parent structural vp where the children will be added - * @param stats structure which maps between #fr_dict_attr_t and internal stats structures - * @param ctx the internal structure holding the stastics - * @return - * - <0 on error - * - 0 on success */ -int fr_stats_pair_add(fr_pair_t *parent, fr_stats_struct_t const *stats, void const *ctx) +static int stats_merge_internal(fr_stats_link_t const *def, void *out, void const *in) { - fr_stats_entry_t const *entry; + size_t i; - fr_assert(fr_type_is_structural(parent->vp_type)); + for (i = 0; i < def->num_elements; i++) { + uint8_t const *field_in; + uint8_t const *field_out; - for (entry = &stats->table[0]; entry->name != NULL; entry++) { - fr_pair_t *vp; - fr_dict_attr_t const *da; + field_in = ((uint8_t const *) in) + def->entry[i].offset; + field_out = ((uint8_t const *) out) + def->entry[i].offset; - da = fr_dict_attr_child_by_num(parent->da, entry->number); - if (!da) { - fr_strerror_printf("Unknown child %d for parent %s", entry->number, parent->da->name); - return -1; + switch (def->entry[i].type) { + case FR_TYPE_UINT16: + add(uint16_t, field_out, field_in); + break; + + case FR_TYPE_UINT32: + add(uint32_t, field_out, field_in); + break; + + case FR_TYPE_UINT64: + add(uint64_t, field_out, field_in); + break; + + default: + break; } + } + + return 0; +} + +/** Public API for merging two statistics structures + * + */ +int fr_stats_merge(fr_stats_instance_t *out, fr_stats_instance_t const *in) +{ + if (out->def != in->def) { + fr_strerror_printf("Cannot merge stats into structure %s from different structure %s", + out->def->name, in->def->name); + return -1; + } - vp = fr_pair_afrom_da(parent, da); - if (!vp) return -1; + return stats_merge_internal(out->def, out->stats, in->stats); +} - if (fr_value_box_memcpy_in(&vp->data, ((uint8_t const *) ctx) + entry->offset) < 0) return -1; - fr_pair_append(&parent->vp_group, vp); +void fr_stats_iter_init(fr_stats_instance_t const *in, fr_stats_iter_t *iter) +{ + iter->inst = in; + iter->current = 0; +} + +bool fr_stats_iter_next(fr_stats_iter_t *iter) +{ + if (iter->current < iter->inst->def->num_elements) { + iter->current++; + return true; } + return false; +} + +int fr_stats_iter_to_value_box(TALLOC_CTX *ctx, fr_value_box_t **out, fr_stats_iter_t *iter) +{ + fr_value_box_t *box; + uint8_t const *field; + fr_dict_attr_t const *da; + + fr_assert(iter->inst); + fr_assert(iter->current < iter->inst->def->num_elements); + + da = *(iter->inst->def->entry[iter->current].da_p); + fr_assert(da != NULL); + + box = fr_value_box_alloc(ctx, iter->inst->def->entry[iter->current].type, da); + if (!box) return -1; + + field = ((uint8_t const *) iter->inst->stats) + iter->inst->def->entry[iter->current].offset; + +#undef COPY +#define COPY(_type, _field) \ + case _type: \ + memcpy(&box->vb_ ## _field, field, sizeof(box->vb_ ## _field)); \ + break + + switch (box->type) { + case FR_TYPE_STRING: + if (!iter->inst->def->entry[iter->current].size) { + char const *str; + + memcpy(&str, field, sizeof(str)); + + if (fr_value_box_strdup(box, box, da, str, false) < 0) { + fail: + talloc_free(box); + return -1; + } + } else { + uint8_t const *end; + + /* + * Find the trailing NUL within the fixed-size field. + */ + end = memchr(field, '\0', iter->inst->def->entry[iter->current].size); + + if (fr_value_box_bstrndup(box, box, da, (char const *) field, + (size_t) (end - field), false) < 0) { + goto fail; + } + } + break; + + case FR_TYPE_OCTETS: + if (fr_value_box_memdup(box, box, da, field, + iter->inst->def->entry[iter->current].size, false) < 0) { + goto fail; + } + break; + + COPY(FR_TYPE_UINT16, uint16); + COPY(FR_TYPE_UINT32, uint32); + COPY(FR_TYPE_UINT64, uint64); + + COPY(FR_TYPE_IPV4_ADDR, ipv4addr); /* struct in_addr, and not vp_ip */ + COPY(FR_TYPE_IPV6_ADDR, ipv6addr); /* struct in6_addr, and not vp_ip */ + + COPY(FR_TYPE_DATE, date); + COPY(FR_TYPE_TIME_DELTA, time_delta); + + default: + fr_strerror_printf("Unsupported data type '%s'", fr_type_to_str(box->type)); + return -1; + } + + *out = box; return 0; } + + diff --git a/src/lib/util/stats.h b/src/lib/util/stats.h index fb888897784..99308162822 100644 --- a/src/lib/util/stats.h +++ b/src/lib/util/stats.h @@ -31,35 +31,78 @@ RCSIDH(stats_h, "$Id$") extern "C" { #endif -/** Define a statistics mapping between dictionary attribute and a field in an internal structure. +/** Link a struct entry to an autoloaded #fr_dict_attr_t * - * Note that the data types used in the internal structure have to match the #fr_type_t. */ typedef struct { - char const *name; //!< Attribute name - fr_type_t type; //!< data type for this statistics - int number; //!< attribute number, so that numbers are consistent - struct { - bool counter; //!< data type is a counter (can add them) - bool gauge; //!< data type is a gauge (take the maximum) - } flags; + fr_dict_attr_t const **da_p; //!< point to the autoload definition of the DA for this field + fr_type_t type; //!< cached for locality and simplicity + size_t offset; //!< from the start of the struct + size_t size; //!< size of this field +} fr_stats_link_entry_t; - size_t offset; //!< from start of the structure -} fr_stats_entry_t; +typedef struct { + char const *name; //!< name of the stats structure + fr_dict_attr_t const **root_p; //!< point to the autoload definition of the DA for this structure + size_t size; //!< overall size of the structure + size_t num_elements; //!< number of elements in the table. Note no "NULL" terminator + fr_stats_link_entry_t entry[]; //!< the field entries, in offset order +} fr_stats_link_t; + +/** Common header for all statistics instances + * + * def pointer to the definition of the structure for this instance + * path the full path to this instance + * uctx for any necessary callbacks + * entry in the doubly linked list of statics which all use the same "def". + */ +#define STATS_HEADER_COMMON \ + fr_stats_link_t const *def; \ + char const *path; \ + void *uctx; \ + fr_dlist_t entry + +/** Generic statistics structure + * + */ +typedef struct { + STATS_HEADER_COMMON; //!< common header -#define STATS_ENTRY_TERMINATOR { .attr = NULL } + uint8_t stats[]; //!< generic statistics data +} fr_stats_instance_t; -/** Define a statistics mapping between a public name and an entire internal structure +/** Macro to define a typedef for a particular kind of statistics * */ +#define FR_STATS_TYPEDEF(_name, _type) \ + typedef struct { \ + STATS_HEADER_COMMON; \ + _type stats; \ + } fr_stats_## _name ## _instance_t + +/** Macro used when declaring a variable which contains the statistics + * + */ +#define FR_STATS_ENTRY(_name) fr_stats_## _name ## _instance_t + +/** Macro used to increment one field in the statistics structure + * + */ +#define FR_STATS_INC(_var, _field) _var->stats._field++ + typedef struct { - char const *name; //!< of this structure for public consumption - fr_stats_entry_t table[]; //!< of mappings -} fr_stats_struct_t; + fr_stats_instance_t const *inst; + unsigned int current; +} fr_stats_iter_t; + +int fr_stats_to_pairs(TALLOC_CTX *ctx, fr_pair_list_t *out, fr_stats_instance_t const *in) CC_HINT(nonnull); +int fr_stats_from_pairs(TALLOC_CTX *ctx, fr_stats_instance_t *out, fr_pair_list_t *in) CC_HINT(nonnull); +int fr_stats_merge(fr_stats_instance_t *out, fr_stats_instance_t const *in) CC_HINT(nonnull); -int fr_stats_attr_init(fr_dict_attr_t *parent, fr_stats_struct_t const *stats) CC_HINT(nonnull); +void fr_stats_iter_init(fr_stats_instance_t const *in, fr_stats_iter_t *iter); +bool fr_stats_iter_next(fr_stats_iter_t *iter); +int fr_stats_iter_to_value_box(TALLOC_CTX *ctx, fr_value_box_t **out, fr_stats_iter_t *iter); -int fr_stats_pair_add(fr_pair_t *parent, fr_stats_struct_t const *stats, void const *ctx) CC_HINT(nonnull); #ifdef __cplusplus }