stats: add derive support; add DERIVE_DIV type

Add basic derive counter support, where a counters value is derived from
2 other counters.

Add DERIVE_DIV that divides the first counter value by the second
counters value.

Convert `decoder.avg_pkt_size` to be derived from `decoder.bytes` and
`decoder.pkts`.

Ticket: #5615.
pull/14639/head
Victor Julien 4 months ago
parent b145e389ab
commit 6524a3fb5c

@ -1,4 +1,4 @@
/* Copyright (C) 2007-2024 Open Information Security Foundation
/* Copyright (C) 2007-2025 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
@ -56,8 +56,9 @@ enum {
STATS_TYPE_AVERAGE = 2,
STATS_TYPE_MAXIMUM = 3,
STATS_TYPE_FUNC = 4,
STATS_TYPE_DERIVE_DIV = 5,
STATS_TYPE_MAX = 5,
STATS_TYPE_MAX = 6,
};
/**
@ -595,6 +596,20 @@ static void StatsReleaseCounter(StatsCounter *pc)
}
}
/** \internal
* \brief Get ID for counters referenced in a derive counter
* \retval id (>=1) or 0 on error
*/
static uint16_t GetIdByName(const StatsPublicThreadContext *pctx, const char *name)
{
for (const StatsCounter *c = pctx->head; c != NULL; c = c->next) {
if (strcmp(name, c->name) == 0) {
return c->id;
}
}
return 0;
}
/**
* \brief Registers a counter.
*
@ -606,8 +621,8 @@ static void StatsReleaseCounter(StatsCounter *pc)
* present counter on success
* \retval 0 on failure
*/
static uint16_t StatsRegisterQualifiedCounter(
const char *name, StatsPublicThreadContext *pctx, int type_q, uint64_t (*Func)(void))
static uint16_t StatsRegisterQualifiedCounter(const char *name, StatsPublicThreadContext *pctx,
int type_q, uint64_t (*Func)(void), const char *dname1, const char *dname2)
{
StatsCounter **head = &pctx->head;
StatsCounter *temp = NULL;
@ -634,14 +649,27 @@ static uint16_t StatsRegisterQualifiedCounter(
if (temp != NULL)
return(temp->id);
uint16_t did1 = 0;
uint16_t did2 = 0;
if (type_q == STATS_TYPE_DERIVE_DIV) {
did1 = GetIdByName(pctx, dname1);
did2 = GetIdByName(pctx, dname2);
if (did1 == 0 || did2 == 0) {
return 0;
}
}
/* if we reach this point we don't have a counter registered by this name */
if ((pc = SCCalloc(1, sizeof(StatsCounter))) == NULL)
return 0;
/* assign a unique id to this StatsCounter. The id is local to this
* thread context. Please note that the id start from 1, and not 0 */
pc->id = ++(pctx->curr_id);
if (type_q == STATS_TYPE_DERIVE_DIV) {
pc->id = ++pctx->derive_id;
} else {
pc->id = ++(pctx->curr_id);
}
/* for AVG counters we use 2 indices into the tables: one for values,
* the other to track updates. */
if (type_q == STATS_TYPE_AVERAGE)
@ -655,6 +683,8 @@ static uint16_t StatsRegisterQualifiedCounter(
pc->type = type_q;
pc->Func = Func;
pc->did1 = did1;
pc->did2 = did2;
/* we now add the counter to the list */
if (prev == NULL)
@ -740,7 +770,7 @@ static int StatsOutput(ThreadVars *tv)
/* copy private table to a local variable to loop it w/o lock */
bool skip = false;
SCSpinLock(&sts->ctx->lock);
uint16_t table_size = sts->ctx->curr_id + 1;
const uint16_t table_size = sts->ctx->curr_id + sts->ctx->derive_id + 1;
if (sts->ctx->copy_of_private == NULL) {
skip = true;
} else {
@ -771,6 +801,12 @@ static int StatsOutput(ThreadVars *tv)
/* skip updates row */
i++;
break;
case STATS_TYPE_DERIVE_DIV:
SCLogDebug("counter %u/%u is derived from counters %u / %u", pc->id, pc->gid,
pc->did1, pc->did2);
thread_table[pc->gid].value = thread_table_from_private[pc->did1].v;
thread_table[pc->gid].updates = thread_table_from_private[pc->did2].v;
break;
default:
SCLogDebug("Counter %s (%u:%u) value %" PRIu64, pc->name, pc->id, pc->gid,
thread_table_from_private[i].v);
@ -824,6 +860,7 @@ static int StatsOutput(ThreadVars *tv)
switch (e->type) {
case STATS_TYPE_AVERAGE:
case STATS_TYPE_DERIVE_DIV:
if (e->value > 0 && e->updates > 0) {
r->value = (uint64_t)(e->value / e->updates);
}
@ -853,6 +890,7 @@ static int StatsOutput(ThreadVars *tv)
table[x].value = m->value;
break;
case STATS_TYPE_AVERAGE:
case STATS_TYPE_DERIVE_DIV:
if (m->value > 0 && m->updates > 0) {
table[x].value = (uint64_t)(m->value / m->updates);
}
@ -1000,7 +1038,8 @@ void StatsSpawnThreads(void)
*/
StatsCounterId StatsRegisterCounter(const char *name, StatsThreadContext *stats)
{
uint16_t id = StatsRegisterQualifiedCounter(name, &stats->pub, STATS_TYPE_NORMAL, NULL);
uint16_t id =
StatsRegisterQualifiedCounter(name, &stats->pub, STATS_TYPE_NORMAL, NULL, NULL, NULL);
StatsCounterId s = { .id = id };
return s;
}
@ -1018,7 +1057,8 @@ StatsCounterId StatsRegisterCounter(const char *name, StatsThreadContext *stats)
*/
StatsCounterAvgId StatsRegisterAvgCounter(const char *name, StatsThreadContext *stats)
{
uint16_t id = StatsRegisterQualifiedCounter(name, &stats->pub, STATS_TYPE_AVERAGE, NULL);
uint16_t id =
StatsRegisterQualifiedCounter(name, &stats->pub, STATS_TYPE_AVERAGE, NULL, NULL, NULL);
StatsCounterAvgId s = { .id = id };
return s;
}
@ -1036,7 +1076,8 @@ StatsCounterAvgId StatsRegisterAvgCounter(const char *name, StatsThreadContext *
*/
StatsCounterMaxId StatsRegisterMaxCounter(const char *name, StatsThreadContext *stats)
{
uint16_t id = StatsRegisterQualifiedCounter(name, &stats->pub, STATS_TYPE_MAXIMUM, NULL);
uint16_t id =
StatsRegisterQualifiedCounter(name, &stats->pub, STATS_TYPE_MAXIMUM, NULL, NULL, NULL);
StatsCounterMaxId s = { .id = id };
return s;
}
@ -1060,7 +1101,36 @@ StatsCounterGlobalId StatsRegisterGlobalCounter(const char *name, uint64_t (*Fun
BUG_ON(stats_ctx == NULL);
#endif
uint16_t id = StatsRegisterQualifiedCounter(
name, &(stats_ctx->global_counter_ctx), STATS_TYPE_FUNC, Func);
name, &(stats_ctx->global_counter_ctx), STATS_TYPE_FUNC, Func, NULL, NULL);
s.id = id;
return s;
}
/**
* \brief Registers a counter which tracks the result of the calculating the value
* of counter dname1 divided by the value of the counter dname2
*
* \param name Name of the counter, to be registered
* \param dname1 First counter name
* \param dname2 Second counter name
*
* Both counters need to already be registered in this thread.
*
* \retval id Counter id for the newly registered counter, or the already
* present counter
*/
StatsCounterDeriveId StatsRegisterDeriveDivCounter(
const char *name, const char *dname1, const char *dname2, StatsThreadContext *stats)
{
StatsCounterDeriveId s = { .id = 0 };
#if defined(UNITTESTS) || defined(FUZZ)
if (stats_ctx == NULL)
return s;
#else
BUG_ON(stats_ctx == NULL);
#endif
uint16_t id = StatsRegisterQualifiedCounter(
name, &stats->pub, STATS_TYPE_DERIVE_DIV, NULL, dname1, dname2);
s.id = id;
return s;
}
@ -1105,15 +1175,29 @@ static void CountersIdHashFreeFunc(void *data)
static int StatsThreadSetupPublic(StatsPublicThreadContext *pctx)
{
size_t array_size = pctx->curr_id + 1;
size_t array_size = pctx->curr_id + pctx->derive_id + 1;
pctx->pc_array = SCCalloc(array_size, sizeof(StatsCounter *));
if (pctx->pc_array == NULL) {
return -1;
}
/* regular counters that get direct updates by their id as idx */
for (StatsCounter *pc = pctx->head; pc != NULL; pc = pc->next) {
SCLogDebug("pc %s gid %u id %u", pc->name, pc->gid, pc->id);
BUG_ON(pctx->pc_array[pc->id] != NULL);
pctx->pc_array[pc->id] = pc;
if (pc->type != STATS_TYPE_DERIVE_DIV) {
SCLogDebug("pc %s gid %u id %u", pc->name, pc->gid, pc->id);
BUG_ON(pctx->pc_array[pc->id] != NULL);
pctx->pc_array[pc->id] = pc;
}
}
/* derive counters are not updated by the thread itself and will be put
* at the end of the array */
for (StatsCounter *pc = pctx->head; pc != NULL; pc = pc->next) {
if (pc->type == STATS_TYPE_DERIVE_DIV) {
uint16_t id = pctx->curr_id + pc->id;
SCLogDebug("STATS_TYPE_DERIVE_DIV: pc %s gid %u pc->id %u id %u", pc->name, pc->gid,
pc->id, id);
BUG_ON(pctx->pc_array[id] != NULL);
pctx->pc_array[id] = pc;
}
}
SCLogDebug("array_size %u memory %" PRIu64, (uint32_t)array_size,
@ -1365,7 +1449,7 @@ void StatsThreadCleanup(StatsThreadContext *stats)
static StatsCounterId RegisterCounter(
const char *name, const char *tm_name, StatsPublicThreadContext *pctx)
{
uint16_t id = StatsRegisterQualifiedCounter(name, pctx, STATS_TYPE_NORMAL, NULL);
uint16_t id = StatsRegisterQualifiedCounter(name, pctx, STATS_TYPE_NORMAL, NULL, NULL, NULL);
StatsCounterId s = { .id = id };
return s;
}

@ -43,6 +43,12 @@ typedef struct StatsCounterGlobalId {
uint16_t id;
} StatsCounterGlobalId;
/* derive counters are counters that are derived from 2 other
* counters. */
typedef struct StatsCounterDeriveId {
uint16_t id;
} StatsCounterDeriveId;
/**
* \brief Container to hold the counter variable
*/
@ -55,6 +61,11 @@ typedef struct StatsCounter_ {
/* global id, used in output */
uint16_t gid;
/* derive id's: thread specific id's for the 2 counters part
* of this derive counter. */
uint16_t did1;
uint16_t did2;
/* when using type STATS_TYPE_Q_FUNC this function is called once
* to get the counter value, regardless of how many threads there are. */
uint64_t (*Func)(void);
@ -88,6 +99,10 @@ typedef struct StatsPublicThreadContext_ {
/* holds the total no of counters already assigned for this perf context */
uint16_t curr_id;
/* separate id space for derive counters. These are set up per thread, but should not be part
* the StatsLocalCounter array as they are not updated in the thread directly. */
uint16_t derive_id;
/* array of pointers to the StatsCounters in `head` above, indexed by the per
* thread counter id.
* Size is `curr_id + 1` after all counters have been registered.
@ -135,6 +150,9 @@ StatsCounterAvgId StatsRegisterAvgCounter(const char *, StatsThreadContext *);
StatsCounterMaxId StatsRegisterMaxCounter(const char *, StatsThreadContext *);
StatsCounterGlobalId StatsRegisterGlobalCounter(const char *cname, uint64_t (*Func)(void));
StatsCounterDeriveId StatsRegisterDeriveDivCounter(
const char *cname, const char *dname1, const char *dname2, StatsThreadContext *);
/* functions used to update local counter values */
void StatsCounterAddI64(StatsThreadContext *, StatsCounterId, int64_t);
void StatsCounterSetI64(StatsThreadContext *, StatsCounterId, int64_t);

@ -674,7 +674,8 @@ void DecodeRegisterPerfCounters(DecodeThreadVars *dtv, ThreadVars *tv)
dtv->counter_ipv4inipv6 = StatsRegisterCounter("decoder.ipv4_in_ipv6", &tv->stats);
dtv->counter_ipv6inipv6 = StatsRegisterCounter("decoder.ipv6_in_ipv6", &tv->stats);
dtv->counter_mpls = StatsRegisterCounter("decoder.mpls", &tv->stats);
dtv->counter_avg_pkt_size = StatsRegisterAvgCounter("decoder.avg_pkt_size", &tv->stats);
dtv->counter_avg_pkt_size = StatsRegisterDeriveDivCounter(
"decoder.avg_pkt_size", "decoder.bytes", "decoder.pkts", &tv->stats);
dtv->counter_max_pkt_size = StatsRegisterMaxCounter("decoder.max_pkt_size", &tv->stats);
dtv->counter_max_mac_addrs_src =
StatsRegisterMaxCounter("decoder.max_mac_addrs_src", &tv->stats);
@ -789,7 +790,6 @@ void DecodeUpdatePacketCounters(ThreadVars *tv,
{
StatsCounterIncr(&tv->stats, dtv->counter_pkts);
StatsCounterAddI64(&tv->stats, dtv->counter_bytes, GET_PKT_LEN(p));
StatsCounterAvgAddI64(&tv->stats, dtv->counter_avg_pkt_size, GET_PKT_LEN(p));
StatsCounterMaxUpdateI64(&tv->stats, dtv->counter_max_pkt_size, GET_PKT_LEN(p));
}

@ -967,7 +967,7 @@ typedef struct DecodeThreadVars_
/** stats/counters */
StatsCounterId counter_pkts;
StatsCounterId counter_bytes;
StatsCounterAvgId counter_avg_pkt_size;
StatsCounterDeriveId counter_avg_pkt_size;
StatsCounterMaxId counter_max_pkt_size;
StatsCounterMaxId counter_max_mac_addrs_src;
StatsCounterMaxId counter_max_mac_addrs_dst;

Loading…
Cancel
Save