• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OISF / suricata / 23374838686

21 Mar 2026 07:29AM UTC coverage: 59.341% (-20.0%) from 79.315%
23374838686

Pull #15075

github

web-flow
Merge 90b4e834f into 6587e363a
Pull Request #15075: Stack 8001 v16.4

38 of 70 new or added lines in 10 files covered. (54.29%)

34165 existing lines in 563 files now uncovered.

119621 of 201584 relevant lines covered (59.34%)

650666.92 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

25.27
/src/counters.c
1
/* Copyright (C) 2007-2025 Open Information Security Foundation
2
 *
3
 * You can copy, redistribute or modify this Program under the terms of
4
 * the GNU General Public License version 2 as published by the Free
5
 * Software Foundation.
6
 *
7
 * This program is distributed in the hope that it will be useful,
8
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10
 * GNU General Public License for more details.
11
 *
12
 * You should have received a copy of the GNU General Public License
13
 * version 2 along with this program; if not, write to the Free Software
14
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15
 * 02110-1301, USA.
16
 */
17

18
/**
19
 * \file
20
 *
21
 * \author Anoop Saldanha <anoopsaldanha@gmail.com>
22
 * \author Victor Julien <victor@inliniac.net>
23
 *
24
 * Engine stats API
25
 */
26

27
#include "suricata-common.h"
28
#include "counters.h"
29

30
#include "suricata.h"
31
#include "threadvars.h"
32

33
#include "output.h"
34
#include "output-json-stats.h"
35

36
#include "util-byte.h"
37
#include "util-conf.h"
38
#include "util-hash.h"
39
#include "util-time.h"
40

41
#include "tm-threads.h"
42
#include "util-privs.h"
43

44
/* Time interval for syncing the local counters with the global ones */
UNCOV
45
#define STATS_WUT_TTS 3
×
46

47
/* Time interval at which the mgmt thread o/p the stats */
48
#define STATS_MGMTT_TTS 8
×
49

50
/**
51
 * \brief Different kinds of qualifier that can be used to modify the behaviour
52
 *        of the counter to be registered
53
 */
54
enum StatsType {
55
    STATS_TYPE_NORMAL = 1,
56
    STATS_TYPE_AVERAGE = 2,
57
    STATS_TYPE_MAXIMUM = 3,
58
    STATS_TYPE_FUNC = 4,
59
    STATS_TYPE_DERIVE_DIV = 5,
60
};
61

62
/**
63
 * \brief per thread store of counters
64
 */
65
typedef struct StatsThreadStore_ {
66
    /** thread name used in output */
67
    const char *name;
68

69
    StatsPublicThreadContext *ctx;
70

71
    struct StatsThreadStore_ *next;
72
} StatsThreadStore;
73

74
/**
75
 * \brief Holds the output interface context for the counter api
76
 */
77
typedef struct StatsGlobalContext_ {
78
    /** list of thread stores: one per thread plus one global */
79
    StatsThreadStore *sts;
80
    SCMutex sts_lock;
81
    int sts_cnt;
82

83
    HashTable *counters_id_hash;
84

85
    StatsPublicThreadContext global_counter_ctx;
86
} StatsGlobalContext;
87

88
static void *stats_thread_data = NULL;
89
static StatsGlobalContext *stats_ctx = NULL;
90
static time_t stats_start_time;
91
/** refresh interval in seconds */
92
static uint32_t stats_tts = STATS_MGMTT_TTS;
93
/** is the stats counter enabled? */
94
static bool stats_enabled = true;
95

96
/**< add decoder events as stats? enabled by default */
97
bool stats_decoder_events = true;
98
const char *stats_decoder_events_prefix = "decoder.event";
99
/**< add stream events as stats? disabled by default */
100
bool stats_stream_events = false;
101

102
static int StatsOutput(ThreadVars *tv);
103
static int StatsThreadRegister(const char *thread_name, StatsPublicThreadContext *);
104
static void StatsReleaseCounters(StatsCounter *head);
105
static int StatsUpdateCounterArray(StatsPrivateThreadContext *pca, StatsPublicThreadContext *pctx);
106

107
/** stats table is filled each interval and passed to the
108
 *  loggers. Initialized at first use. */
109
static StatsTable stats_table = { NULL, NULL, 0, 0, 0, {0 , 0}};
110
static SCMutex stats_table_mutex = SCMUTEX_INITIALIZER;
111
static int stats_loggers_active = 1;
112

113
static uint16_t counters_global_id = 0;
114

115
bool StatsEnabled(void)
UNCOV
116
{
×
UNCOV
117
    return stats_enabled;
×
UNCOV
118
}
×
119

120
static void StatsPublicThreadContextInit(StatsPublicThreadContext *t)
121
{
2✔
122
    SCSpinInit(&t->lock, 0);
2✔
123
}
2✔
124

125
static void StatsPublicThreadContextCleanup(StatsPublicThreadContext *t)
UNCOV
126
{
×
UNCOV
127
    SCSpinLock(&t->lock);
×
UNCOV
128
    SCFree(t->copy_of_private);
×
UNCOV
129
    SCFree(t->pc_array);
×
UNCOV
130
    StatsReleaseCounters(t->head);
×
UNCOV
131
    t->head = NULL;
×
UNCOV
132
    SC_ATOMIC_SET(t->sync_now, false);
×
UNCOV
133
    t->curr_id = 0;
×
UNCOV
134
    SCSpinUnlock(&t->lock);
×
UNCOV
135
    SCSpinDestroy(&t->lock);
×
UNCOV
136
}
×
137

138
/**
139
 * \brief Adds a value of type uint64_t to the local counter.
140
 *
141
 * \param id  ID of the counter as set by the API
142
 * \param pca Counter array that holds the local counter for this TM
143
 * \param x   Value to add to this local counter
144
 */
145
void StatsCounterAddI64(StatsThreadContext *stats, StatsCounterId id, int64_t x)
146
{
2,519,339✔
147
    StatsPrivateThreadContext *pca = &stats->priv;
2,519,339✔
148
#if defined (UNITTESTS) || defined (FUZZ)
2,519,339✔
149
    if (pca->initialized == 0)
2,519,339✔
150
        return;
796,711✔
151
#endif
1,722,628✔
152
#ifdef DEBUG
153
    BUG_ON((id.id < 1) || (id.id > pca->size));
154
#endif
155
    pca->head[id.id].v += x;
1,722,628✔
156
}
1,722,628✔
157

158
/**
159
 * \brief Increments the local counter
160
 *
161
 * \param id  Index of the counter in the counter array
162
 * \param pca Counter array that holds the local counters for this TM
163
 */
164
void StatsCounterIncr(StatsThreadContext *stats, StatsCounterId id)
165
{
10,771,221✔
166
    StatsPrivateThreadContext *pca = &stats->priv;
10,771,221✔
167
#if defined (UNITTESTS) || defined (FUZZ)
10,771,221✔
168
    if (pca->initialized == 0)
10,771,221✔
169
        return;
3,272,594✔
170
#endif
7,498,627✔
171
#ifdef DEBUG
172
    BUG_ON((id.id < 1) || (id.id > pca->size));
173
#endif
174
    pca->head[id.id].v++;
7,498,627✔
175
}
7,498,627✔
176

177
/**
178
 * \brief Decrements the local counter
179
 *
180
 * \param stats per thread counter structure
181
 * \param id  Index of the counter in the counter array
182
 */
183
void StatsCounterDecr(StatsThreadContext *stats, StatsCounterId id)
184
{
16,339✔
185
    StatsPrivateThreadContext *pca = &stats->priv;
16,339✔
186
#if defined(UNITTESTS) || defined(FUZZ)
16,339✔
187
    if (pca->initialized == 0)
16,339✔
UNCOV
188
        return;
×
189
#endif
16,339✔
190
#ifdef DEBUG
191
    BUG_ON((id.id < 1) || (id.id > pca->size));
192
#endif
193
    pca->head[id.id].v--;
16,339✔
194
}
16,339✔
195

196
/**
197
 * \brief set, so overwrite, the value of the local counter
198
 *
199
 * \param stats per thread counter structure
200
 * \param id  Index of the local counter in the counter array
201
 * \param x   The value to set for the counter
202
 */
203
void StatsCounterSetI64(StatsThreadContext *stats, StatsCounterId id, int64_t x)
UNCOV
204
{
×
UNCOV
205
    StatsPrivateThreadContext *pca = &stats->priv;
×
UNCOV
206
#if defined (UNITTESTS) || defined (FUZZ)
×
UNCOV
207
    if (pca->initialized == 0)
×
UNCOV
208
        return;
×
UNCOV
209
#endif
×
210
#ifdef DEBUG
211
    BUG_ON((id.id < 1) || (id.id > pca->size));
212
#endif
UNCOV
213
    pca->head[id.id].v = x;
×
UNCOV
214
}
×
215

216
/**
217
 * \brief update the value of the localmax counter
218
 *
219
 * \param stats per thread counter structure
220
 * \param id  Index of the local counter in the counter array
221
 * \param x   The value to set for the counter
222
 */
223
void StatsCounterMaxUpdateI64(StatsThreadContext *stats, StatsCounterMaxId id, int64_t x)
224
{
2,232,884✔
225
    StatsPrivateThreadContext *pca = &stats->priv;
2,232,884✔
226
#if defined(UNITTESTS) || defined(FUZZ)
2,232,884✔
227
    if (pca->initialized == 0)
2,232,884✔
228
        return;
796,711✔
229
#endif
1,436,173✔
230
#ifdef DEBUG
231
    BUG_ON((id.id < 1) || (id.id > pca->size));
232
#endif
233

234
    if ((int64_t)x > pca->head[id.id].v) {
1,436,173✔
235
        pca->head[id.id].v = x;
22✔
236
    }
22✔
237
}
1,436,173✔
238

239
void StatsCounterAvgAddI64(StatsThreadContext *stats, StatsCounterAvgId id, int64_t x)
240
{
100✔
241
    StatsPrivateThreadContext *pca = &stats->priv;
100✔
242
#if defined(UNITTESTS) || defined(FUZZ)
100✔
243
    if (pca->initialized == 0)
100✔
UNCOV
244
        return;
×
245
#endif
100✔
246
#ifdef DEBUG
247
    BUG_ON((id.id < 1) || (id.id > pca->size));
248
#endif
249

250
    pca->head[id.id].v += x;
100✔
251
    pca->head[id.id + 1].v++;
100✔
252
}
100✔
253

254
static SCConfNode *GetConfig(void)
255
{
1✔
256
    SCConfNode *stats = SCConfGetNode("stats");
1✔
257
    if (stats != NULL)
1✔
UNCOV
258
        return stats;
×
259

260
    SCConfNode *root = SCConfGetNode("outputs");
1✔
261
    SCConfNode *node = NULL;
1✔
262
    if (root != NULL) {
1✔
263
        TAILQ_FOREACH(node, &root->head, next) {
5✔
264
            if (strcmp(node->val, "stats") == 0) {
5✔
UNCOV
265
                return node->head.tqh_first;
×
UNCOV
266
            }
×
267
        }
5✔
268
    }
1✔
269
    return NULL;
1✔
270
}
1✔
271

272
/**
273
 * \brief Initializes stats context
274
 */
275
static void StatsInitCtxPreOutput(void)
276
{
1✔
277
    SCEnter();
1✔
278
    SCConfNode *stats = GetConfig();
1✔
279
    if (stats != NULL) {
1✔
UNCOV
280
        const char *enabled = SCConfNodeLookupChildValue(stats, "enabled");
×
UNCOV
281
        if (enabled != NULL && SCConfValIsFalse(enabled)) {
×
UNCOV
282
            stats_enabled = false;
×
UNCOV
283
            SCLogDebug("Stats module has been disabled");
×
UNCOV
284
            SCReturn;
×
UNCOV
285
        }
×
286
        /* warn if we are using legacy config to enable stats */
UNCOV
287
        SCConfNode *gstats = SCConfGetNode("stats");
×
UNCOV
288
        if (gstats == NULL) {
×
UNCOV
289
            SCLogWarning("global stats config is missing. "
×
UNCOV
290
                         "Stats enabled through legacy stats.log. "
×
UNCOV
291
                         "See %s/configuration/suricata-yaml.html#stats",
×
UNCOV
292
                    GetDocURL());
×
UNCOV
293
        }
×
294

UNCOV
295
        const char *interval = SCConfNodeLookupChildValue(stats, "interval");
×
UNCOV
296
        if (interval != NULL)
×
UNCOV
297
            if (StringParseUint32(&stats_tts, 10, 0, interval) < 0) {
×
298
                SCLogWarning("Invalid value for "
×
299
                             "interval: \"%s\". Resetting to %d.",
×
300
                        interval, STATS_MGMTT_TTS);
×
301
                stats_tts = STATS_MGMTT_TTS;
×
302
            }
×
303

UNCOV
304
        int b;
×
UNCOV
305
        int ret = SCConfGetChildValueBool(stats, "decoder-events", &b);
×
UNCOV
306
        if (ret) {
×
UNCOV
307
            stats_decoder_events = (b == 1);
×
UNCOV
308
        }
×
UNCOV
309
        ret = SCConfGetChildValueBool(stats, "stream-events", &b);
×
UNCOV
310
        if (ret) {
×
UNCOV
311
            stats_stream_events = (b == 1);
×
UNCOV
312
        }
×
313

UNCOV
314
        const char *prefix = NULL;
×
UNCOV
315
        if (SCConfGet("stats.decoder-events-prefix", &prefix) != 1) {
×
UNCOV
316
            prefix = "decoder.event";
×
UNCOV
317
        }
×
UNCOV
318
        stats_decoder_events_prefix = prefix;
×
UNCOV
319
    }
×
320
    SCReturn;
1✔
321
}
1✔
322

323
static void StatsInitCtxPostOutput(void)
324
{
1✔
325
    SCEnter();
1✔
326
    /* Store the engine start time */
327
    time(&stats_start_time);
1✔
328

329
    /* init the lock used by StatsThreadStore */
330
    if (SCMutexInit(&stats_ctx->sts_lock, NULL) != 0) {
1✔
331
        FatalError("error initializing sts mutex");
×
332
    }
×
333

334
    if (stats_enabled && !OutputStatsLoggersRegistered()) {
1✔
335
        stats_loggers_active = 0;
1✔
336

337
        /* if the unix command socket is enabled we do the background
338
         * stats sync just in case someone runs 'dump-counters' */
339
        if (!ConfUnixSocketIsEnable()) {
1✔
340
            SCLogWarning("stats are enabled but no loggers are active");
1✔
341
            stats_enabled = false;
1✔
342
            SCReturn;
1✔
343
        }
1✔
344
    }
1✔
345

346
    SCReturn;
1✔
347
}
1✔
348

349
/**
350
 * \brief Releases the resources allotted to the output context of the
351
 *        Stats API
352
 */
353
static void StatsReleaseCtx(void)
UNCOV
354
{
×
UNCOV
355
    if (stats_ctx == NULL) {
×
UNCOV
356
        SCLogDebug("Counter module has been disabled");
×
UNCOV
357
        return;
×
UNCOV
358
    }
×
359

UNCOV
360
    StatsThreadStore *sts = NULL;
×
UNCOV
361
    StatsThreadStore *temp = NULL;
×
UNCOV
362
    sts = stats_ctx->sts;
×
363

UNCOV
364
    while (sts != NULL) {
×
UNCOV
365
        temp = sts->next;
×
UNCOV
366
        SCFree(sts);
×
UNCOV
367
        sts = temp;
×
UNCOV
368
    }
×
369

UNCOV
370
    if (stats_ctx->counters_id_hash != NULL) {
×
UNCOV
371
        HashTableFree(stats_ctx->counters_id_hash);
×
UNCOV
372
        stats_ctx->counters_id_hash = NULL;
×
UNCOV
373
        counters_global_id = 0;
×
UNCOV
374
    }
×
375

UNCOV
376
    StatsPublicThreadContextCleanup(&stats_ctx->global_counter_ctx);
×
UNCOV
377
    SCFree(stats_ctx);
×
UNCOV
378
    stats_ctx = NULL;
×
379

UNCOV
380
    SCMutexLock(&stats_table_mutex);
×
381
    /* free stats table */
UNCOV
382
    if (stats_table.tstats != NULL) {
×
UNCOV
383
        SCFree(stats_table.tstats);
×
UNCOV
384
        stats_table.tstats = NULL;
×
UNCOV
385
    }
×
386

UNCOV
387
    if (stats_table.stats != NULL) {
×
UNCOV
388
        SCFree(stats_table.stats);
×
UNCOV
389
        stats_table.stats = NULL;
×
UNCOV
390
    }
×
UNCOV
391
    memset(&stats_table, 0, sizeof(stats_table));
×
UNCOV
392
    SCMutexUnlock(&stats_table_mutex);
×
UNCOV
393
}
×
394

395
/**
396
 * \brief management thread. This thread is responsible for writing the stats
397
 *
398
 * \param arg thread var
399
 *
400
 * \retval NULL This is the value that is always returned
401
 */
402
static void *StatsMgmtThread(void *arg)
UNCOV
403
{
×
UNCOV
404
    ThreadVars *tv_local = (ThreadVars *)arg;
×
405

UNCOV
406
    SCSetThreadName(tv_local->name);
×
407

UNCOV
408
    if (tv_local->thread_setup_flags != 0)
×
UNCOV
409
        TmThreadSetupOptions(tv_local);
×
410

411
    /* Set the threads capability */
UNCOV
412
    tv_local->cap_flags = 0;
×
UNCOV
413
    SCDropCaps(tv_local);
×
414

UNCOV
415
    if (stats_ctx == NULL) {
×
416
        SCLogError("Stats API not init"
×
417
                   "StatsInitCounterApi() has to be called first");
×
418
        TmThreadsSetFlag(tv_local, THV_CLOSED | THV_RUNNING_DONE);
×
419
        return NULL;
×
420
    }
×
421

UNCOV
422
    TmModule *tm = &tmm_modules[TMM_STATSLOGGER];
×
UNCOV
423
    BUG_ON(tm->ThreadInit == NULL);
×
UNCOV
424
    int r = tm->ThreadInit(tv_local, NULL, &stats_thread_data);
×
UNCOV
425
    if (r != 0 || stats_thread_data == NULL) {
×
426
        SCLogError("Stats API "
×
427
                   "ThreadInit failed");
×
428
        TmThreadsSetFlag(tv_local, THV_CLOSED | THV_RUNNING_DONE);
×
429
        return NULL;
×
430
    }
×
UNCOV
431
    SCLogDebug("stats_thread_data %p", &stats_thread_data);
×
432

UNCOV
433
    TmThreadsSetFlag(tv_local, THV_INIT_DONE | THV_RUNNING);
×
UNCOV
434
    bool run = TmThreadsWaitForUnpause(tv_local);
×
UNCOV
435
    while (run) {
×
UNCOV
436
        struct timeval cur_timev;
×
UNCOV
437
        gettimeofday(&cur_timev, NULL);
×
UNCOV
438
        struct timespec cond_time = FROM_TIMEVAL(cur_timev);
×
UNCOV
439
        cond_time.tv_sec += (stats_tts);
×
440

441
        /* wait for the set time, or until we are woken up by
442
         * the shutdown procedure */
UNCOV
443
        SCCtrlMutexLock(tv_local->ctrl_mutex);
×
UNCOV
444
        while (1) {
×
UNCOV
445
            if (TmThreadsCheckFlag(tv_local, THV_KILL)) {
×
UNCOV
446
                break;
×
UNCOV
447
            }
×
UNCOV
448
            int rc = SCCtrlCondTimedwait(tv_local->ctrl_cond, tv_local->ctrl_mutex, &cond_time);
×
UNCOV
449
            if (rc == ETIMEDOUT || rc < 0) {
×
UNCOV
450
                break;
×
UNCOV
451
            }
×
UNCOV
452
        }
×
UNCOV
453
        SCCtrlMutexUnlock(tv_local->ctrl_mutex);
×
454

UNCOV
455
        SCMutexLock(&stats_table_mutex);
×
UNCOV
456
        StatsOutput(tv_local);
×
UNCOV
457
        SCMutexUnlock(&stats_table_mutex);
×
458

UNCOV
459
        if (TmThreadsCheckFlag(tv_local, THV_KILL)) {
×
UNCOV
460
            break;
×
UNCOV
461
        }
×
UNCOV
462
    }
×
463

UNCOV
464
    TmThreadsSetFlag(tv_local, THV_RUNNING_DONE);
×
UNCOV
465
    TmThreadWaitForFlag(tv_local, THV_DEINIT);
×
466

UNCOV
467
    r = tm->ThreadDeinit(tv_local, stats_thread_data);
×
UNCOV
468
    if (r != TM_ECODE_OK) {
×
469
        SCLogError("Stats Counter API "
×
470
                   "ThreadDeinit failed");
×
471
    }
×
472

UNCOV
473
    TmThreadsSetFlag(tv_local, THV_CLOSED);
×
UNCOV
474
    return NULL;
×
UNCOV
475
}
×
476

477
void StatsSyncCounters(StatsThreadContext *stats)
UNCOV
478
{
×
UNCOV
479
    StatsUpdateCounterArray(&stats->priv, &stats->pub);
×
UNCOV
480
}
×
481

482
void StatsSyncCountersIfSignalled(StatsThreadContext *stats)
483
{
24,825✔
484
    if (SC_ATOMIC_GET(stats->pub.sync_now)) {
24,825✔
UNCOV
485
        StatsUpdateCounterArray(&stats->priv, &stats->pub);
×
UNCOV
486
    }
×
487
}
24,825✔
488

489
/**
490
 * \brief Wake up thread.  This thread wakes up every TTS(time to sleep) seconds
491
 *        and sets the flag for every ThreadVars' StatsPublicThreadContext
492
 *
493
 * \param arg is NULL always
494
 *
495
 * \retval NULL This is the value that is always returned
496
 */
497
static void *StatsWakeupThread(void *arg)
UNCOV
498
{
×
UNCOV
499
    ThreadVars *tv_local = (ThreadVars *)arg;
×
500

UNCOV
501
    SCSetThreadName(tv_local->name);
×
502

UNCOV
503
    if (tv_local->thread_setup_flags != 0)
×
UNCOV
504
        TmThreadSetupOptions(tv_local);
×
505

506
    /* Set the threads capability */
UNCOV
507
    tv_local->cap_flags = 0;
×
UNCOV
508
    SCDropCaps(tv_local);
×
509

UNCOV
510
    if (stats_ctx == NULL) {
×
511
        SCLogError("Stats API not init"
×
512
                   "StatsInitCounterApi() has to be called first");
×
513
        TmThreadsSetFlag(tv_local, THV_CLOSED | THV_RUNNING_DONE);
×
514
        return NULL;
×
515
    }
×
516

UNCOV
517
    TmThreadsSetFlag(tv_local, THV_INIT_DONE | THV_RUNNING);
×
UNCOV
518
    bool run = TmThreadsWaitForUnpause(tv_local);
×
519

UNCOV
520
    while (run) {
×
UNCOV
521
        struct timeval cur_timev;
×
UNCOV
522
        gettimeofday(&cur_timev, NULL);
×
UNCOV
523
        struct timespec cond_time = FROM_TIMEVAL(cur_timev);
×
UNCOV
524
        cond_time.tv_sec += STATS_WUT_TTS;
×
525

526
        /* wait for the set time, or until we are woken up by
527
         * the shutdown procedure */
UNCOV
528
        SCCtrlMutexLock(tv_local->ctrl_mutex);
×
UNCOV
529
        while (1) {
×
UNCOV
530
            if (TmThreadsCheckFlag(tv_local, THV_KILL)) {
×
UNCOV
531
                break;
×
UNCOV
532
            }
×
UNCOV
533
            int rc = SCCtrlCondTimedwait(tv_local->ctrl_cond, tv_local->ctrl_mutex, &cond_time);
×
UNCOV
534
            if (rc == ETIMEDOUT || rc < 0) {
×
UNCOV
535
                break;
×
UNCOV
536
            }
×
UNCOV
537
        }
×
UNCOV
538
        SCCtrlMutexUnlock(tv_local->ctrl_mutex);
×
539

UNCOV
540
        SCMutexLock(&tv_root_lock);
×
UNCOV
541
        ThreadVars *tv = tv_root[TVT_PPT];
×
UNCOV
542
        while (tv != NULL) {
×
UNCOV
543
            if (tv->stats.pub.head == NULL) {
×
544
                tv = tv->next;
×
545
                continue;
×
546
            }
×
547

UNCOV
548
            SC_ATOMIC_SET(tv->stats.pub.sync_now, true);
×
549

UNCOV
550
            if (tv->inq != NULL) {
×
UNCOV
551
                PacketQueue *q = tv->inq->pq;
×
UNCOV
552
                SCMutexLock(&q->mutex_q);
×
UNCOV
553
                SCCondSignal(&q->cond_q);
×
UNCOV
554
                SCMutexUnlock(&q->mutex_q);
×
UNCOV
555
            }
×
556

UNCOV
557
            tv = tv->next;
×
UNCOV
558
        }
×
559

560
        /* mgt threads for flow manager */
UNCOV
561
        tv = tv_root[TVT_MGMT];
×
UNCOV
562
        while (tv != NULL) {
×
UNCOV
563
            if (tv->stats.pub.head == NULL) {
×
UNCOV
564
                tv = tv->next;
×
UNCOV
565
                continue;
×
UNCOV
566
            }
×
567

UNCOV
568
            SC_ATOMIC_SET(tv->stats.pub.sync_now, true);
×
569

UNCOV
570
            tv = tv->next;
×
UNCOV
571
        }
×
UNCOV
572
        SCMutexUnlock(&tv_root_lock);
×
573

UNCOV
574
        if (TmThreadsCheckFlag(tv_local, THV_KILL)) {
×
UNCOV
575
            break;
×
UNCOV
576
        }
×
UNCOV
577
    }
×
578

UNCOV
579
    TmThreadsSetFlag(tv_local, THV_RUNNING_DONE);
×
UNCOV
580
    TmThreadWaitForFlag(tv_local, THV_DEINIT);
×
UNCOV
581
    TmThreadsSetFlag(tv_local, THV_CLOSED);
×
UNCOV
582
    return NULL;
×
UNCOV
583
}
×
584

585
/**
586
 * \brief Releases a counter
587
 *
588
 * \param pc Pointer to the StatsCounter to be freed
589
 */
590
static void StatsReleaseCounter(StatsCounter *pc)
UNCOV
591
{
×
UNCOV
592
    if (pc != NULL) {
×
UNCOV
593
        SCFree(pc);
×
UNCOV
594
    }
×
UNCOV
595
}
×
596

597
/** \internal
598
 *  \brief Get ID for counters referenced in a derive counter
599
 *  \retval id (>=1) or 0 on error
600
 */
601
static uint16_t GetIdByName(const StatsPublicThreadContext *pctx, const char *name)
602
{
4✔
603
    for (const StatsCounter *c = pctx->head; c != NULL; c = c->next) {
6✔
604
        if (strcmp(name, c->name) == 0) {
6✔
605
            return c->id;
4✔
606
        }
4✔
607
    }
6✔
608
    return 0;
×
609
}
4✔
610

611
/**
612
 * \brief Registers a counter.
613
 *
614
 * \param name    Name of the counter, to be registered
615
 * \param pctx     StatsPublicThreadContext for this tm-tv instance
616
 * \param type_q   Qualifier describing the type of counter to be registered
617
 *
618
 * \retval the counter id for the newly registered counter, or the already
619
 *         present counter on success
620
 * \retval 0 on failure
621
 */
622
static uint16_t StatsRegisterQualifiedCounter(const char *name, StatsPublicThreadContext *pctx,
623
        enum StatsType type_q, uint64_t (*Func)(void), const char *dname1, const char *dname2)
624
{
107,304✔
625
    StatsCounter **head = &pctx->head;
107,304✔
626
    StatsCounter *temp = NULL;
107,304✔
627
    StatsCounter *prev = NULL;
107,304✔
628
    StatsCounter *pc = NULL;
107,304✔
629

630
    if (name == NULL || pctx == NULL) {
107,304✔
631
        SCLogDebug("Counter name, StatsPublicThreadContext NULL");
1✔
632
        return 0;
1✔
633
    }
1✔
634

635
    temp = prev = *head;
107,303✔
636
    while (temp != NULL) {
26,014,842✔
637
        prev = temp;
26,014,092✔
638

639
        if (strcmp(name, temp->name) == 0) {
26,014,092✔
640
            break;
106,553✔
641
        }
106,553✔
642

643
        temp = temp->next;
25,907,539✔
644
    }
25,907,539✔
645

646
    /* We already have a counter registered by this name */
647
    if (temp != NULL)
107,303✔
648
        return(temp->id);
106,553✔
649

650
    uint16_t did1 = 0;
750✔
651
    uint16_t did2 = 0;
750✔
652
    if (type_q == STATS_TYPE_DERIVE_DIV) {
750✔
653
        did1 = GetIdByName(pctx, dname1);
2✔
654
        did2 = GetIdByName(pctx, dname2);
2✔
655
        if (did1 == 0 || did2 == 0) {
2✔
656
            return 0;
×
657
        }
×
658
    }
2✔
659

660
    /* if we reach this point we don't have a counter registered by this name */
661
    if ((pc = SCCalloc(1, sizeof(StatsCounter))) == NULL)
750✔
662
        return 0;
×
663

664
    /* assign a unique id to this StatsCounter.  The id is local to this
665
     * thread context.  Please note that the id start from 1, and not 0 */
666
    if (type_q == STATS_TYPE_DERIVE_DIV) {
750✔
667
        pc->id = ++pctx->derive_id;
2✔
668
    } else {
748✔
669
        pc->id = ++(pctx->curr_id);
748✔
670
    }
748✔
671
    /* for AVG counters we use 2 indices into the tables: one for values,
672
     * the other to track updates. */
673
    if (type_q == STATS_TYPE_AVERAGE)
750✔
674
        ++(pctx->curr_id);
2✔
675
    pc->name = name;
750✔
676

677
    /* Precalculate the short name */
678
    if (strrchr(name, '.') != NULL) {
750✔
679
        pc->short_name = &name[strrchr(name, '.') - name + 1];
750✔
680
    }
750✔
681

682
    pc->type = type_q;
750✔
683
    pc->Func = Func;
750✔
684
    pc->did1 = did1;
750✔
685
    pc->did2 = did2;
750✔
686

687
    /* we now add the counter to the list */
688
    if (prev == NULL)
750✔
689
        *head = pc;
4✔
690
    else
746✔
691
        prev->next = pc;
746✔
692

693
    return pc->id;
750✔
694
}
750✔
695

696
/**
697
 * \brief The output interface for the Stats API
698
 */
699
static int StatsOutput(ThreadVars *tv)
UNCOV
700
{
×
UNCOV
701
    const StatsThreadStore *sts = NULL;
×
UNCOV
702
    void *td = stats_thread_data;
×
703

UNCOV
704
    if (counters_global_id == 0)
×
705
        return -1;
×
706

UNCOV
707
    if (stats_table.nstats == 0) {
×
UNCOV
708
        StatsThreadRegister("Global", &stats_ctx->global_counter_ctx);
×
709

UNCOV
710
        uint32_t nstats = counters_global_id;
×
711

UNCOV
712
        stats_table.nstats = nstats;
×
UNCOV
713
        stats_table.stats = SCCalloc(stats_table.nstats, sizeof(StatsRecord));
×
UNCOV
714
        if (stats_table.stats == NULL) {
×
715
            stats_table.nstats = 0;
×
716
            SCLogError("could not alloc memory for stats");
×
717
            return -1;
×
718
        }
×
719

UNCOV
720
        stats_table.ntstats = stats_ctx->sts_cnt;
×
UNCOV
721
        uint32_t array_size = stats_table.nstats * sizeof(StatsRecord);
×
UNCOV
722
        stats_table.tstats = SCCalloc(stats_table.ntstats, array_size);
×
UNCOV
723
        if (stats_table.tstats == NULL) {
×
724
            stats_table.ntstats = 0;
×
725
            SCLogError("could not alloc memory for stats");
×
726
            return -1;
×
727
        }
×
728

UNCOV
729
        stats_table.start_time = stats_start_time;
×
UNCOV
730
    }
×
731

UNCOV
732
    const uint16_t max_id = counters_global_id;
×
UNCOV
733
    if (max_id == 0)
×
734
        return -1;
×
735

736
    /** temporary local table to merge the per thread counters,
737
     *  especially needed for the average counters */
UNCOV
738
    struct CountersMergeTable {
×
UNCOV
739
        enum StatsType type;
×
UNCOV
740
        int64_t value;
×
UNCOV
741
        uint64_t updates;
×
UNCOV
742
    } merge_table[max_id];
×
UNCOV
743
    memset(&merge_table, 0x00,
×
UNCOV
744
           max_id * sizeof(struct CountersMergeTable));
×
745

UNCOV
746
    int thread = stats_ctx->sts_cnt - 1;
×
UNCOV
747
    StatsRecord *table = stats_table.stats;
×
748

749
    /* Loop through the thread counter stores. The global counters
750
     * are in a separate store inside this list. */
UNCOV
751
    sts = stats_ctx->sts;
×
UNCOV
752
    SCLogDebug("sts %p", sts);
×
UNCOV
753
    while (sts != NULL) {
×
UNCOV
754
        DEBUG_VALIDATE_BUG_ON(thread < 0);
×
755

UNCOV
756
        SCLogDebug("Thread %d %s ctx %p", thread, sts->name, sts->ctx);
×
757

758
        /* temporary table for quickly storing the counters for this
759
         * thread store, so that we can post process them outside
760
         * of the thread store lock */
UNCOV
761
        struct CountersMergeTable thread_table[max_id];
×
UNCOV
762
        memset(&thread_table, 0x00,
×
UNCOV
763
                max_id * sizeof(struct CountersMergeTable));
×
764

UNCOV
765
        StatsLocalCounter thread_table_from_private[max_id];
×
UNCOV
766
        memset(&thread_table_from_private, 0x00, max_id * sizeof(StatsLocalCounter));
×
767

768
        /* copy private table to a local variable to loop it w/o lock */
UNCOV
769
        bool skip = false;
×
UNCOV
770
        SCSpinLock(&sts->ctx->lock);
×
UNCOV
771
        const uint16_t table_size = sts->ctx->curr_id + sts->ctx->derive_id + 1;
×
UNCOV
772
        if (sts->ctx->copy_of_private == NULL) {
×
773
            skip = true;
×
UNCOV
774
        } else {
×
UNCOV
775
            memcpy(&thread_table_from_private, sts->ctx->copy_of_private,
×
UNCOV
776
                    table_size * sizeof(StatsLocalCounter));
×
UNCOV
777
        }
×
UNCOV
778
        SCSpinUnlock(&sts->ctx->lock);
×
UNCOV
779
        if (skip)
×
780
            goto next;
×
781

782
        /* loop counters and handle them. This includes the global counters, which
783
         * access the StatsCounters but don't modify them. */
UNCOV
784
        for (uint16_t i = 1; i < table_size; i++) {
×
UNCOV
785
            const StatsCounter *pc = sts->ctx->pc_array[i];
×
UNCOV
786
            thread_table[pc->gid].type = pc->type;
×
787

UNCOV
788
            table[pc->gid].name = pc->name;
×
UNCOV
789
            table[pc->gid].short_name = pc->short_name;
×
790

UNCOV
791
            switch (pc->type) {
×
UNCOV
792
                case STATS_TYPE_FUNC:
×
UNCOV
793
                    if (pc->Func != NULL)
×
UNCOV
794
                        thread_table[pc->gid].value = pc->Func();
×
UNCOV
795
                    break;
×
UNCOV
796
                case STATS_TYPE_AVERAGE:
×
UNCOV
797
                    thread_table[pc->gid].value = thread_table_from_private[i].v;
×
UNCOV
798
                    thread_table[pc->gid].updates = thread_table_from_private[i + 1].v;
×
799
                    /* skip updates row */
UNCOV
800
                    i++;
×
UNCOV
801
                    break;
×
UNCOV
802
                case STATS_TYPE_DERIVE_DIV:
×
UNCOV
803
                    SCLogDebug("counter %u/%u is derived from counters %u / %u", pc->id, pc->gid,
×
UNCOV
804
                            pc->did1, pc->did2);
×
UNCOV
805
                    thread_table[pc->gid].value = thread_table_from_private[pc->did1].v;
×
UNCOV
806
                    thread_table[pc->gid].updates = thread_table_from_private[pc->did2].v;
×
UNCOV
807
                    break;
×
UNCOV
808
                default:
×
UNCOV
809
                    SCLogDebug("Counter %s (%u:%u) value %" PRIu64, pc->name, pc->id, pc->gid,
×
UNCOV
810
                            thread_table_from_private[i].v);
×
811

UNCOV
812
                    thread_table[pc->gid].value = thread_table_from_private[i].v;
×
UNCOV
813
                    break;
×
UNCOV
814
            }
×
UNCOV
815
        }
×
816

817
        /* update merge table */
UNCOV
818
        for (uint16_t c = 0; c < max_id; c++) {
×
UNCOV
819
            const struct CountersMergeTable *e = &thread_table[c];
×
820
            /* thread only sets type if it has a counter
821
             * of this type. */
UNCOV
822
            if (e->type == 0)
×
UNCOV
823
                continue;
×
824

UNCOV
825
            switch (e->type) {
×
UNCOV
826
                case STATS_TYPE_MAXIMUM:
×
UNCOV
827
                    if (e->value > merge_table[c].value)
×
UNCOV
828
                        merge_table[c].value = e->value;
×
UNCOV
829
                    break;
×
UNCOV
830
                case STATS_TYPE_FUNC:
×
UNCOV
831
                    merge_table[c].value = e->value;
×
UNCOV
832
                    break;
×
UNCOV
833
                case STATS_TYPE_AVERAGE:
×
UNCOV
834
                default:
×
UNCOV
835
                    merge_table[c].value += e->value;
×
UNCOV
836
                    break;
×
UNCOV
837
            }
×
UNCOV
838
            merge_table[c].updates += e->updates;
×
UNCOV
839
            merge_table[c].type = e->type;
×
UNCOV
840
        }
×
841

842
        /* update per thread stats table */
UNCOV
843
        for (uint16_t c = 0; c < max_id; c++) {
×
UNCOV
844
            const struct CountersMergeTable *e = &thread_table[c];
×
845
            /* thread only sets type if it has a counter
846
             * of this type. */
UNCOV
847
            if (e->type == 0)
×
UNCOV
848
                continue;
×
849

UNCOV
850
            uint32_t offset = (thread * stats_table.nstats) + c;
×
UNCOV
851
            StatsRecord *r = &stats_table.tstats[offset];
×
852
            /* xfer previous value to pvalue and reset value */
UNCOV
853
            r->pvalue = r->value;
×
UNCOV
854
            r->value = 0;
×
UNCOV
855
            r->name = table[c].name;
×
UNCOV
856
            r->short_name = table[c].short_name;
×
UNCOV
857
            r->tm_name = sts->name;
×
858

UNCOV
859
            switch (e->type) {
×
UNCOV
860
                case STATS_TYPE_AVERAGE:
×
UNCOV
861
                case STATS_TYPE_DERIVE_DIV:
×
UNCOV
862
                    if (e->value > 0 && e->updates > 0) {
×
UNCOV
863
                        r->value = (uint64_t)(e->value / e->updates);
×
UNCOV
864
                    }
×
UNCOV
865
                    break;
×
UNCOV
866
                default:
×
UNCOV
867
                    r->value = e->value;
×
UNCOV
868
                    break;
×
UNCOV
869
            }
×
UNCOV
870
        }
×
871

UNCOV
872
    next:
×
UNCOV
873
        sts = sts->next;
×
UNCOV
874
        thread--;
×
UNCOV
875
    }
×
876

877
    /* transfer 'merge table' to final stats table */
UNCOV
878
    for (uint16_t x = 0; x < max_id; x++) {
×
879
        /* xfer previous value to pvalue and reset value */
UNCOV
880
        table[x].pvalue = table[x].value;
×
UNCOV
881
        table[x].value = 0;
×
UNCOV
882
        table[x].tm_name = "Total";
×
883

UNCOV
884
        const struct CountersMergeTable *m = &merge_table[x];
×
UNCOV
885
        switch (m->type) {
×
UNCOV
886
            case STATS_TYPE_MAXIMUM:
×
UNCOV
887
                if (m->value > table[x].value)
×
UNCOV
888
                    table[x].value = m->value;
×
UNCOV
889
                break;
×
UNCOV
890
            case STATS_TYPE_AVERAGE:
×
UNCOV
891
            case STATS_TYPE_DERIVE_DIV:
×
UNCOV
892
                if (m->value > 0 && m->updates > 0) {
×
UNCOV
893
                    table[x].value = (uint64_t)(m->value / m->updates);
×
UNCOV
894
                }
×
UNCOV
895
                break;
×
UNCOV
896
            default:
×
UNCOV
897
                table[x].value += m->value;
×
UNCOV
898
                break;
×
UNCOV
899
        }
×
UNCOV
900
    }
×
901

902
    /* invoke logger(s) */
UNCOV
903
    if (stats_loggers_active) {
×
UNCOV
904
        OutputStatsLog(tv, td, &stats_table);
×
UNCOV
905
    }
×
UNCOV
906
    return 1;
×
UNCOV
907
}
×
908

909
#ifdef BUILD_UNIX_SOCKET
910
/** \brief callback for getting stats into unix socket
911
 */
912
TmEcode StatsOutputCounterSocket(json_t *cmd,
913
                               json_t *answer, void *data)
UNCOV
914
{
×
UNCOV
915
    json_t *message = NULL;
×
UNCOV
916
    TmEcode r = TM_ECODE_OK;
×
917

UNCOV
918
    if (!stats_enabled) {
×
919
        r = TM_ECODE_FAILED;
×
920
        message = json_string("stats are disabled in the config");
×
UNCOV
921
    } else {
×
UNCOV
922
        SCMutexLock(&stats_table_mutex);
×
UNCOV
923
        if (stats_table.start_time == 0) {
×
924
            r = TM_ECODE_FAILED;
×
925
            message = json_string("stats not yet synchronized");
×
UNCOV
926
        } else {
×
UNCOV
927
            message = StatsToJSON(&stats_table, JSON_STATS_TOTALS|JSON_STATS_THREADS);
×
UNCOV
928
        }
×
UNCOV
929
        SCMutexUnlock(&stats_table_mutex);
×
UNCOV
930
    }
×
UNCOV
931
    json_object_set_new(answer, "message", message);
×
UNCOV
932
    return r;
×
UNCOV
933
}
×
934
#endif /* BUILD_UNIX_SOCKET */
935

936
static void StatsLogSummary(void)
UNCOV
937
{
×
UNCOV
938
    if (!stats_enabled) {
×
UNCOV
939
        return;
×
UNCOV
940
    }
×
UNCOV
941
    uint64_t alerts = 0;
×
UNCOV
942
    SCMutexLock(&stats_table_mutex);
×
UNCOV
943
    if (stats_table.start_time != 0) {
×
UNCOV
944
        const StatsTable *st = &stats_table;
×
UNCOV
945
        for (uint32_t u = 0; u < st->nstats; u++) {
×
UNCOV
946
            const char *name = st->stats[u].name;
×
UNCOV
947
            if (name == NULL || strcmp(name, "detect.alert") != 0)
×
UNCOV
948
                continue;
×
UNCOV
949
            alerts = st->stats[u].value;
×
UNCOV
950
            break;
×
UNCOV
951
        }
×
UNCOV
952
    }
×
UNCOV
953
    SCMutexUnlock(&stats_table_mutex);
×
UNCOV
954
    SCLogInfo("Alerts: %"PRIu64, alerts);
×
UNCOV
955
}
×
956

957
/**
958
 * \brief Initializes the perf counter api.  Things are hard coded currently.
959
 *        More work to be done when we implement multiple interfaces
960
 */
961
void StatsInit(void)
962
{
2✔
963
    BUG_ON(stats_ctx != NULL);
2✔
964
    if ((stats_ctx = SCCalloc(1, sizeof(StatsGlobalContext))) == NULL) {
2✔
965
        FatalError("Fatal error encountered in StatsInitCtx. Exiting...");
×
966
    }
×
967

968
    StatsPublicThreadContextInit(&stats_ctx->global_counter_ctx);
2✔
969
}
2✔
970

971
void StatsSetupPostConfigPreOutput(void)
972
{
1✔
973
    StatsInitCtxPreOutput();
1✔
974
}
1✔
975

976
void StatsSetupPostConfigPostOutput(void)
977
{
1✔
978
    StatsInitCtxPostOutput();
1✔
979
}
1✔
980

981

982
/**
983
 * \brief Spawns the wakeup, and the management thread used by the stats api
984
 *
985
 *  The threads use the condition variable in the thread vars to control
986
 *  their wait loops to make sure the main thread can quickly kill them.
987
 */
988
void StatsSpawnThreads(void)
UNCOV
989
{
×
UNCOV
990
    SCEnter();
×
991

UNCOV
992
    if (!stats_enabled) {
×
UNCOV
993
        SCReturn;
×
UNCOV
994
    }
×
995

UNCOV
996
    ThreadVars *tv_wakeup = NULL;
×
UNCOV
997
    ThreadVars *tv_mgmt = NULL;
×
998

999
    /* spawn the stats wakeup thread */
UNCOV
1000
    tv_wakeup = TmThreadCreateMgmtThread(thread_name_counter_wakeup,
×
UNCOV
1001
                                         StatsWakeupThread, 1);
×
UNCOV
1002
    if (tv_wakeup == NULL) {
×
1003
        FatalError("TmThreadCreateMgmtThread "
×
1004
                   "failed");
×
1005
    }
×
1006

UNCOV
1007
    if (TmThreadSpawn(tv_wakeup) != 0) {
×
1008
        FatalError("TmThreadSpawn failed for "
×
1009
                   "StatsWakeupThread");
×
1010
    }
×
1011

1012
    /* spawn the stats mgmt thread */
UNCOV
1013
    tv_mgmt = TmThreadCreateMgmtThread(thread_name_counter_stats,
×
UNCOV
1014
                                       StatsMgmtThread, 1);
×
UNCOV
1015
    if (tv_mgmt == NULL) {
×
1016
        FatalError("TmThreadCreateMgmtThread failed");
×
1017
    }
×
1018

UNCOV
1019
    if (TmThreadSpawn(tv_mgmt) != 0) {
×
1020
        FatalError("TmThreadSpawn failed for "
×
1021
                   "StatsWakeupThread");
×
1022
    }
×
1023

UNCOV
1024
    SCReturn;
×
UNCOV
1025
}
×
1026

1027
/**
1028
 * \brief Registers a normal, unqualified counter
1029
 *
1030
 * \param name Name of the counter, to be registered
1031
 * \param tv    Pointer to the ThreadVars instance for which the counter would
1032
 *              be registered
1033
 *
1034
 * \retval id Counter id for the newly registered counter, or the already
1035
 *            present counter
1036
 */
1037
StatsCounterId StatsRegisterCounter(const char *name, StatsThreadContext *stats)
1038
{
107,252✔
1039
    uint16_t id =
107,252✔
1040
            StatsRegisterQualifiedCounter(name, &stats->pub, STATS_TYPE_NORMAL, NULL, NULL, NULL);
107,252✔
1041
    StatsCounterId s = { .id = id };
107,252✔
1042
    return s;
107,252✔
1043
}
107,252✔
1044

1045
/**
1046
 * \brief Registers a counter, whose value holds the average of all the values
1047
 *        assigned to it.
1048
 *
1049
 * \param name Name of the counter, to be registered
1050
 * \param tv    Pointer to the ThreadVars instance for which the counter would
1051
 *              be registered
1052
 *
1053
 * \retval id Counter id for the newly registered counter, or the already
1054
 *            present counter
1055
 */
1056
StatsCounterAvgId StatsRegisterAvgCounter(const char *name, StatsThreadContext *stats)
1057
{
3✔
1058
    uint16_t id =
3✔
1059
            StatsRegisterQualifiedCounter(name, &stats->pub, STATS_TYPE_AVERAGE, NULL, NULL, NULL);
3✔
1060
    StatsCounterAvgId s = { .id = id };
3✔
1061
    return s;
3✔
1062
}
3✔
1063

1064
/**
1065
 * \brief Registers a counter, whose value holds the maximum of all the values
1066
 *        assigned to it.
1067
 *
1068
 * \param name Name of the counter, to be registered
1069
 * \param tv    Pointer to the ThreadVars instance for which the counter would
1070
 *              be registered
1071
 *
1072
 * \retval the counter id for the newly registered counter, or the already
1073
 *         present counter
1074
 */
1075
StatsCounterMaxId StatsRegisterMaxCounter(const char *name, StatsThreadContext *stats)
1076
{
10✔
1077
    uint16_t id =
10✔
1078
            StatsRegisterQualifiedCounter(name, &stats->pub, STATS_TYPE_MAXIMUM, NULL, NULL, NULL);
10✔
1079
    StatsCounterMaxId s = { .id = id };
10✔
1080
    return s;
10✔
1081
}
10✔
1082

1083
/**
1084
 * \brief Registers a counter, which represents a global value
1085
 *
1086
 * \param name Name of the counter, to be registered
1087
 * \param Func  Function Pointer returning a uint64_t
1088
 *
1089
 * \retval id Counter id for the newly registered counter, or the already
1090
 *            present counter
1091
 */
1092
StatsCounterGlobalId StatsRegisterGlobalCounter(const char *name, uint64_t (*Func)(void))
1093
{
36✔
1094
    StatsCounterGlobalId s = { .id = 0 };
36✔
1095
#if defined (UNITTESTS) || defined (FUZZ)
36✔
1096
    if (stats_ctx == NULL)
36✔
UNCOV
1097
        return s;
×
1098
#else
1099
    BUG_ON(stats_ctx == NULL);
1100
#endif
1101
    uint16_t id = StatsRegisterQualifiedCounter(
36✔
1102
            name, &(stats_ctx->global_counter_ctx), STATS_TYPE_FUNC, Func, NULL, NULL);
36✔
1103
    s.id = id;
36✔
1104
    return s;
36✔
1105
}
36✔
1106

1107
/**
1108
 * \brief Registers a counter which tracks the result of the calculating the value
1109
 * of counter dname1 divided by the value of the counter dname2
1110
 *
1111
 * \param name Name of the counter, to be registered
1112
 * \param dname1 First counter name
1113
 * \param dname2 Second counter name
1114
 *
1115
 * Both counters need to already be registered in this thread.
1116
 *
1117
 * \retval id Counter id for the newly registered counter, or the already
1118
 *            present counter
1119
 */
1120
StatsCounterDeriveId StatsRegisterDeriveDivCounter(
1121
        const char *name, const char *dname1, const char *dname2, StatsThreadContext *stats)
1122
{
3✔
1123
    StatsCounterDeriveId s = { .id = 0 };
3✔
1124
#if defined(UNITTESTS) || defined(FUZZ)
3✔
1125
    if (stats_ctx == NULL)
3✔
UNCOV
1126
        return s;
×
1127
#else
1128
    BUG_ON(stats_ctx == NULL);
1129
#endif
1130
    uint16_t id = StatsRegisterQualifiedCounter(
3✔
1131
            name, &stats->pub, STATS_TYPE_DERIVE_DIV, NULL, dname1, dname2);
3✔
1132
    s.id = id;
3✔
1133
    return s;
3✔
1134
}
3✔
1135

1136
typedef struct CountersIdType_ {
1137
    uint16_t id;
1138
    const char *string;
1139
} CountersIdType;
1140

1141
static uint32_t CountersIdHashFunc(HashTable *ht, void *data, uint16_t datalen)
UNCOV
1142
{
×
UNCOV
1143
    CountersIdType *t = (CountersIdType *)data;
×
UNCOV
1144
    uint32_t hash = 0;
×
UNCOV
1145
    size_t len = strlen(t->string);
×
1146

UNCOV
1147
    for (size_t i = 0; i < len; i++)
×
UNCOV
1148
        hash += u8_tolower((unsigned char)t->string[i]);
×
1149

UNCOV
1150
    hash = hash % ht->array_size;
×
UNCOV
1151
    return hash;
×
UNCOV
1152
}
×
1153

1154
static char CountersIdHashCompareFunc(void *data1, uint16_t datalen1,
1155
                               void *data2, uint16_t datalen2)
UNCOV
1156
{
×
UNCOV
1157
    CountersIdType *t1 = (CountersIdType *)data1;
×
UNCOV
1158
    CountersIdType *t2 = (CountersIdType *)data2;
×
1159

UNCOV
1160
    if (t1 == NULL || t2 == NULL)
×
1161
        return 0;
×
1162

UNCOV
1163
    if (t1->string == NULL || t2->string == NULL)
×
1164
        return 0;
×
1165

UNCOV
1166
    return strcmp(t1->string, t2->string) == 0;
×
UNCOV
1167
}
×
1168

1169
static void CountersIdHashFreeFunc(void *data)
UNCOV
1170
{
×
UNCOV
1171
    SCFree(data);
×
UNCOV
1172
}
×
1173

1174
static int StatsThreadSetupPublic(StatsPublicThreadContext *pctx)
UNCOV
1175
{
×
UNCOV
1176
    size_t array_size = pctx->curr_id + pctx->derive_id + 1;
×
UNCOV
1177
    pctx->pc_array = SCCalloc(array_size, sizeof(StatsCounter *));
×
UNCOV
1178
    if (pctx->pc_array == NULL) {
×
1179
        return -1;
×
1180
    }
×
1181
    /* regular counters that get direct updates by their id as idx */
UNCOV
1182
    for (StatsCounter *pc = pctx->head; pc != NULL; pc = pc->next) {
×
UNCOV
1183
        if (pc->type != STATS_TYPE_DERIVE_DIV) {
×
UNCOV
1184
            SCLogDebug("pc %s gid %u id %u", pc->name, pc->gid, pc->id);
×
UNCOV
1185
            BUG_ON(pctx->pc_array[pc->id] != NULL);
×
UNCOV
1186
            pctx->pc_array[pc->id] = pc;
×
UNCOV
1187
        }
×
UNCOV
1188
    }
×
1189
    /* derive counters are not updated by the thread itself and will be put
1190
     * at the end of the array */
UNCOV
1191
    for (StatsCounter *pc = pctx->head; pc != NULL; pc = pc->next) {
×
UNCOV
1192
        if (pc->type == STATS_TYPE_DERIVE_DIV) {
×
UNCOV
1193
            uint16_t id = pctx->curr_id + pc->id;
×
UNCOV
1194
            SCLogDebug("STATS_TYPE_DERIVE_DIV: pc %s gid %u pc->id %u id %u", pc->name, pc->gid,
×
UNCOV
1195
                    pc->id, id);
×
UNCOV
1196
            BUG_ON(pctx->pc_array[id] != NULL);
×
UNCOV
1197
            pctx->pc_array[id] = pc;
×
UNCOV
1198
        }
×
UNCOV
1199
    }
×
1200

UNCOV
1201
    SCLogDebug("array_size %u memory %" PRIu64, (uint32_t)array_size,
×
UNCOV
1202
            (uint64_t)(array_size * sizeof(StatsLocalCounter)));
×
UNCOV
1203
    pctx->copy_of_private = SCCalloc(array_size, sizeof(StatsLocalCounter));
×
UNCOV
1204
    if (pctx->copy_of_private == NULL) {
×
1205
        return -1;
×
1206
    }
×
UNCOV
1207
    return 0;
×
UNCOV
1208
}
×
1209

1210
/** \internal
1211
 *  \brief Adds a TM to the clubbed TM table.  Multiple instances of the same TM
1212
 *         are stacked together in a PCTMI container.
1213
 *
1214
 *  \param tm_name Name of the tm to be added to the table
1215
 *  \param pctx    StatsPublicThreadContext associated with the TM tm_name
1216
 *
1217
 *  \retval 1 on success, 0 on failure
1218
 */
1219
static int StatsThreadRegister(const char *thread_name, StatsPublicThreadContext *pctx)
1220
{
1✔
1221
    if (stats_ctx == NULL) {
1✔
UNCOV
1222
        return 1;
×
UNCOV
1223
    }
×
1224

1225
    if (thread_name == NULL || pctx == NULL) {
1✔
1226
        SCLogDebug("supplied argument(s) to StatsThreadRegister NULL");
1✔
1227
        return 0;
1✔
1228
    }
1✔
1229

UNCOV
1230
    SCMutexLock(&stats_ctx->sts_lock);
×
UNCOV
1231
    SCLogDebug("thread %s", thread_name);
×
UNCOV
1232
    if (stats_ctx->counters_id_hash == NULL) {
×
UNCOV
1233
        stats_ctx->counters_id_hash = HashTableInit(256, CountersIdHashFunc,
×
UNCOV
1234
                                                              CountersIdHashCompareFunc,
×
UNCOV
1235
                                                              CountersIdHashFreeFunc);
×
UNCOV
1236
        if (stats_ctx->counters_id_hash == NULL) {
×
1237
            SCMutexUnlock(&stats_ctx->sts_lock);
×
1238
            return 0;
×
1239
        }
×
UNCOV
1240
    }
×
UNCOV
1241
    StatsCounter *pc = pctx->head;
×
UNCOV
1242
    while (pc != NULL) {
×
UNCOV
1243
        CountersIdType t = { 0, pc->name }, *id = NULL;
×
UNCOV
1244
        id = HashTableLookup(stats_ctx->counters_id_hash, &t, sizeof(t));
×
UNCOV
1245
        if (id == NULL) {
×
UNCOV
1246
            id = SCCalloc(1, sizeof(*id));
×
UNCOV
1247
            BUG_ON(id == NULL);
×
UNCOV
1248
            id->id = counters_global_id++;
×
UNCOV
1249
            id->string = pc->name;
×
UNCOV
1250
            int r = HashTableAdd(stats_ctx->counters_id_hash, id, sizeof(*id));
×
UNCOV
1251
            DEBUG_VALIDATE_BUG_ON(r < 0);
×
UNCOV
1252
            if (r < 0) {
×
1253
                SCMutexUnlock(&stats_ctx->sts_lock);
×
1254
                return 0;
×
1255
            }
×
UNCOV
1256
        }
×
UNCOV
1257
        pc->gid = id->id;
×
UNCOV
1258
        pc = pc->next;
×
UNCOV
1259
    }
×
1260

UNCOV
1261
    if (StatsThreadSetupPublic(pctx) != 0) {
×
1262
        SCLogDebug("failed to setup StatsThreadSetupPublic");
×
1263
        SCMutexUnlock(&stats_ctx->sts_lock);
×
1264
        return 0;
×
1265
    }
×
1266

UNCOV
1267
    StatsThreadStore *temp = NULL;
×
UNCOV
1268
    if ((temp = SCCalloc(1, sizeof(StatsThreadStore))) == NULL) {
×
1269
        SCMutexUnlock(&stats_ctx->sts_lock);
×
1270
        return 0;
×
1271
    }
×
1272

UNCOV
1273
    temp->ctx = pctx;
×
UNCOV
1274
    temp->name = thread_name;
×
1275

UNCOV
1276
    temp->next = stats_ctx->sts;
×
UNCOV
1277
    stats_ctx->sts = temp;
×
UNCOV
1278
    stats_ctx->sts_cnt++;
×
UNCOV
1279
    SCLogDebug("stats_ctx->sts %p", stats_ctx->sts);
×
1280

UNCOV
1281
    SCMutexUnlock(&stats_ctx->sts_lock);
×
UNCOV
1282
    return 1;
×
UNCOV
1283
}
×
1284

1285
/** \internal
1286
 *  \brief Returns a counter array for all counters registered for this tm
1287
 *         instance
1288
 *
1289
 *  \param pctx Pointer to the tv's StatsPublicThreadContext
1290
 *
1291
 *  \retval pca Pointer to a counter-array for all counter of this tm instance
1292
 *              on success; NULL on failure
1293
 */
1294
static int StatsGetAllCountersArray(
1295
        StatsPublicThreadContext *pctx, StatsPrivateThreadContext *private)
1296
{
1✔
1297
    if (pctx == NULL || private == NULL)
1✔
UNCOV
1298
        return -1;
×
1299

1300
    private->size = pctx->curr_id + 1;
1✔
1301

1302
    private->head = SCCalloc(private->size, sizeof(StatsLocalCounter));
1✔
1303
    if (private->head == NULL) {
1✔
1304
        return -1;
×
1305
    }
×
1306

1307
    private->initialized = 1;
1✔
1308
    return 0;
1✔
1309
}
1✔
1310

1311
int StatsSetupPrivate(StatsThreadContext *stats, const char *thread_name)
1312
{
1✔
1313
    int r = StatsGetAllCountersArray(&stats->pub, &stats->priv);
1✔
1314
    if (r < 0) {
1✔
1315
        return -1;
×
1316
    }
×
1317

1318
    r = StatsThreadRegister(thread_name, &stats->pub);
1✔
1319
    if (r != 1) {
1✔
1320
        return -2;
1✔
1321
    }
1✔
UNCOV
1322
    return 0;
×
1323
}
1✔
1324

1325
static void StatsThreadInitPublic(StatsPublicThreadContext *pctx)
1326
{
1✔
1327
    memset(pctx, 0x00, sizeof(*pctx));
1✔
1328
    SCSpinInit(&pctx->lock, 0);
1✔
1329
}
1✔
1330

1331
void StatsThreadInit(StatsThreadContext *stats)
1332
{
1✔
1333
    StatsThreadInitPublic(&stats->pub);
1✔
1334
}
1✔
1335

1336
/**
1337
 * \brief the private stats store with the public stats store
1338
 *
1339
 * \param pca      Pointer to the StatsPrivateThreadContext
1340
 * \param pctx     Pointer the tv's StatsPublicThreadContext
1341
 *
1342
 * \retval  1 on success
1343
 * \retval -1 on error
1344
 */
1345
static int StatsUpdateCounterArray(StatsPrivateThreadContext *pca, StatsPublicThreadContext *pctx)
UNCOV
1346
{
×
1347

UNCOV
1348
    if (pca == NULL || pctx == NULL) {
×
1349
        SCLogDebug("pca or pctx is NULL inside StatsUpdateCounterArray");
×
1350
        return -1;
×
1351
    }
×
1352

UNCOV
1353
    if (pca->size > 0 && pctx->copy_of_private != NULL) {
×
1354
        /* copy the whole table under lock to the public section
1355
         * and release the lock. The stats thread will copy it from
1356
         * there. */
UNCOV
1357
        SCSpinLock(&pctx->lock);
×
UNCOV
1358
        memcpy(pctx->copy_of_private, pca->head, pca->size * sizeof(StatsLocalCounter));
×
UNCOV
1359
        SCSpinUnlock(&pctx->lock);
×
UNCOV
1360
    }
×
UNCOV
1361
    SC_ATOMIC_SET(pctx->sync_now, false);
×
UNCOV
1362
    return 1;
×
UNCOV
1363
}
×
1364

1365
/**
1366
 * \brief Get the value of the local copy of the counter that hold this id.
1367
 *
1368
 * \param tv threadvars
1369
 * \param id The counter id.
1370
 *
1371
 * \retval  0 on success.
1372
 * \retval -1 on error.
1373
 */
1374
int64_t StatsCounterGetLocalValue(StatsThreadContext *stats, StatsCounterId id)
UNCOV
1375
{
×
UNCOV
1376
    StatsPrivateThreadContext *pca = &stats->priv;
×
1377
#ifdef DEBUG
1378
    BUG_ON((id.id < 1) || (id.id > pca->size));
1379
#endif
UNCOV
1380
    return pca->head[id.id].v;
×
UNCOV
1381
}
×
1382

1383
/**
1384
 * \brief Releases the resources allotted by the Stats API
1385
 */
1386
void StatsReleaseResources(void)
UNCOV
1387
{
×
UNCOV
1388
    StatsLogSummary();
×
UNCOV
1389
    StatsReleaseCtx();
×
UNCOV
1390
}
×
1391

1392
/**
1393
 * \brief Releases counters
1394
 *
1395
 * \param head Pointer to the head of the list of perf counters that have to
1396
 *             be freed
1397
 */
1398
static void StatsReleaseCounters(StatsCounter *head)
UNCOV
1399
{
×
UNCOV
1400
    StatsCounter *pc = NULL;
×
1401

UNCOV
1402
    while (head != NULL) {
×
UNCOV
1403
        pc = head;
×
UNCOV
1404
        head = head->next;
×
UNCOV
1405
        StatsReleaseCounter(pc);
×
UNCOV
1406
    }
×
UNCOV
1407
}
×
1408

1409
/** \internal
1410
 *  \brief Releases the StatsPrivateThreadContext allocated by the user,
1411
 *         for storing and updating local counter values
1412
 *
1413
 * \param pca Pointer to the StatsPrivateThreadContext
1414
 */
1415
static void StatsReleasePrivateThreadContext(StatsPrivateThreadContext *pca)
UNCOV
1416
{
×
UNCOV
1417
    if (pca != NULL) {
×
UNCOV
1418
        if (pca->head != NULL) {
×
UNCOV
1419
            SCFree(pca->head);
×
UNCOV
1420
            pca->head = NULL;
×
UNCOV
1421
            pca->size = 0;
×
UNCOV
1422
        }
×
UNCOV
1423
        pca->initialized = 0;
×
UNCOV
1424
    }
×
UNCOV
1425
}
×
1426

1427
void StatsThreadCleanup(StatsThreadContext *stats)
UNCOV
1428
{
×
UNCOV
1429
    StatsPublicThreadContextCleanup(&stats->pub);
×
UNCOV
1430
    StatsReleasePrivateThreadContext(&stats->priv);
×
UNCOV
1431
}
×
1432

1433
/*----------------------------------Unit_Tests--------------------------------*/
1434

1435
#ifdef UNITTESTS
1436
/** \internal
1437
 * \brief Registers a normal, unqualified counter
1438
 *
1439
 * \param name   Name of the counter, to be registered
1440
 * \param type    Datatype of this counter variable
1441
 * \param pctx    StatsPublicThreadContext corresponding to the tm_name key under which the
1442
 *                key has to be registered
1443
 *
1444
 * \retval id Counter id for the newly registered counter, or the already
1445
 *            present counter
1446
 */
1447
static StatsCounterId RegisterCounter(
1448
        const char *name, const char *tm_name, StatsPublicThreadContext *pctx)
1449
{
1450
    uint16_t id = StatsRegisterQualifiedCounter(name, pctx, STATS_TYPE_NORMAL, NULL, NULL, NULL);
1451
    StatsCounterId s = { .id = id };
1452
    return s;
1453
}
1454

1455
static int StatsTestCounterReg02(void)
1456
{
1457
    StatsPublicThreadContext pctx;
1458
    StatsThreadInitPublic(&pctx);
1459

1460
    StatsCounterId id = RegisterCounter(NULL, NULL, &pctx);
1461
    FAIL_IF_NOT(id.id == 0);
1462
    PASS;
1463
}
1464

1465
static int StatsTestCounterReg03(void)
1466
{
1467
    StatsPublicThreadContext pctx;
1468
    StatsThreadInitPublic(&pctx);
1469

1470
    StatsCounterId id = RegisterCounter("t1", "c1", &pctx);
1471
    FAIL_IF_NOT(id.id == 1);
1472

1473
    StatsReleaseCounters(pctx.head);
1474
    PASS;
1475
}
1476

1477
static int StatsTestCounterReg04(void)
1478
{
1479
    StatsPublicThreadContext pctx;
1480
    StatsThreadInitPublic(&pctx);
1481

1482
    StatsCounterId c1 = RegisterCounter("t1", "c1", &pctx);
1483
    FAIL_IF_NOT(c1.id == 1);
1484
    StatsCounterId c2 = RegisterCounter("t2", "c2", &pctx);
1485
    FAIL_IF_NOT(c2.id == 2);
1486
    StatsCounterId c3 = RegisterCounter("t3", "c3", &pctx);
1487
    FAIL_IF_NOT(c3.id == 3);
1488
    StatsCounterId id = RegisterCounter("t1", "c1", &pctx);
1489
    FAIL_IF_NOT(id.id == 1);
1490

1491
    StatsReleaseCounters(pctx.head);
1492
    PASS;
1493
}
1494

1495
static int StatsTestGetCntArray05(void)
1496
{
1497
    ThreadVars tv;
1498
    memset(&tv, 0, sizeof(ThreadVars));
1499
    StatsThreadInit(&tv.stats);
1500
    StatsCounterId c1 = RegisterCounter("t1", "c1", &tv.stats.pub);
1501
    FAIL_IF(c1.id != 1);
1502
    int r = StatsGetAllCountersArray(NULL, &tv.stats.priv);
1503
    FAIL_IF_NOT(r == -1);
1504
    StatsThreadCleanup(&tv.stats);
1505
    PASS;
1506
}
1507

1508
static int StatsTestGetCntArray06(void)
1509
{
1510
    ThreadVars tv;
1511
    memset(&tv, 0, sizeof(ThreadVars));
1512
    StatsThreadInit(&tv.stats);
1513
    StatsCounterId c1 = RegisterCounter("t1", "c1", &tv.stats.pub);
1514
    FAIL_IF(c1.id != 1);
1515
    StatsThreadSetupPublic(&tv.stats.pub);
1516
    int r = StatsGetAllCountersArray(&tv.stats.pub, &tv.stats.priv);
1517
    FAIL_IF_NOT(r == 0);
1518
    StatsThreadCleanup(&tv.stats);
1519
    PASS;
1520
}
1521

1522
static int StatsTestCntArraySize07(void)
1523
{
1524
    ThreadVars tv;
1525
    memset(&tv, 0, sizeof(ThreadVars));
1526
    StatsThreadInit(&tv.stats);
1527
    StatsPrivateThreadContext *pca = NULL;
1528

1529
    StatsCounterId id1 = RegisterCounter("t1", "c1", &tv.stats.pub);
1530
    StatsCounterId id2 = RegisterCounter("t2", "c2", &tv.stats.pub);
1531

1532
    StatsThreadSetupPublic(&tv.stats.pub);
1533
    StatsGetAllCountersArray(&tv.stats.pub, &tv.stats.priv);
1534
    pca = &tv.stats.priv;
1535

1536
    StatsCounterIncr(&tv.stats, id1);
1537
    StatsCounterIncr(&tv.stats, id2);
1538

1539
    FAIL_IF_NOT(pca->size == 3);
1540

1541
    StatsThreadCleanup(&tv.stats);
1542
    PASS;
1543
}
1544

1545
static int StatsTestUpdateCounter08(void)
1546
{
1547
    ThreadVars tv;
1548
    memset(&tv, 0, sizeof(ThreadVars));
1549
    StatsThreadInit(&tv.stats);
1550
    StatsCounterId c1 = RegisterCounter("t1", "c1", &tv.stats.pub);
1551
    StatsThreadSetupPublic(&tv.stats.pub);
1552
    StatsGetAllCountersArray(&tv.stats.pub, &tv.stats.priv);
1553
    StatsPrivateThreadContext *pca = &tv.stats.priv;
1554

1555
    StatsCounterIncr(&tv.stats, c1);
1556
    StatsCounterAddI64(&tv.stats, c1, 100);
1557
    FAIL_IF_NOT(pca->head[c1.id].v == 101);
1558

1559
    StatsThreadCleanup(&tv.stats);
1560
    PASS;
1561
}
1562

1563
static int StatsTestUpdateCounter09(void)
1564
{
1565
    ThreadVars tv;
1566
    memset(&tv, 0, sizeof(ThreadVars));
1567
    StatsThreadInit(&tv.stats);
1568

1569
    StatsCounterId c1 = RegisterCounter("t1", "c1", &tv.stats.pub);
1570
    RegisterCounter("t2", "c2", &tv.stats.pub);
1571
    RegisterCounter("t3", "c3", &tv.stats.pub);
1572
    RegisterCounter("t4", "c4", &tv.stats.pub);
1573
    StatsCounterId c5 = RegisterCounter("t5", "c5", &tv.stats.pub);
1574

1575
    StatsThreadSetupPublic(&tv.stats.pub);
1576
    StatsGetAllCountersArray(&tv.stats.pub, &tv.stats.priv);
1577
    StatsPrivateThreadContext *pca = &tv.stats.priv;
1578

1579
    StatsCounterIncr(&tv.stats, c5);
1580
    StatsCounterAddI64(&tv.stats, c5, 100);
1581

1582
    FAIL_IF_NOT(pca->head[c1.id].v == 0);
1583
    FAIL_IF_NOT(pca->head[c5.id].v == 101);
1584

1585
    StatsThreadCleanup(&tv.stats);
1586
    PASS;
1587
}
1588

1589
static int StatsTestUpdateGlobalCounter10(void)
1590
{
1591
    ThreadVars tv;
1592
    memset(&tv, 0, sizeof(ThreadVars));
1593
    StatsThreadInit(&tv.stats);
1594

1595
    StatsCounterId c1 = RegisterCounter("t1", "c1", &tv.stats.pub);
1596
    StatsCounterId c2 = RegisterCounter("t2", "c2", &tv.stats.pub);
1597
    StatsCounterId c3 = RegisterCounter("t3", "c3", &tv.stats.pub);
1598

1599
    StatsThreadSetupPublic(&tv.stats.pub);
1600
    StatsGetAllCountersArray(&tv.stats.pub, &tv.stats.priv);
1601
    StatsPrivateThreadContext *pca = &tv.stats.priv;
1602

1603
    StatsCounterIncr(&tv.stats, c1);
1604
    StatsCounterAddI64(&tv.stats, c2, 100);
1605
    StatsCounterIncr(&tv.stats, c3);
1606
    StatsCounterAddI64(&tv.stats, c3, 100);
1607

1608
    StatsUpdateCounterArray(pca, &tv.stats.pub);
1609

1610
    FAIL_IF_NOT(1 == tv.stats.pub.copy_of_private[c1.id].v);
1611
    FAIL_IF_NOT(100 == tv.stats.pub.copy_of_private[c2.id].v);
1612
    FAIL_IF_NOT(101 == tv.stats.pub.copy_of_private[c3.id].v);
1613

1614
    StatsThreadCleanup(&tv.stats);
1615
    PASS;
1616
}
1617

1618
static int StatsTestCounterValues11(void)
1619
{
1620
    ThreadVars tv;
1621
    memset(&tv, 0, sizeof(ThreadVars));
1622
    StatsThreadInit(&tv.stats);
1623

1624
    StatsCounterId c1 = RegisterCounter("t1", "c1", &tv.stats.pub);
1625
    StatsCounterId c2 = RegisterCounter("t2", "c2", &tv.stats.pub);
1626
    StatsCounterId c3 = RegisterCounter("t3", "c3", &tv.stats.pub);
1627
    StatsCounterId c4 = RegisterCounter("t4", "c4", &tv.stats.pub);
1628

1629
    StatsThreadSetupPublic(&tv.stats.pub);
1630
    StatsGetAllCountersArray(&tv.stats.pub, &tv.stats.priv);
1631
    StatsPrivateThreadContext *pca = &tv.stats.priv;
1632

1633
    StatsCounterIncr(&tv.stats, c1);
1634
    StatsCounterAddI64(&tv.stats, c2, 256);
1635
    StatsCounterAddI64(&tv.stats, c3, 257);
1636
    StatsCounterAddI64(&tv.stats, c4, 16843024);
1637

1638
    StatsUpdateCounterArray(pca, &tv.stats.pub);
1639

1640
    FAIL_IF_NOT(1 == tv.stats.pub.copy_of_private[c1.id].v);
1641
    FAIL_IF_NOT(256 == tv.stats.pub.copy_of_private[c2.id].v);
1642
    FAIL_IF_NOT(257 == tv.stats.pub.copy_of_private[c3.id].v);
1643
    FAIL_IF_NOT(16843024 == tv.stats.pub.copy_of_private[c4.id].v);
1644

1645
    StatsThreadCleanup(&tv.stats);
1646
    PASS;
1647
}
1648

1649
#endif
1650

1651
void StatsRegisterTests(void)
UNCOV
1652
{
×
1653
#ifdef UNITTESTS
1654
    UtRegisterTest("StatsTestCounterReg02", StatsTestCounterReg02);
1655
    UtRegisterTest("StatsTestCounterReg03", StatsTestCounterReg03);
1656
    UtRegisterTest("StatsTestCounterReg04", StatsTestCounterReg04);
1657
    UtRegisterTest("StatsTestGetCntArray05", StatsTestGetCntArray05);
1658
    UtRegisterTest("StatsTestGetCntArray06", StatsTestGetCntArray06);
1659
    UtRegisterTest("StatsTestCntArraySize07", StatsTestCntArraySize07);
1660
    UtRegisterTest("StatsTestUpdateCounter08", StatsTestUpdateCounter08);
1661
    UtRegisterTest("StatsTestUpdateCounter09", StatsTestUpdateCounter09);
1662
    UtRegisterTest("StatsTestUpdateGlobalCounter10",
1663
                   StatsTestUpdateGlobalCounter10);
1664
    UtRegisterTest("StatsTestCounterValues11", StatsTestCounterValues11);
1665
#endif
UNCOV
1666
}
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc