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

OISF / suricata / 22712336922

05 Mar 2026 09:55AM UTC coverage: 66.796% (-12.5%) from 79.283%
22712336922

Pull #14946

github

web-flow
Merge 91559149c into 7e97dfd52
Pull Request #14946: Stack 8001 v15

14 of 19 new or added lines in 7 files covered. (73.68%)

11298 existing lines in 298 files now uncovered.

155282 of 232473 relevant lines covered (66.8%)

5923441.93 hits per line

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

86.58
/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 */
45
#define STATS_WUT_TTS 3
3,025✔
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)
116
{
3,009✔
117
    return stats_enabled;
3,009✔
118
}
3,009✔
119

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

125
static void StatsPublicThreadContextCleanup(StatsPublicThreadContext *t)
126
{
32,833✔
127
    SCSpinLock(&t->lock);
32,833✔
128
    SCFree(t->copy_of_private);
32,833✔
129
    SCFree(t->pc_array);
32,833✔
130
    StatsReleaseCounters(t->head);
32,833✔
131
    t->head = NULL;
32,833✔
132
    SC_ATOMIC_SET(t->sync_now, false);
32,833✔
133
    t->curr_id = 0;
32,833✔
134
    SCSpinUnlock(&t->lock);
32,833✔
135
    SCSpinDestroy(&t->lock);
32,833✔
136
}
32,833✔
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
{
6,082,896✔
147
    StatsPrivateThreadContext *pca = &stats->priv;
6,082,896✔
148
#if defined (UNITTESTS) || defined (FUZZ)
2,537,685✔
149
    if (pca->initialized == 0)
2,537,685✔
150
        return;
815,604✔
151
#endif
1,722,081✔
152
#ifdef DEBUG
153
    BUG_ON((id.id < 1) || (id.id > pca->size));
154
#endif
155
    pca->head[id.id].v += x;
5,267,292✔
156
}
5,267,292✔
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
{
28,022,981✔
166
    StatsPrivateThreadContext *pca = &stats->priv;
28,022,981✔
167
#if defined (UNITTESTS) || defined (FUZZ)
10,797,011✔
168
    if (pca->initialized == 0)
10,797,011✔
169
        return;
3,305,984✔
170
#endif
7,491,027✔
171
#ifdef DEBUG
172
    BUG_ON((id.id < 1) || (id.id > pca->size));
173
#endif
174
    pca->head[id.id].v++;
24,716,997✔
175
}
24,716,997✔
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
{
40,578✔
185
    StatsPrivateThreadContext *pca = &stats->priv;
40,578✔
186
#if defined(UNITTESTS) || defined(FUZZ)
16,341✔
187
    if (pca->initialized == 0)
16,341✔
188
        return;
189
#endif
16,341✔
190
#ifdef DEBUG
191
    BUG_ON((id.id < 1) || (id.id > pca->size));
192
#endif
193
    pca->head[id.id].v--;
40,578✔
194
}
40,578✔
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)
204
{
19,417✔
205
    StatsPrivateThreadContext *pca = &stats->priv;
19,417✔
206
#if defined (UNITTESTS) || defined (FUZZ)
207
    if (pca->initialized == 0)
208
        return;
209
#endif
210
#ifdef DEBUG
211
    BUG_ON((id.id < 1) || (id.id > pca->size));
212
#endif
213
    pca->head[id.id].v = x;
19,417✔
214
}
19,417✔
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
{
5,604,977✔
225
    StatsPrivateThreadContext *pca = &stats->priv;
5,604,977✔
226
#if defined(UNITTESTS) || defined(FUZZ)
2,249,612✔
227
    if (pca->initialized == 0)
2,249,612✔
228
        return;
815,604✔
229
#endif
1,434,008✔
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) {
4,789,373✔
235
        pca->head[id.id].v = x;
16,575✔
236
    }
16,575✔
237
}
4,789,373✔
238

239
void StatsCounterAvgAddI64(StatsThreadContext *stats, StatsCounterAvgId id, int64_t x)
240
{
525,895✔
241
    StatsPrivateThreadContext *pca = &stats->priv;
525,895✔
242
#if defined(UNITTESTS) || defined(FUZZ)
100✔
243
    if (pca->initialized == 0)
100✔
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;
525,895✔
251
    pca->head[id.id + 1].v++;
525,895✔
252
}
525,895✔
253

254
static SCConfNode *GetConfig(void)
255
{
3,553✔
256
    SCConfNode *stats = SCConfGetNode("stats");
3,553✔
257
    if (stats != NULL)
3,553✔
258
        return stats;
2,994✔
259

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

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

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

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

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

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

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

334
    if (stats_enabled && !OutputStatsLoggersRegistered()) {
3,549✔
335
        stats_loggers_active = 0;
535✔
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()) {
535✔
340
            SCLogWarning("stats are enabled but no loggers are active");
535✔
341
            stats_enabled = false;
535✔
342
            SCReturn;
535✔
343
        }
535✔
344
    }
535✔
345

346
    SCReturn;
3,549✔
347
}
3,549✔
348

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

360
    StatsThreadStore *sts = NULL;
3,393✔
361
    StatsThreadStore *temp = NULL;
3,393✔
362
    sts = stats_ctx->sts;
3,393✔
363

364
    while (sts != NULL) {
29,935✔
365
        temp = sts->next;
26,542✔
366
        SCFree(sts);
26,542✔
367
        sts = temp;
26,542✔
368
    }
26,542✔
369

370
    if (stats_ctx->counters_id_hash != NULL) {
3,393✔
371
        HashTableFree(stats_ctx->counters_id_hash);
3,393✔
372
        stats_ctx->counters_id_hash = NULL;
3,393✔
373
        counters_global_id = 0;
3,393✔
374
    }
3,393✔
375

376
    StatsPublicThreadContextCleanup(&stats_ctx->global_counter_ctx);
3,393✔
377
    SCFree(stats_ctx);
3,393✔
378
    stats_ctx = NULL;
3,393✔
379

380
    SCMutexLock(&stats_table_mutex);
3,393✔
381
    /* free stats table */
382
    if (stats_table.tstats != NULL) {
3,393✔
383
        SCFree(stats_table.tstats);
2,908✔
384
        stats_table.tstats = NULL;
2,908✔
385
    }
2,908✔
386

387
    if (stats_table.stats != NULL) {
3,393✔
388
        SCFree(stats_table.stats);
2,908✔
389
        stats_table.stats = NULL;
2,908✔
390
    }
2,908✔
391
    memset(&stats_table, 0, sizeof(stats_table));
3,393✔
392
    SCMutexUnlock(&stats_table_mutex);
3,393✔
393
}
3,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)
403
{
2,908✔
404
    ThreadVars *tv_local = (ThreadVars *)arg;
2,908✔
405

406
    SCSetThreadName(tv_local->name);
2,908✔
407

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

411
    /* Set the threads capability */
412
    tv_local->cap_flags = 0;
2,908✔
413
    SCDropCaps(tv_local);
2,908✔
414

415
    if (stats_ctx == NULL) {
2,908✔
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

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

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

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

455
        SCMutexLock(&stats_table_mutex);
2,950✔
456
        StatsOutput(tv_local);
2,950✔
457
        SCMutexUnlock(&stats_table_mutex);
2,950✔
458

459
        if (TmThreadsCheckFlag(tv_local, THV_KILL)) {
2,950✔
460
            break;
2,908✔
461
        }
2,908✔
462
    }
2,950✔
463

464
    TmThreadsSetFlag(tv_local, THV_RUNNING_DONE);
2,908✔
465
    TmThreadWaitForFlag(tv_local, THV_DEINIT);
2,908✔
466

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

473
    TmThreadsSetFlag(tv_local, THV_CLOSED);
2,908✔
474
    return NULL;
2,908✔
475
}
2,908✔
476

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

482
void StatsSyncCountersIfSignalled(StatsThreadContext *stats)
483
{
3,644,513✔
484
    if (SC_ATOMIC_GET(stats->pub.sync_now)) {
3,644,513✔
485
        StatsUpdateCounterArray(&stats->priv, &stats->pub);
952✔
486
    }
952✔
487
}
3,644,513✔
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)
498
{
2,908✔
499
    ThreadVars *tv_local = (ThreadVars *)arg;
2,908✔
500

501
    SCSetThreadName(tv_local->name);
2,908✔
502

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

506
    /* Set the threads capability */
507
    tv_local->cap_flags = 0;
2,908✔
508
    SCDropCaps(tv_local);
2,908✔
509

510
    if (stats_ctx == NULL) {
2,908✔
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

517
    TmThreadsSetFlag(tv_local, THV_INIT_DONE | THV_RUNNING);
2,908✔
518
    bool run = TmThreadsWaitForUnpause(tv_local);
2,908✔
519

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

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

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

548
            SC_ATOMIC_SET(tv->stats.pub.sync_now, true);
15,212✔
549

550
            if (tv->inq != NULL) {
15,212✔
551
                PacketQueue *q = tv->inq->pq;
11,739✔
552
                SCMutexLock(&q->mutex_q);
11,739✔
553
                SCCondSignal(&q->cond_q);
11,739✔
554
                SCMutexUnlock(&q->mutex_q);
11,739✔
555
            }
11,739✔
556

557
            tv = tv->next;
15,212✔
558
        }
15,212✔
559

560
        /* mgt threads for flow manager */
561
        tv = tv_root[TVT_MGMT];
3,025✔
562
        while (tv != NULL) {
15,125✔
563
            if (tv->stats.pub.head == NULL) {
12,100✔
564
                tv = tv->next;
6,050✔
565
                continue;
6,050✔
566
            }
6,050✔
567

568
            SC_ATOMIC_SET(tv->stats.pub.sync_now, true);
6,050✔
569

570
            tv = tv->next;
6,050✔
571
        }
6,050✔
572
        SCMutexUnlock(&tv_root_lock);
3,025✔
573

574
        if (TmThreadsCheckFlag(tv_local, THV_KILL)) {
3,025✔
575
            break;
2,908✔
576
        }
2,908✔
577
    }
3,025✔
578

579
    TmThreadsSetFlag(tv_local, THV_RUNNING_DONE);
2,908✔
580
    TmThreadWaitForFlag(tv_local, THV_DEINIT);
2,908✔
581
    TmThreadsSetFlag(tv_local, THV_CLOSED);
2,908✔
582
    return NULL;
2,908✔
583
}
2,908✔
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)
591
{
7,270,602✔
592
    if (pc != NULL) {
7,270,602✔
593
        SCFree(pc);
7,270,602✔
594
    }
7,270,602✔
595
}
7,270,602✔
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
{
33,678✔
603
    for (const StatsCounter *c = pctx->head; c != NULL; c = c->next) {
1,083,133✔
604
        if (strcmp(name, c->name) == 0) {
1,083,133✔
605
            return c->id;
33,678✔
606
        }
33,678✔
607
    }
1,083,133✔
608
    return 0;
×
609
}
33,678✔
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
{
7,752,163✔
625
    StatsCounter **head = &pctx->head;
7,752,163✔
626
    StatsCounter *temp = NULL;
7,752,163✔
627
    StatsCounter *prev = NULL;
7,752,163✔
628
    StatsCounter *pc = NULL;
7,752,163✔
629

630
    if (name == NULL || pctx == NULL) {
7,752,163✔
631
        SCLogDebug("Counter name, StatsPublicThreadContext NULL");
16,878✔
632
        return 0;
16,878✔
633
    }
16,878✔
634

635
    temp = prev = *head;
7,735,285✔
636
    while (temp != NULL) {
1,719,303,581✔
637
        prev = temp;
1,712,029,367✔
638

639
        if (strcmp(name, temp->name) == 0) {
1,712,029,367✔
640
            break;
461,071✔
641
        }
461,071✔
642

643
        temp = temp->next;
1,711,568,296✔
644
    }
1,711,568,296✔
645

646
    /* We already have a counter registered by this name */
647
    if (temp != NULL)
7,735,285✔
648
        return(temp->id);
461,071✔
649

650
    uint16_t did1 = 0;
7,274,214✔
651
    uint16_t did2 = 0;
7,274,214✔
652
    if (type_q == STATS_TYPE_DERIVE_DIV) {
7,274,214✔
653
        did1 = GetIdByName(pctx, dname1);
16,839✔
654
        did2 = GetIdByName(pctx, dname2);
16,839✔
655
        if (did1 == 0 || did2 == 0) {
16,839✔
656
            return 0;
×
657
        }
×
658
    }
16,839✔
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)
7,274,214✔
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) {
7,274,214✔
667
        pc->id = ++pctx->derive_id;
16,839✔
668
    } else {
7,257,375✔
669
        pc->id = ++(pctx->curr_id);
7,257,375✔
670
    }
7,257,375✔
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)
7,274,214✔
674
        ++(pctx->curr_id);
20,256✔
675
    pc->name = name;
7,274,214✔
676

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

682
    pc->type = type_q;
7,274,214✔
683
    pc->Func = Func;
7,274,214✔
684
    pc->did1 = did1;
7,274,214✔
685
    pc->did2 = did2;
7,274,214✔
686

687
    /* we now add the counter to the list */
688
    if (prev == NULL)
7,274,214✔
689
        *head = pc;
27,180✔
690
    else
7,247,034✔
691
        prev->next = pc;
7,247,034✔
692

693
    return pc->id;
7,274,214✔
694
}
7,274,214✔
695

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

704
    if (counters_global_id == 0)
2,950✔
705
        return -1;
×
706

707
    if (stats_table.nstats == 0) {
2,950✔
708
        StatsThreadRegister("Global", &stats_ctx->global_counter_ctx);
2,908✔
709

710
        uint32_t nstats = counters_global_id;
2,908✔
711

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

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

729
        stats_table.start_time = stats_start_time;
2,908✔
730
    }
2,908✔
731

732
    const uint16_t max_id = counters_global_id;
2,950✔
733
    if (max_id == 0)
2,950✔
734
        return -1;
×
735

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

746
    int thread = stats_ctx->sts_cnt - 1;
2,950✔
747
    StatsRecord *table = stats_table.stats;
2,950✔
748

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

756
        SCLogDebug("Thread %d %s ctx %p", thread, sts->name, sts->ctx);
23,644✔
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 */
761
        struct CountersMergeTable thread_table[max_id];
23,644✔
762
        memset(&thread_table, 0x00,
23,644✔
763
                max_id * sizeof(struct CountersMergeTable));
23,644✔
764

765
        StatsLocalCounter thread_table_from_private[max_id];
23,644✔
766
        memset(&thread_table_from_private, 0x00, max_id * sizeof(StatsLocalCounter));
23,644✔
767

768
        /* copy private table to a local variable to loop it w/o lock */
769
        bool skip = false;
23,644✔
770
        SCSpinLock(&sts->ctx->lock);
23,644✔
771
        const uint16_t table_size = sts->ctx->curr_id + sts->ctx->derive_id + 1;
23,644✔
772
        if (sts->ctx->copy_of_private == NULL) {
23,644✔
773
            skip = true;
×
774
        } else {
23,644✔
775
            memcpy(&thread_table_from_private, sts->ctx->copy_of_private,
23,644✔
776
                    table_size * sizeof(StatsLocalCounter));
23,644✔
777
        }
23,644✔
778
        SCSpinUnlock(&sts->ctx->lock);
23,644✔
779
        if (skip)
23,644✔
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. */
784
        for (uint16_t i = 1; i < table_size; i++) {
6,448,865✔
785
            const StatsCounter *pc = sts->ctx->pc_array[i];
6,425,221✔
786
            thread_table[pc->gid].type = pc->type;
6,425,221✔
787

788
            table[pc->gid].name = pc->name;
6,425,221✔
789
            table[pc->gid].short_name = pc->short_name;
6,425,221✔
790

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

812
                    thread_table[pc->gid].value = thread_table_from_private[i].v;
6,336,537✔
813
                    break;
6,336,537✔
814
            }
6,425,221✔
815
        }
6,425,221✔
816

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

825
            switch (e->type) {
6,425,221✔
826
                case STATS_TYPE_MAXIMUM:
64,875✔
827
                    if (e->value > merge_table[c].value)
64,875✔
828
                        merge_table[c].value = e->value;
8,195✔
829
                    break;
64,875✔
830
                case STATS_TYPE_FUNC:
56,050✔
831
                    merge_table[c].value = e->value;
56,050✔
832
                    break;
56,050✔
833
                case STATS_TYPE_AVERAGE:
17,896✔
834
                default:
6,304,296✔
835
                    merge_table[c].value += e->value;
6,304,296✔
836
                    break;
6,304,296✔
837
            }
6,425,221✔
838
            merge_table[c].updates += e->updates;
6,425,221✔
839
            merge_table[c].type = e->type;
6,425,221✔
840
        }
6,425,221✔
841

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

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

859
            switch (e->type) {
6,425,221✔
860
                case STATS_TYPE_AVERAGE:
17,896✔
861
                case STATS_TYPE_DERIVE_DIV:
32,634✔
862
                    if (e->value > 0 && e->updates > 0) {
32,634✔
863
                        r->value = (uint64_t)(e->value / e->updates);
8,870✔
864
                    }
8,870✔
865
                    break;
32,634✔
866
                default:
6,392,587✔
867
                    r->value = e->value;
6,392,587✔
868
                    break;
6,392,587✔
869
            }
6,425,221✔
870
        }
6,425,221✔
871

872
    next:
23,644✔
873
        sts = sts->next;
23,644✔
874
        thread--;
23,644✔
875
    }
23,644✔
876

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

884
        const struct CountersMergeTable *m = &merge_table[x];
1,516,279✔
885
        switch (m->type) {
1,516,279✔
886
            case STATS_TYPE_MAXIMUM:
20,650✔
887
                if (m->value > table[x].value)
20,650✔
888
                    table[x].value = m->value;
8,166✔
889
                break;
20,650✔
890
            case STATS_TYPE_AVERAGE:
5,932✔
891
            case STATS_TYPE_DERIVE_DIV:
8,882✔
892
                if (m->value > 0 && m->updates > 0) {
8,882✔
893
                    table[x].value = (uint64_t)(m->value / m->updates);
7,833✔
894
                }
7,833✔
895
                break;
8,882✔
896
            default:
1,486,747✔
897
                table[x].value += m->value;
1,486,747✔
898
                break;
1,486,747✔
899
        }
1,516,279✔
900
    }
1,516,279✔
901

902
    /* invoke logger(s) */
903
    if (stats_loggers_active) {
2,950✔
904
        OutputStatsLog(tv, td, &stats_table);
2,950✔
905
    }
2,950✔
906
    return 1;
2,950✔
907
}
2,950✔
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)
937
{
3,393✔
938
    if (!stats_enabled) {
3,393✔
939
        return;
485✔
940
    }
485✔
941
    uint64_t alerts = 0;
2,908✔
942
    SCMutexLock(&stats_table_mutex);
2,908✔
943
    if (stats_table.start_time != 0) {
2,908✔
944
        const StatsTable *st = &stats_table;
2,908✔
945
        for (uint32_t u = 0; u < st->nstats; u++) {
762,580✔
946
            const char *name = st->stats[u].name;
762,382✔
947
            if (name == NULL || strcmp(name, "detect.alert") != 0)
762,382✔
948
                continue;
759,672✔
949
            alerts = st->stats[u].value;
2,710✔
950
            break;
2,710✔
951
        }
762,382✔
952
    }
2,908✔
953
    SCMutexUnlock(&stats_table_mutex);
2,908✔
954
    SCLogInfo("Alerts: %"PRIu64, alerts);
2,908✔
955
}
2,908✔
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
{
3,569✔
963
    BUG_ON(stats_ctx != NULL);
3,569✔
964
    if ((stats_ctx = SCCalloc(1, sizeof(StatsGlobalContext))) == NULL) {
3,569✔
965
        FatalError("Fatal error encountered in StatsInitCtx. Exiting...");
×
966
    }
×
967

968
    StatsPublicThreadContextInit(&stats_ctx->global_counter_ctx);
3,569✔
969
}
3,569✔
970

971
void StatsSetupPostConfigPreOutput(void)
972
{
3,553✔
973
    StatsInitCtxPreOutput();
3,553✔
974
}
3,553✔
975

976
void StatsSetupPostConfigPostOutput(void)
977
{
3,549✔
978
    StatsInitCtxPostOutput();
3,549✔
979
}
3,549✔
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)
989
{
3,393✔
990
    SCEnter();
3,393✔
991

992
    if (!stats_enabled) {
3,393✔
993
        SCReturn;
485✔
994
    }
485✔
995

996
    ThreadVars *tv_wakeup = NULL;
2,908✔
997
    ThreadVars *tv_mgmt = NULL;
2,908✔
998

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

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

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

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

1024
    SCReturn;
2,908✔
1025
}
2,908✔
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
{
7,573,217✔
1039
    uint16_t id =
7,573,217✔
1040
            StatsRegisterQualifiedCounter(name, &stats->pub, STATS_TYPE_NORMAL, NULL, NULL, NULL);
7,573,217✔
1041
    StatsCounterId s = { .id = id };
7,573,217✔
1042
    return s;
7,573,217✔
1043
}
7,573,217✔
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
{
20,317✔
1058
    uint16_t id =
20,317✔
1059
            StatsRegisterQualifiedCounter(name, &stats->pub, STATS_TYPE_AVERAGE, NULL, NULL, NULL);
20,317✔
1060
    StatsCounterAvgId s = { .id = id };
20,317✔
1061
    return s;
20,317✔
1062
}
20,317✔
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
{
74,364✔
1077
    uint16_t id =
74,364✔
1078
            StatsRegisterQualifiedCounter(name, &stats->pub, STATS_TYPE_MAXIMUM, NULL, NULL, NULL);
74,364✔
1079
    StatsCounterMaxId s = { .id = id };
74,364✔
1080
    return s;
74,364✔
1081
}
74,364✔
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
{
67,365✔
1094
    StatsCounterGlobalId s = { .id = 0 };
67,365✔
1095
#if defined (UNITTESTS) || defined (FUZZ)
36✔
1096
    if (stats_ctx == NULL)
36✔
1097
        return s;
1098
#else
1099
    BUG_ON(stats_ctx == NULL);
67,329✔
1100
#endif
67,329✔
1101
    uint16_t id = StatsRegisterQualifiedCounter(
67,365✔
1102
            name, &(stats_ctx->global_counter_ctx), STATS_TYPE_FUNC, Func, NULL, NULL);
67,365✔
1103
    s.id = id;
67,365✔
1104
    return s;
67,365✔
1105
}
67,365✔
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
{
16,900✔
1123
    StatsCounterDeriveId s = { .id = 0 };
16,900✔
1124
#if defined(UNITTESTS) || defined(FUZZ)
3✔
1125
    if (stats_ctx == NULL)
3✔
1126
        return s;
1127
#else
1128
    BUG_ON(stats_ctx == NULL);
16,897✔
1129
#endif
16,897✔
1130
    uint16_t id = StatsRegisterQualifiedCounter(
16,900✔
1131
            name, &stats->pub, STATS_TYPE_DERIVE_DIV, NULL, dname1, dname2);
16,900✔
1132
    s.id = id;
16,900✔
1133
    return s;
16,900✔
1134
}
16,900✔
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)
1142
{
8,986,067✔
1143
    CountersIdType *t = (CountersIdType *)data;
8,986,067✔
1144
    uint32_t hash = 0;
8,986,067✔
1145
    size_t len = strlen(t->string);
8,986,067✔
1146

1147
    for (size_t i = 0; i < len; i++)
245,523,755✔
1148
        hash += u8_tolower((unsigned char)t->string[i]);
236,537,688✔
1149

1150
    hash = hash % ht->array_size;
8,986,067✔
1151
    return hash;
8,986,067✔
1152
}
8,986,067✔
1153

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

1160
    if (t1 == NULL || t2 == NULL)
12,279,340✔
1161
        return 0;
×
1162

1163
    if (t1->string == NULL || t2->string == NULL)
12,279,340✔
1164
        return 0;
×
1165

1166
    return strcmp(t1->string, t2->string) == 0;
12,279,340✔
1167
}
12,279,340✔
1168

1169
static void CountersIdHashFreeFunc(void *data)
1170
{
1,724,680✔
1171
    SCFree(data);
1,724,680✔
1172
}
1,724,680✔
1173

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

1201
    SCLogDebug("array_size %u memory %" PRIu64, (uint32_t)array_size,
26,550✔
1202
            (uint64_t)(array_size * sizeof(StatsLocalCounter)));
26,550✔
1203
    pctx->copy_of_private = SCCalloc(array_size, sizeof(StatsLocalCounter));
26,550✔
1204
    if (pctx->copy_of_private == NULL) {
26,550✔
1205
        return -1;
×
1206
    }
×
1207
    return 0;
26,550✔
1208
}
26,550✔
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
{
26,553✔
1221
    if (stats_ctx == NULL) {
26,553✔
1222
        return 1;
2✔
1223
    }
2✔
1224

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

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

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

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

1273
    temp->ctx = pctx;
26,550✔
1274
    temp->name = thread_name;
26,550✔
1275

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

1281
    SCMutexUnlock(&stats_ctx->sts_lock);
26,550✔
1282
    return 1;
26,550✔
1283
}
26,550✔
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
{
23,645✔
1297
    if (pctx == NULL || private == NULL)
23,645✔
UNCOV
1298
        return -1;
×
1299

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

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

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

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

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

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

1331
void StatsThreadInit(StatsThreadContext *stats)
1332
{
29,461✔
1333
    StatsThreadInitPublic(&stats->pub);
29,461✔
1334
}
29,461✔
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)
1346
{
58,206✔
1347

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

1353
    if (pca->size > 0 && pctx->copy_of_private != NULL) {
58,209✔
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. */
1357
        SCSpinLock(&pctx->lock);
58,198✔
1358
        memcpy(pctx->copy_of_private, pca->head, pca->size * sizeof(StatsLocalCounter));
58,198✔
1359
        SCSpinUnlock(&pctx->lock);
58,198✔
1360
    }
58,198✔
1361
    SC_ATOMIC_SET(pctx->sync_now, false);
58,206✔
1362
    return 1;
58,206✔
1363
}
58,206✔
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)
1375
{
48✔
1376
    StatsPrivateThreadContext *pca = &stats->priv;
48✔
1377
#ifdef DEBUG
1378
    BUG_ON((id.id < 1) || (id.id > pca->size));
1379
#endif
1380
    return pca->head[id.id].v;
48✔
1381
}
48✔
1382

1383
/**
1384
 * \brief Releases the resources allotted by the Stats API
1385
 */
1386
void StatsReleaseResources(void)
1387
{
3,393✔
1388
    StatsLogSummary();
3,393✔
1389
    StatsReleaseCtx();
3,393✔
1390
}
3,393✔
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)
1399
{
32,833✔
1400
    StatsCounter *pc = NULL;
32,833✔
1401

1402
    while (head != NULL) {
7,303,435✔
1403
        pc = head;
7,270,602✔
1404
        head = head->next;
7,270,602✔
1405
        StatsReleaseCounter(pc);
7,270,602✔
1406
    }
7,270,602✔
1407
}
32,833✔
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)
1416
{
29,440✔
1417
    if (pca != NULL) {
29,440✔
1418
        if (pca->head != NULL) {
29,440✔
1419
            SCFree(pca->head);
23,624✔
1420
            pca->head = NULL;
23,624✔
1421
            pca->size = 0;
23,624✔
1422
        }
23,624✔
1423
        pca->initialized = 0;
29,440✔
1424
    }
29,440✔
1425
}
29,440✔
1426

1427
void StatsThreadCleanup(StatsThreadContext *stats)
1428
{
29,440✔
1429
    StatsPublicThreadContextCleanup(&stats->pub);
29,440✔
1430
    StatsReleasePrivateThreadContext(&stats->priv);
29,440✔
1431
}
29,440✔
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