Coverage Report

Created: 2025-11-16 07:09

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/suricata7/src/counters.c
Line
Count
Source
1
/* Copyright (C) 2007-2024 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
0
#define STATS_WUT_TTS 3
46
47
/* Time interval at which the mgmt thread o/p the stats */
48
0
#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 {
55
    STATS_TYPE_NORMAL = 1,
56
    STATS_TYPE_AVERAGE = 2,
57
    STATS_TYPE_MAXIMUM = 3,
58
    STATS_TYPE_FUNC = 4,
59
60
    STATS_TYPE_MAX = 5,
61
};
62
63
/**
64
 * \brief per thread store of counters
65
 */
66
typedef struct StatsThreadStore_ {
67
    /** thread name used in output */
68
    const char *name;
69
70
    StatsPublicThreadContext *ctx;
71
72
    StatsPublicThreadContext **head;
73
    uint32_t size;
74
75
    struct StatsThreadStore_ *next;
76
} StatsThreadStore;
77
78
/**
79
 * \brief Holds the output interface context for the counter api
80
 */
81
typedef struct StatsGlobalContext_ {
82
    /** list of thread stores: one per thread plus one global */
83
    StatsThreadStore *sts;
84
    SCMutex sts_lock;
85
    int sts_cnt;
86
87
    HashTable *counters_id_hash;
88
89
    StatsPublicThreadContext global_counter_ctx;
90
} StatsGlobalContext;
91
92
static void *stats_thread_data = NULL;
93
static StatsGlobalContext *stats_ctx = NULL;
94
static time_t stats_start_time;
95
/** refresh interval in seconds */
96
static uint32_t stats_tts = STATS_MGMTT_TTS;
97
/** is the stats counter enabled? */
98
static bool stats_enabled = true;
99
100
/**< add decoder events as stats? enabled by default */
101
bool stats_decoder_events = true;
102
const char *stats_decoder_events_prefix = "decoder.event";
103
/**< add stream events as stats? disabled by default */
104
bool stats_stream_events = false;
105
106
static int StatsOutput(ThreadVars *tv);
107
static int StatsThreadRegister(const char *thread_name, StatsPublicThreadContext *);
108
void StatsReleaseCounters(StatsCounter *head);
109
110
/** stats table is filled each interval and passed to the
111
 *  loggers. Initialized at first use. */
112
static StatsTable stats_table = { NULL, NULL, 0, 0, 0, {0 , 0}};
113
static SCMutex stats_table_mutex = SCMUTEX_INITIALIZER;
114
static int stats_loggers_active = 1;
115
116
static uint16_t counters_global_id = 0;
117
118
bool StatsEnabled(void)
119
0
{
120
0
    return stats_enabled;
121
0
}
122
123
static void StatsPublicThreadContextInit(StatsPublicThreadContext *t)
124
71
{
125
71
    SCMutexInit(&t->m, NULL);
126
71
}
127
128
static void StatsPublicThreadContextCleanup(StatsPublicThreadContext *t)
129
0
{
130
0
    SCMutexLock(&t->m);
131
0
    StatsReleaseCounters(t->head);
132
0
    t->head = NULL;
133
0
    t->perf_flag = 0;
134
0
    t->curr_id = 0;
135
0
    SCMutexUnlock(&t->m);
136
0
    SCMutexDestroy(&t->m);
137
0
}
138
139
/**
140
 * \brief Adds a value of type uint64_t to the local counter.
141
 *
142
 * \param id  ID of the counter as set by the API
143
 * \param pca Counter array that holds the local counter for this TM
144
 * \param x   Value to add to this local counter
145
 */
146
void StatsAddUI64(ThreadVars *tv, uint16_t id, uint64_t x)
147
18.7M
{
148
18.7M
    StatsPrivateThreadContext *pca = &tv->perf_private_ctx;
149
18.7M
#if defined (UNITTESTS) || defined (FUZZ)
150
18.7M
    if (pca->initialized == 0)
151
1.04M
        return;
152
17.7M
#endif
153
#ifdef DEBUG
154
    BUG_ON ((id < 1) || (id > pca->size));
155
#endif
156
17.7M
    pca->head[id].value += x;
157
17.7M
    pca->head[id].updates++;
158
17.7M
    return;
159
18.7M
}
160
161
/**
162
 * \brief Increments the local counter
163
 *
164
 * \param id  Index of the counter in the counter array
165
 * \param pca Counter array that holds the local counters for this TM
166
 */
167
void StatsIncr(ThreadVars *tv, uint16_t id)
168
44.9M
{
169
44.9M
    StatsPrivateThreadContext *pca = &tv->perf_private_ctx;
170
44.9M
#if defined (UNITTESTS) || defined (FUZZ)
171
44.9M
    if (pca->initialized == 0)
172
2.02M
        return;
173
42.9M
#endif
174
#ifdef DEBUG
175
    BUG_ON ((id < 1) || (id > pca->size));
176
#endif
177
42.9M
    pca->head[id].value++;
178
42.9M
    pca->head[id].updates++;
179
42.9M
    return;
180
44.9M
}
181
182
/**
183
 * \brief Decrements the local counter
184
 *
185
 * \param id  Index of the counter in the counter array
186
 * \param pca Counter array that holds the local counters for this TM
187
 */
188
void StatsDecr(ThreadVars *tv, uint16_t id)
189
185k
{
190
185k
    StatsPrivateThreadContext *pca = &tv->perf_private_ctx;
191
185k
#if defined(UNITTESTS) || defined(FUZZ)
192
185k
    if (pca->initialized == 0)
193
0
        return;
194
185k
#endif
195
#ifdef DEBUG
196
    BUG_ON((id < 1) || (id > pca->size));
197
#endif
198
185k
    pca->head[id].value--;
199
185k
    pca->head[id].updates++;
200
185k
    return;
201
185k
}
202
203
/**
204
 * \brief Sets a value of type double to the local counter
205
 *
206
 * \param id  Index of the local counter in the counter array
207
 * \param pca Pointer to the StatsPrivateThreadContext
208
 * \param x   The value to set for the counter
209
 */
210
void StatsSetUI64(ThreadVars *tv, uint16_t id, uint64_t x)
211
8.87M
{
212
8.87M
    StatsPrivateThreadContext *pca = &tv->perf_private_ctx;
213
8.87M
#if defined (UNITTESTS) || defined (FUZZ)
214
8.87M
    if (pca->initialized == 0)
215
520k
        return;
216
8.35M
#endif
217
#ifdef DEBUG
218
    BUG_ON ((id < 1) || (id > pca->size));
219
#endif
220
221
8.35M
    if ((pca->head[id].pc->type == STATS_TYPE_MAXIMUM) && ((int64_t)x > pca->head[id].value)) {
222
425
        pca->head[id].value = x;
223
8.34M
    } else if (pca->head[id].pc->type == STATS_TYPE_NORMAL) {
224
0
        pca->head[id].value = x;
225
0
    }
226
227
8.35M
    pca->head[id].updates++;
228
229
8.35M
    return;
230
8.87M
}
231
232
4
static ConfNode *GetConfig(void) {
233
4
    ConfNode *stats = ConfGetNode("stats");
234
4
    if (stats != NULL)
235
0
        return stats;
236
237
4
    ConfNode *root = ConfGetNode("outputs");
238
4
    ConfNode *node = NULL;
239
4
    if (root != NULL) {
240
20
        TAILQ_FOREACH(node, &root->head, next) {
241
20
            if (strcmp(node->val, "stats") == 0) {
242
0
                return node->head.tqh_first;
243
0
            }
244
20
        }
245
4
    }
246
4
    return NULL;
247
4
}
248
249
/**
250
 * \brief Initializes stats context
251
 */
252
static void StatsInitCtxPreOutput(void)
253
4
{
254
4
    SCEnter();
255
4
    ConfNode *stats = GetConfig();
256
4
    if (stats != NULL) {
257
0
        const char *enabled = ConfNodeLookupChildValue(stats, "enabled");
258
0
        if (enabled != NULL && ConfValIsFalse(enabled)) {
259
0
            stats_enabled = false;
260
0
            SCLogDebug("Stats module has been disabled");
261
0
            SCReturn;
262
0
        }
263
        /* warn if we are using legacy config to enable stats */
264
0
        ConfNode *gstats = ConfGetNode("stats");
265
0
        if (gstats == NULL) {
266
0
            SCLogWarning("global stats config is missing. "
267
0
                         "Stats enabled through legacy stats.log. "
268
0
                         "See %s/configuration/suricata-yaml.html#stats",
269
0
                    GetDocURL());
270
0
        }
271
272
0
        const char *interval = ConfNodeLookupChildValue(stats, "interval");
273
0
        if (interval != NULL)
274
0
            if (StringParseUint32(&stats_tts, 10, 0, interval) < 0) {
275
0
                SCLogWarning("Invalid value for "
276
0
                             "interval: \"%s\". Resetting to %d.",
277
0
                        interval, STATS_MGMTT_TTS);
278
0
                stats_tts = STATS_MGMTT_TTS;
279
0
            }
280
281
0
        int b;
282
0
        int ret = ConfGetChildValueBool(stats, "decoder-events", &b);
283
0
        if (ret) {
284
0
            stats_decoder_events = (b == 1);
285
0
        }
286
0
        ret = ConfGetChildValueBool(stats, "stream-events", &b);
287
0
        if (ret) {
288
0
            stats_stream_events = (b == 1);
289
0
        }
290
291
0
        const char *prefix = NULL;
292
0
        if (ConfGet("stats.decoder-events-prefix", &prefix) != 1) {
293
0
            prefix = "decoder.event";
294
0
        }
295
0
        stats_decoder_events_prefix = prefix;
296
0
    }
297
4
    SCReturn;
298
4
}
299
300
static void StatsInitCtxPostOutput(void)
301
4
{
302
4
    SCEnter();
303
    /* Store the engine start time */
304
4
    time(&stats_start_time);
305
306
    /* init the lock used by StatsThreadStore */
307
4
    if (SCMutexInit(&stats_ctx->sts_lock, NULL) != 0) {
308
0
        FatalError("error initializing sts mutex");
309
0
    }
310
311
4
    if (stats_enabled && !OutputStatsLoggersRegistered()) {
312
4
        stats_loggers_active = 0;
313
314
        /* if the unix command socket is enabled we do the background
315
         * stats sync just in case someone runs 'dump-counters' */
316
4
        if (!ConfUnixSocketIsEnable()) {
317
4
            SCLogWarning("stats are enabled but no loggers are active");
318
4
            stats_enabled = false;
319
4
            SCReturn;
320
4
        }
321
4
    }
322
323
4
    SCReturn;
324
4
}
325
326
/**
327
 * \brief Releases the resources allotted to the output context of the
328
 *        Stats API
329
 */
330
static void StatsReleaseCtx(void)
331
0
{
332
0
    if (stats_ctx == NULL) {
333
0
        SCLogDebug("Counter module has been disabled");
334
0
        return;
335
0
    }
336
337
0
    StatsThreadStore *sts = NULL;
338
0
    StatsThreadStore *temp = NULL;
339
0
    sts = stats_ctx->sts;
340
341
0
    while (sts != NULL) {
342
0
        if (sts->head != NULL)
343
0
            SCFree(sts->head);
344
345
0
        temp = sts->next;
346
0
        SCFree(sts);
347
0
        sts = temp;
348
0
    }
349
350
0
    if (stats_ctx->counters_id_hash != NULL) {
351
0
        HashTableFree(stats_ctx->counters_id_hash);
352
0
        stats_ctx->counters_id_hash = NULL;
353
0
        counters_global_id = 0;
354
0
    }
355
356
0
    StatsPublicThreadContextCleanup(&stats_ctx->global_counter_ctx);
357
0
    SCFree(stats_ctx);
358
0
    stats_ctx = NULL;
359
360
0
    SCMutexLock(&stats_table_mutex);
361
    /* free stats table */
362
0
    if (stats_table.tstats != NULL) {
363
0
        SCFree(stats_table.tstats);
364
0
        stats_table.tstats = NULL;
365
0
    }
366
367
0
    if (stats_table.stats != NULL) {
368
0
        SCFree(stats_table.stats);
369
0
        stats_table.stats = NULL;
370
0
    }
371
0
    memset(&stats_table, 0, sizeof(stats_table));
372
0
    SCMutexUnlock(&stats_table_mutex);
373
374
0
    return;
375
0
}
376
377
/**
378
 * \brief management thread. This thread is responsible for writing the stats
379
 *
380
 * \param arg thread var
381
 *
382
 * \retval NULL This is the value that is always returned
383
 */
384
static void *StatsMgmtThread(void *arg)
385
0
{
386
0
    ThreadVars *tv_local = (ThreadVars *)arg;
387
388
0
    SCSetThreadName(tv_local->name);
389
390
0
    if (tv_local->thread_setup_flags != 0)
391
0
        TmThreadSetupOptions(tv_local);
392
393
    /* Set the threads capability */
394
0
    tv_local->cap_flags = 0;
395
0
    SCDropCaps(tv_local);
396
397
0
    if (stats_ctx == NULL) {
398
0
        SCLogError("Stats API not init"
399
0
                   "StatsInitCounterApi() has to be called first");
400
0
        TmThreadsSetFlag(tv_local, THV_CLOSED | THV_RUNNING_DONE);
401
0
        return NULL;
402
0
    }
403
404
0
    TmModule *tm = &tmm_modules[TMM_STATSLOGGER];
405
0
    BUG_ON(tm->ThreadInit == NULL);
406
0
    int r = tm->ThreadInit(tv_local, NULL, &stats_thread_data);
407
0
    if (r != 0 || stats_thread_data == NULL) {
408
0
        SCLogError("Stats API "
409
0
                   "ThreadInit failed");
410
0
        TmThreadsSetFlag(tv_local, THV_CLOSED | THV_RUNNING_DONE);
411
0
        return NULL;
412
0
    }
413
0
    SCLogDebug("stats_thread_data %p", &stats_thread_data);
414
415
0
    TmThreadsSetFlag(tv_local, THV_INIT_DONE | THV_RUNNING);
416
0
    while (1) {
417
0
        if (TmThreadsCheckFlag(tv_local, THV_PAUSE)) {
418
0
            TmThreadsSetFlag(tv_local, THV_PAUSED);
419
0
            TmThreadTestThreadUnPaused(tv_local);
420
0
            TmThreadsUnsetFlag(tv_local, THV_PAUSED);
421
0
        }
422
423
0
        struct timeval cur_timev;
424
0
        gettimeofday(&cur_timev, NULL);
425
0
        struct timespec cond_time = FROM_TIMEVAL(cur_timev);
426
0
        cond_time.tv_sec += (stats_tts);
427
428
        /* wait for the set time, or until we are woken up by
429
         * the shutdown procedure */
430
0
        SCCtrlMutexLock(tv_local->ctrl_mutex);
431
0
        SCCtrlCondTimedwait(tv_local->ctrl_cond, tv_local->ctrl_mutex, &cond_time);
432
0
        SCCtrlMutexUnlock(tv_local->ctrl_mutex);
433
434
0
        SCMutexLock(&stats_table_mutex);
435
0
        StatsOutput(tv_local);
436
0
        SCMutexUnlock(&stats_table_mutex);
437
438
0
        if (TmThreadsCheckFlag(tv_local, THV_KILL)) {
439
0
            break;
440
0
        }
441
0
    }
442
443
0
    TmThreadsSetFlag(tv_local, THV_RUNNING_DONE);
444
0
    TmThreadWaitForFlag(tv_local, THV_DEINIT);
445
446
0
    r = tm->ThreadDeinit(tv_local, stats_thread_data);
447
0
    if (r != TM_ECODE_OK) {
448
0
        SCLogError("Stats Counter API "
449
0
                   "ThreadDeinit failed");
450
0
    }
451
452
0
    TmThreadsSetFlag(tv_local, THV_CLOSED);
453
0
    return NULL;
454
0
}
455
456
/**
457
 * \brief Wake up thread.  This thread wakes up every TTS(time to sleep) seconds
458
 *        and sets the flag for every ThreadVars' StatsPublicThreadContext
459
 *
460
 * \param arg is NULL always
461
 *
462
 * \retval NULL This is the value that is always returned
463
 */
464
static void *StatsWakeupThread(void *arg)
465
0
{
466
0
    ThreadVars *tv_local = (ThreadVars *)arg;
467
468
0
    SCSetThreadName(tv_local->name);
469
470
0
    if (tv_local->thread_setup_flags != 0)
471
0
        TmThreadSetupOptions(tv_local);
472
473
    /* Set the threads capability */
474
0
    tv_local->cap_flags = 0;
475
0
    SCDropCaps(tv_local);
476
477
0
    if (stats_ctx == NULL) {
478
0
        SCLogError("Stats API not init"
479
0
                   "StatsInitCounterApi() has to be called first");
480
0
        TmThreadsSetFlag(tv_local, THV_CLOSED | THV_RUNNING_DONE);
481
0
        return NULL;
482
0
    }
483
484
0
    TmThreadsSetFlag(tv_local, THV_INIT_DONE | THV_RUNNING);
485
486
0
    while (1) {
487
0
        if (TmThreadsCheckFlag(tv_local, THV_PAUSE)) {
488
0
            TmThreadsSetFlag(tv_local, THV_PAUSED);
489
0
            TmThreadTestThreadUnPaused(tv_local);
490
0
            TmThreadsUnsetFlag(tv_local, THV_PAUSED);
491
0
        }
492
493
0
        struct timeval cur_timev;
494
0
        gettimeofday(&cur_timev, NULL);
495
0
        struct timespec cond_time = FROM_TIMEVAL(cur_timev);
496
0
        cond_time.tv_sec += STATS_WUT_TTS;
497
498
        /* wait for the set time, or until we are woken up by
499
         * the shutdown procedure */
500
0
        SCCtrlMutexLock(tv_local->ctrl_mutex);
501
0
        SCCtrlCondTimedwait(tv_local->ctrl_cond, tv_local->ctrl_mutex, &cond_time);
502
0
        SCCtrlMutexUnlock(tv_local->ctrl_mutex);
503
504
0
        SCMutexLock(&tv_root_lock);
505
0
        ThreadVars *tv = tv_root[TVT_PPT];
506
0
        while (tv != NULL) {
507
0
            if (tv->perf_public_ctx.head == NULL) {
508
0
                tv = tv->next;
509
0
                continue;
510
0
            }
511
512
            /* assuming the assignment of an int to be atomic, and even if it's
513
             * not, it should be okay */
514
0
            tv->perf_public_ctx.perf_flag = 1;
515
516
0
            if (tv->inq != NULL) {
517
0
                PacketQueue *q = tv->inq->pq;
518
0
                SCCondSignal(&q->cond_q);
519
0
            }
520
521
0
            tv = tv->next;
522
0
        }
523
524
        /* mgt threads for flow manager */
525
0
        tv = tv_root[TVT_MGMT];
526
0
        while (tv != NULL) {
527
0
            if (tv->perf_public_ctx.head == NULL) {
528
0
                tv = tv->next;
529
0
                continue;
530
0
            }
531
532
            /* assuming the assignment of an int to be atomic, and even if it's
533
             * not, it should be okay */
534
0
            tv->perf_public_ctx.perf_flag = 1;
535
536
0
            tv = tv->next;
537
0
        }
538
0
        SCMutexUnlock(&tv_root_lock);
539
540
0
        if (TmThreadsCheckFlag(tv_local, THV_KILL)) {
541
0
            break;
542
0
        }
543
0
    }
544
545
0
    TmThreadsSetFlag(tv_local, THV_RUNNING_DONE);
546
0
    TmThreadWaitForFlag(tv_local, THV_DEINIT);
547
0
    TmThreadsSetFlag(tv_local, THV_CLOSED);
548
0
    return NULL;
549
0
}
550
551
/**
552
 * \brief Releases a counter
553
 *
554
 * \param pc Pointer to the StatsCounter to be freed
555
 */
556
static void StatsReleaseCounter(StatsCounter *pc)
557
0
{
558
0
    if (pc != NULL) {
559
0
        SCFree(pc);
560
0
    }
561
562
0
    return;
563
0
}
564
565
/**
566
 * \brief Registers a counter.
567
 *
568
 * \param name    Name of the counter, to be registered
569
 * \param tm_name  Thread module to which this counter belongs
570
 * \param pctx     StatsPublicThreadContext for this tm-tv instance
571
 * \param type_q   Qualifier describing the type of counter to be registered
572
 *
573
 * \retval the counter id for the newly registered counter, or the already
574
 *         present counter on success
575
 * \retval 0 on failure
576
 */
577
static uint16_t StatsRegisterQualifiedCounter(const char *name, const char *tm_name,
578
                                              StatsPublicThreadContext *pctx,
579
                                              int type_q, uint64_t (*Func)(void))
580
398k
{
581
398k
    StatsCounter **head = &pctx->head;
582
398k
    StatsCounter *temp = NULL;
583
398k
    StatsCounter *prev = NULL;
584
398k
    StatsCounter *pc = NULL;
585
586
398k
    if (name == NULL || pctx == NULL) {
587
4
        SCLogDebug("Counter name, StatsPublicThreadContext NULL");
588
4
        return 0;
589
4
    }
590
591
398k
    temp = prev = *head;
592
91.5M
    while (temp != NULL) {
593
91.5M
        prev = temp;
594
595
91.5M
        if (strcmp(name, temp->name) == 0) {
596
395k
            break;
597
395k
        }
598
599
91.1M
        temp = temp->next;
600
91.1M
    }
601
602
    /* We already have a counter registered by this name */
603
398k
    if (temp != NULL)
604
395k
        return(temp->id);
605
606
    /* if we reach this point we don't have a counter registered by this name */
607
3.11k
    if ((pc = SCCalloc(1, sizeof(StatsCounter))) == NULL)
608
0
        return 0;
609
610
    /* assign a unique id to this StatsCounter.  The id is local to this
611
     * thread context.  Please note that the id start from 1, and not 0 */
612
3.11k
    pc->id = ++(pctx->curr_id);
613
3.11k
    pc->name = name;
614
615
    /* Precalculate the short name */
616
3.11k
    if (strrchr(name, '.') != NULL) {
617
3.11k
        pc->short_name = &name[strrchr(name, '.') - name + 1];
618
3.11k
    }
619
620
3.11k
    pc->type = type_q;
621
3.11k
    pc->Func = Func;
622
623
    /* we now add the counter to the list */
624
3.11k
    if (prev == NULL)
625
77
        *head = pc;
626
3.03k
    else
627
3.03k
        prev->next = pc;
628
629
3.11k
    return pc->id;
630
3.11k
}
631
632
/**
633
 * \brief Copies the StatsCounter value from the local counter present in the
634
 *        StatsPrivateThreadContext to its corresponding global counterpart.  Used
635
 *        internally by StatsUpdateCounterArray()
636
 *
637
 * \param pcae     Pointer to the StatsPrivateThreadContext which holds the local
638
 *                 versions of the counters
639
 */
640
static void StatsCopyCounterValue(StatsLocalCounter *pcae)
641
0
{
642
0
    StatsCounter *pc = pcae->pc;
643
644
0
    pc->value = pcae->value;
645
0
    pc->updates = pcae->updates;
646
0
    return;
647
0
}
648
649
/**
650
 * \brief The output interface for the Stats API
651
 */
652
static int StatsOutput(ThreadVars *tv)
653
0
{
654
0
    const StatsThreadStore *sts = NULL;
655
0
    const StatsCounter *pc = NULL;
656
0
    void *td = stats_thread_data;
657
658
0
    if (counters_global_id == 0)
659
0
        return -1;
660
661
0
    if (stats_table.nstats == 0) {
662
0
        StatsThreadRegister("Global", &stats_ctx->global_counter_ctx);
663
664
0
        uint32_t nstats = counters_global_id;
665
666
0
        stats_table.nstats = nstats;
667
0
        stats_table.stats = SCCalloc(stats_table.nstats, sizeof(StatsRecord));
668
0
        if (stats_table.stats == NULL) {
669
0
            stats_table.nstats = 0;
670
0
            SCLogError("could not alloc memory for stats");
671
0
            return -1;
672
0
        }
673
674
0
        stats_table.ntstats = stats_ctx->sts_cnt;
675
0
        uint32_t array_size = stats_table.nstats * sizeof(StatsRecord);
676
0
        stats_table.tstats = SCCalloc(stats_table.ntstats, array_size);
677
0
        if (stats_table.tstats == NULL) {
678
0
            stats_table.ntstats = 0;
679
0
            SCLogError("could not alloc memory for stats");
680
0
            return -1;
681
0
        }
682
683
0
        stats_table.start_time = stats_start_time;
684
0
    }
685
686
0
    const uint16_t max_id = counters_global_id;
687
0
    if (max_id == 0)
688
0
        return -1;
689
690
    /** temporary local table to merge the per thread counters,
691
     *  especially needed for the average counters */
692
0
    struct CountersMergeTable {
693
0
        int type;
694
0
        int64_t value;
695
0
        uint64_t updates;
696
0
    } merge_table[max_id];
697
0
    memset(&merge_table, 0x00,
698
0
           max_id * sizeof(struct CountersMergeTable));
699
700
0
    int thread = stats_ctx->sts_cnt - 1;
701
0
    StatsRecord *table = stats_table.stats;
702
703
    /* Loop through the thread counter stores. The global counters
704
     * are in a separate store inside this list. */
705
0
    sts = stats_ctx->sts;
706
0
    SCLogDebug("sts %p", sts);
707
0
    while (sts != NULL) {
708
0
        BUG_ON(thread < 0);
709
710
0
        SCLogDebug("Thread %d %s ctx %p", thread, sts->name, sts->ctx);
711
712
        /* temporary table for quickly storing the counters for this
713
         * thread store, so that we can post process them outside
714
         * of the thread store lock */
715
0
        struct CountersMergeTable thread_table[max_id];
716
0
        memset(&thread_table, 0x00,
717
0
                max_id * sizeof(struct CountersMergeTable));
718
719
0
        SCMutexLock(&sts->ctx->m);
720
0
        pc = sts->ctx->head;
721
0
        while (pc != NULL) {
722
0
            SCLogDebug("Counter %s (%u:%u) value %"PRIu64,
723
0
                    pc->name, pc->id, pc->gid, pc->value);
724
725
0
            thread_table[pc->gid].type = pc->type;
726
0
            switch (pc->type) {
727
0
                case STATS_TYPE_FUNC:
728
0
                    if (pc->Func != NULL)
729
0
                        thread_table[pc->gid].value = pc->Func();
730
0
                    break;
731
0
                case STATS_TYPE_AVERAGE:
732
0
                default:
733
0
                    thread_table[pc->gid].value = pc->value;
734
0
                    break;
735
0
            }
736
0
            thread_table[pc->gid].updates = pc->updates;
737
0
            table[pc->gid].name = pc->name;
738
0
            table[pc->gid].short_name = pc->short_name;
739
740
0
            pc = pc->next;
741
0
        }
742
0
        SCMutexUnlock(&sts->ctx->m);
743
744
        /* update merge table */
745
0
        for (uint16_t c = 0; c < max_id; c++) {
746
0
            struct CountersMergeTable *e = &thread_table[c];
747
            /* thread only sets type if it has a counter
748
             * of this type. */
749
0
            if (e->type == 0)
750
0
                continue;
751
752
0
            switch (e->type) {
753
0
                case STATS_TYPE_MAXIMUM:
754
0
                    if (e->value > merge_table[c].value)
755
0
                        merge_table[c].value = e->value;
756
0
                    break;
757
0
                case STATS_TYPE_FUNC:
758
0
                    merge_table[c].value = e->value;
759
0
                    break;
760
0
                case STATS_TYPE_AVERAGE:
761
0
                default:
762
0
                    merge_table[c].value += e->value;
763
0
                    break;
764
0
            }
765
0
            merge_table[c].updates += e->updates;
766
0
            merge_table[c].type = e->type;
767
0
        }
768
769
        /* update per thread stats table */
770
0
        for (uint16_t c = 0; c < max_id; c++) {
771
0
            struct CountersMergeTable *e = &thread_table[c];
772
            /* thread only sets type if it has a counter
773
             * of this type. */
774
0
            if (e->type == 0)
775
0
                continue;
776
777
0
            uint32_t offset = (thread * stats_table.nstats) + c;
778
0
            StatsRecord *r = &stats_table.tstats[offset];
779
            /* xfer previous value to pvalue and reset value */
780
0
            r->pvalue = r->value;
781
0
            r->value = 0;
782
0
            r->name = table[c].name;
783
0
            r->short_name = table[c].short_name;
784
0
            r->tm_name = sts->name;
785
786
0
            switch (e->type) {
787
0
                case STATS_TYPE_AVERAGE:
788
0
                    if (e->value > 0 && e->updates > 0) {
789
0
                        r->value = (uint64_t)(e->value / e->updates);
790
0
                    }
791
0
                    break;
792
0
                default:
793
0
                    r->value = e->value;
794
0
                    break;
795
0
            }
796
0
        }
797
798
0
        sts = sts->next;
799
0
        thread--;
800
0
    }
801
802
    /* transfer 'merge table' to final stats table */
803
0
    for (uint16_t x = 0; x < max_id; x++) {
804
        /* xfer previous value to pvalue and reset value */
805
0
        table[x].pvalue = table[x].value;
806
0
        table[x].value = 0;
807
0
        table[x].tm_name = "Total";
808
809
0
        struct CountersMergeTable *m = &merge_table[x];
810
0
        switch (m->type) {
811
0
            case STATS_TYPE_MAXIMUM:
812
0
                if (m->value > table[x].value)
813
0
                    table[x].value = m->value;
814
0
                break;
815
0
            case STATS_TYPE_AVERAGE:
816
0
                if (m->value > 0 && m->updates > 0) {
817
0
                    table[x].value = (uint64_t)(m->value / m->updates);
818
0
                }
819
0
                break;
820
0
            default:
821
0
                table[x].value += m->value;
822
0
                break;
823
0
        }
824
0
    }
825
826
    /* invoke logger(s) */
827
0
    if (stats_loggers_active) {
828
0
        OutputStatsLog(tv, td, &stats_table);
829
0
    }
830
0
    return 1;
831
0
}
832
833
#ifdef BUILD_UNIX_SOCKET
834
/** \brief callback for getting stats into unix socket
835
 */
836
TmEcode StatsOutputCounterSocket(json_t *cmd,
837
                               json_t *answer, void *data)
838
0
{
839
0
    json_t *message = NULL;
840
0
    TmEcode r = TM_ECODE_OK;
841
842
0
    if (!stats_enabled) {
843
0
        r = TM_ECODE_FAILED;
844
0
        message = json_string("stats are disabled in the config");
845
0
    } else {
846
0
        SCMutexLock(&stats_table_mutex);
847
0
        if (stats_table.start_time == 0) {
848
0
            r = TM_ECODE_FAILED;
849
0
            message = json_string("stats not yet synchronized");
850
0
        } else {
851
0
            message = StatsToJSON(&stats_table, JSON_STATS_TOTALS|JSON_STATS_THREADS);
852
0
        }
853
0
        SCMutexUnlock(&stats_table_mutex);
854
0
    }
855
0
    json_object_set_new(answer, "message", message);
856
0
    return r;
857
0
}
858
#endif /* BUILD_UNIX_SOCKET */
859
860
static void StatsLogSummary(void)
861
0
{
862
0
    if (!stats_enabled) {
863
0
        return;
864
0
    }
865
0
    uint64_t alerts = 0;
866
0
    SCMutexLock(&stats_table_mutex);
867
0
    if (stats_table.start_time != 0) {
868
0
        const StatsTable *st = &stats_table;
869
0
        for (uint32_t u = 0; u < st->nstats; u++) {
870
0
            const char *name = st->stats[u].name;
871
0
            if (name == NULL || strcmp(name, "detect.alert") != 0)
872
0
                continue;
873
0
            alerts = st->stats[u].value;
874
0
            break;
875
0
        }
876
0
    }
877
0
    SCMutexUnlock(&stats_table_mutex);
878
0
    SCLogInfo("Alerts: %"PRIu64, alerts);
879
0
}
880
881
/**
882
 * \brief Initializes the perf counter api.  Things are hard coded currently.
883
 *        More work to be done when we implement multiple interfaces
884
 */
885
void StatsInit(void)
886
71
{
887
71
    BUG_ON(stats_ctx != NULL);
888
71
    if ((stats_ctx = SCCalloc(1, sizeof(StatsGlobalContext))) == NULL) {
889
0
        FatalError("Fatal error encountered in StatsInitCtx. Exiting...");
890
0
    }
891
892
71
    StatsPublicThreadContextInit(&stats_ctx->global_counter_ctx);
893
71
}
894
895
void StatsSetupPostConfigPreOutput(void)
896
4
{
897
4
    StatsInitCtxPreOutput();
898
4
}
899
900
void StatsSetupPostConfigPostOutput(void)
901
4
{
902
4
    StatsInitCtxPostOutput();
903
4
}
904
905
906
/**
907
 * \brief Spawns the wakeup, and the management thread used by the stats api
908
 *
909
 *  The threads use the condition variable in the thread vars to control
910
 *  their wait loops to make sure the main thread can quickly kill them.
911
 */
912
void StatsSpawnThreads(void)
913
0
{
914
0
    SCEnter();
915
916
0
    if (!stats_enabled) {
917
0
        SCReturn;
918
0
    }
919
920
0
    ThreadVars *tv_wakeup = NULL;
921
0
    ThreadVars *tv_mgmt = NULL;
922
923
    /* spawn the stats wakeup thread */
924
0
    tv_wakeup = TmThreadCreateMgmtThread(thread_name_counter_wakeup,
925
0
                                         StatsWakeupThread, 1);
926
0
    if (tv_wakeup == NULL) {
927
0
        FatalError("TmThreadCreateMgmtThread "
928
0
                   "failed");
929
0
    }
930
931
0
    if (TmThreadSpawn(tv_wakeup) != 0) {
932
0
        FatalError("TmThreadSpawn failed for "
933
0
                   "StatsWakeupThread");
934
0
    }
935
936
    /* spawn the stats mgmt thread */
937
0
    tv_mgmt = TmThreadCreateMgmtThread(thread_name_counter_stats,
938
0
                                       StatsMgmtThread, 1);
939
0
    if (tv_mgmt == NULL) {
940
0
        FatalError("TmThreadCreateMgmtThread failed");
941
0
    }
942
943
0
    if (TmThreadSpawn(tv_mgmt) != 0) {
944
0
        FatalError("TmThreadSpawn failed for "
945
0
                   "StatsWakeupThread");
946
0
    }
947
948
0
    SCReturn;
949
0
}
950
951
/**
952
 * \brief Registers a normal, unqualified counter
953
 *
954
 * \param name Name of the counter, to be registered
955
 * \param tv    Pointer to the ThreadVars instance for which the counter would
956
 *              be registered
957
 *
958
 * \retval id Counter id for the newly registered counter, or the already
959
 *            present counter
960
 */
961
uint16_t StatsRegisterCounter(const char *name, struct ThreadVars_ *tv)
962
397k
{
963
397k
    uint16_t id = StatsRegisterQualifiedCounter(name,
964
397k
            (tv->thread_group_name != NULL) ? tv->thread_group_name : tv->printable_name,
965
397k
            &tv->perf_public_ctx,
966
397k
            STATS_TYPE_NORMAL, NULL);
967
397k
    return id;
968
397k
}
969
970
/**
971
 * \brief Registers a counter, whose value holds the average of all the values
972
 *        assigned to it.
973
 *
974
 * \param name Name of the counter, to be registered
975
 * \param tv    Pointer to the ThreadVars instance for which the counter would
976
 *              be registered
977
 *
978
 * \retval id Counter id for the newly registered counter, or the already
979
 *            present counter
980
 */
981
uint16_t StatsRegisterAvgCounter(const char *name, struct ThreadVars_ *tv)
982
20
{
983
20
    uint16_t id = StatsRegisterQualifiedCounter(name,
984
20
            (tv->thread_group_name != NULL) ? tv->thread_group_name : tv->printable_name,
985
20
            &tv->perf_public_ctx,
986
20
            STATS_TYPE_AVERAGE, NULL);
987
20
    return id;
988
20
}
989
990
/**
991
 * \brief Registers a counter, whose value holds the maximum of all the values
992
 *        assigned to it.
993
 *
994
 * \param name Name of the counter, to be registered
995
 * \param tv    Pointer to the ThreadVars instance for which the counter would
996
 *              be registered
997
 *
998
 * \retval the counter id for the newly registered counter, or the already
999
 *         present counter
1000
 */
1001
uint16_t StatsRegisterMaxCounter(const char *name, struct ThreadVars_ *tv)
1002
54
{
1003
54
    uint16_t id = StatsRegisterQualifiedCounter(name,
1004
54
            (tv->thread_group_name != NULL) ? tv->thread_group_name : tv->printable_name,
1005
54
            &tv->perf_public_ctx,
1006
54
            STATS_TYPE_MAXIMUM, NULL);
1007
54
    return id;
1008
54
}
1009
1010
/**
1011
 * \brief Registers a counter, which represents a global value
1012
 *
1013
 * \param name Name of the counter, to be registered
1014
 * \param Func  Function Pointer returning a uint64_t
1015
 *
1016
 * \retval id Counter id for the newly registered counter, or the already
1017
 *            present counter
1018
 */
1019
uint16_t StatsRegisterGlobalCounter(const char *name, uint64_t (*Func)(void))
1020
796
{
1021
796
#if defined (UNITTESTS) || defined (FUZZ)
1022
796
    if (stats_ctx == NULL)
1023
0
        return 0;
1024
#else
1025
    BUG_ON(stats_ctx == NULL);
1026
#endif
1027
796
    uint16_t id = StatsRegisterQualifiedCounter(name, NULL,
1028
796
            &(stats_ctx->global_counter_ctx),
1029
796
            STATS_TYPE_FUNC,
1030
796
            Func);
1031
796
    return id;
1032
796
}
1033
1034
typedef struct CountersIdType_ {
1035
    uint16_t id;
1036
    const char *string;
1037
} CountersIdType;
1038
1039
static uint32_t CountersIdHashFunc(HashTable *ht, void *data, uint16_t datalen)
1040
3.83k
{
1041
3.83k
    CountersIdType *t = (CountersIdType *)data;
1042
3.83k
    uint32_t hash = 0;
1043
3.83k
    int len = strlen(t->string);
1044
1045
104k
    for (int i = 0; i < len; i++)
1046
100k
        hash += u8_tolower((unsigned char)t->string[i]);
1047
1048
3.83k
    hash = hash % ht->array_size;
1049
3.83k
    return hash;
1050
3.83k
}
1051
1052
static char CountersIdHashCompareFunc(void *data1, uint16_t datalen1,
1053
                               void *data2, uint16_t datalen2)
1054
1.74k
{
1055
1.74k
    CountersIdType *t1 = (CountersIdType *)data1;
1056
1.74k
    CountersIdType *t2 = (CountersIdType *)data2;
1057
1.74k
    int len1 = 0;
1058
1.74k
    int len2 = 0;
1059
1060
1.74k
    if (t1 == NULL || t2 == NULL)
1061
0
        return 0;
1062
1063
1.74k
    if (t1->string == NULL || t2->string == NULL)
1064
0
        return 0;
1065
1066
1.74k
    len1 = strlen(t1->string);
1067
1.74k
    len2 = strlen(t2->string);
1068
1069
1.74k
    if (len1 == len2 && memcmp(t1->string, t2->string, len1) == 0) {
1070
0
        return 1;
1071
0
    }
1072
1073
1.74k
    return 0;
1074
1.74k
}
1075
1076
static void CountersIdHashFreeFunc(void *data)
1077
0
{
1078
0
    SCFree(data);
1079
0
}
1080
1081
1082
/** \internal
1083
 *  \brief Adds a TM to the clubbed TM table.  Multiple instances of the same TM
1084
 *         are stacked together in a PCTMI container.
1085
 *
1086
 *  \param tm_name Name of the tm to be added to the table
1087
 *  \param pctx    StatsPublicThreadContext associated with the TM tm_name
1088
 *
1089
 *  \retval 1 on success, 0 on failure
1090
 */
1091
static int StatsThreadRegister(const char *thread_name, StatsPublicThreadContext *pctx)
1092
4
{
1093
4
    if (stats_ctx == NULL) {
1094
0
        SCLogDebug("Counter module has been disabled");
1095
0
        return 0;
1096
0
    }
1097
1098
4
    if (thread_name == NULL || pctx == NULL) {
1099
0
        SCLogDebug("supplied argument(s) to StatsThreadRegister NULL");
1100
0
        return 0;
1101
0
    }
1102
1103
4
    SCMutexLock(&stats_ctx->sts_lock);
1104
4
    if (stats_ctx->counters_id_hash == NULL) {
1105
4
        stats_ctx->counters_id_hash = HashTableInit(256, CountersIdHashFunc,
1106
4
                                                              CountersIdHashCompareFunc,
1107
4
                                                              CountersIdHashFreeFunc);
1108
4
        BUG_ON(stats_ctx->counters_id_hash == NULL);
1109
4
    }
1110
4
    StatsCounter *pc = pctx->head;
1111
1.92k
    while (pc != NULL) {
1112
1.91k
        CountersIdType t = { 0, pc->name }, *id = NULL;
1113
1.91k
        id = HashTableLookup(stats_ctx->counters_id_hash, &t, sizeof(t));
1114
1.91k
        if (id == NULL) {
1115
1.91k
            id = SCCalloc(1, sizeof(*id));
1116
1.91k
            BUG_ON(id == NULL);
1117
1.91k
            id->id = counters_global_id++;
1118
1.91k
            id->string = pc->name;
1119
1.91k
            BUG_ON(HashTableAdd(stats_ctx->counters_id_hash, id, sizeof(*id)) < 0);
1120
1.91k
        }
1121
1.91k
        pc->gid = id->id;
1122
1.91k
        pc = pc->next;
1123
1.91k
    }
1124
1125
1126
4
    StatsThreadStore *temp = NULL;
1127
4
    if ((temp = SCCalloc(1, sizeof(StatsThreadStore))) == NULL) {
1128
0
        SCMutexUnlock(&stats_ctx->sts_lock);
1129
0
        return 0;
1130
0
    }
1131
1132
4
    temp->ctx = pctx;
1133
4
    temp->name = thread_name;
1134
1135
4
    temp->next = stats_ctx->sts;
1136
4
    stats_ctx->sts = temp;
1137
4
    stats_ctx->sts_cnt++;
1138
4
    SCLogDebug("stats_ctx->sts %p", stats_ctx->sts);
1139
1140
4
    SCMutexUnlock(&stats_ctx->sts_lock);
1141
4
    return 1;
1142
4
}
1143
1144
/** \internal
1145
 *  \brief Returns a counter array for counters in this id range(s_id - e_id)
1146
 *
1147
 *  \param s_id Counter id of the first counter to be added to the array
1148
 *  \param e_id Counter id of the last counter to be added to the array
1149
 *  \param pctx Pointer to the tv's StatsPublicThreadContext
1150
 *
1151
 *  \retval a counter-array in this(s_id-e_id) range for this TM instance
1152
 */
1153
static int StatsGetCounterArrayRange(uint16_t s_id, uint16_t e_id,
1154
                                      StatsPublicThreadContext *pctx,
1155
                                      StatsPrivateThreadContext *pca)
1156
4
{
1157
4
    StatsCounter *pc = NULL;
1158
4
    uint32_t i = 0;
1159
1160
4
    if (pctx == NULL || pca == NULL) {
1161
0
        SCLogDebug("pctx/pca is NULL");
1162
0
        return -1;
1163
0
    }
1164
1165
4
    if (s_id < 1 || e_id < 1 || s_id > e_id) {
1166
0
        SCLogDebug("error with the counter ids");
1167
0
        return -1;
1168
0
    }
1169
1170
4
    if (e_id > pctx->curr_id) {
1171
0
        SCLogDebug("end id is greater than the max id for this tv");
1172
0
        return -1;
1173
0
    }
1174
1175
4
    if ((pca->head = SCCalloc(1, sizeof(StatsLocalCounter) * (e_id - s_id + 2))) == NULL) {
1176
0
        return -1;
1177
0
    }
1178
1179
4
    pc = pctx->head;
1180
4
    while (pc->id != s_id)
1181
0
        pc = pc->next;
1182
1183
4
    i = 1;
1184
1.92k
    while ((pc != NULL) && (pc->id <= e_id)) {
1185
1.91k
        pca->head[i].pc = pc;
1186
1.91k
        pca->head[i].id = pc->id;
1187
1.91k
        pc = pc->next;
1188
1.91k
        i++;
1189
1.91k
    }
1190
4
    pca->size = i - 1;
1191
1192
4
    pca->initialized = 1;
1193
4
    return 0;
1194
4
}
1195
1196
/** \internal
1197
 *  \brief Returns a counter array for all counters registered for this tm
1198
 *         instance
1199
 *
1200
 *  \param pctx Pointer to the tv's StatsPublicThreadContext
1201
 *
1202
 *  \retval pca Pointer to a counter-array for all counter of this tm instance
1203
 *              on success; NULL on failure
1204
 */
1205
static int StatsGetAllCountersArray(StatsPublicThreadContext *pctx, StatsPrivateThreadContext *private)
1206
4
{
1207
4
    if (pctx == NULL || private == NULL)
1208
0
        return -1;
1209
1210
4
    return StatsGetCounterArrayRange(1, pctx->curr_id, pctx, private);
1211
4
}
1212
1213
1214
int StatsSetupPrivate(ThreadVars *tv)
1215
4
{
1216
4
    StatsGetAllCountersArray(&(tv)->perf_public_ctx, &(tv)->perf_private_ctx);
1217
1218
4
    StatsThreadRegister(tv->printable_name ? tv->printable_name : tv->name,
1219
4
        &(tv)->perf_public_ctx);
1220
4
    return 0;
1221
4
}
1222
1223
/**
1224
 * \brief the private stats store with the public stats store
1225
 *
1226
 * \param pca      Pointer to the StatsPrivateThreadContext
1227
 * \param pctx     Pointer the tv's StatsPublicThreadContext
1228
 *
1229
 * \retval  1 on success
1230
 * \retval -1 on error
1231
 */
1232
int StatsUpdateCounterArray(StatsPrivateThreadContext *pca, StatsPublicThreadContext *pctx)
1233
0
{
1234
1235
0
    if (pca == NULL || pctx == NULL) {
1236
0
        SCLogDebug("pca or pctx is NULL inside StatsUpdateCounterArray");
1237
0
        return -1;
1238
0
    }
1239
1240
0
    SCMutexLock(&pctx->m);
1241
0
    StatsLocalCounter *pcae = pca->head;
1242
0
    for (uint32_t i = 1; i <= pca->size; i++) {
1243
0
        StatsCopyCounterValue(&pcae[i]);
1244
0
    }
1245
0
    SCMutexUnlock(&pctx->m);
1246
1247
0
    pctx->perf_flag = 0;
1248
0
    return 1;
1249
0
}
1250
1251
/**
1252
 * \brief Get the value of the local copy of the counter that hold this id.
1253
 *
1254
 * \param tv threadvars
1255
 * \param id The counter id.
1256
 *
1257
 * \retval  0 on success.
1258
 * \retval -1 on error.
1259
 */
1260
uint64_t StatsGetLocalCounterValue(ThreadVars *tv, uint16_t id)
1261
0
{
1262
0
    StatsPrivateThreadContext *pca = &tv->perf_private_ctx;
1263
#ifdef DEBUG
1264
    BUG_ON ((id < 1) || (id > pca->size));
1265
#endif
1266
0
    return pca->head[id].value;
1267
0
}
1268
1269
/**
1270
 * \brief Releases the resources allotted by the Stats API
1271
 */
1272
void StatsReleaseResources(void)
1273
0
{
1274
0
    StatsLogSummary();
1275
0
    StatsReleaseCtx();
1276
0
}
1277
1278
/**
1279
 * \brief Releases counters
1280
 *
1281
 * \param head Pointer to the head of the list of perf counters that have to
1282
 *             be freed
1283
 */
1284
void StatsReleaseCounters(StatsCounter *head)
1285
0
{
1286
0
    StatsCounter *pc = NULL;
1287
1288
0
    while (head != NULL) {
1289
0
        pc = head;
1290
0
        head = head->next;
1291
0
        StatsReleaseCounter(pc);
1292
0
    }
1293
0
}
1294
1295
/** \internal
1296
 *  \brief Releases the StatsPrivateThreadContext allocated by the user,
1297
 *         for storing and updating local counter values
1298
 *
1299
 * \param pca Pointer to the StatsPrivateThreadContext
1300
 */
1301
static void StatsReleasePrivateThreadContext(StatsPrivateThreadContext *pca)
1302
0
{
1303
0
    if (pca != NULL) {
1304
0
        if (pca->head != NULL) {
1305
0
            SCFree(pca->head);
1306
0
            pca->head = NULL;
1307
0
            pca->size = 0;
1308
0
        }
1309
0
        pca->initialized = 0;
1310
0
    }
1311
0
}
1312
1313
void StatsThreadCleanup(ThreadVars *tv)
1314
0
{
1315
0
    StatsPublicThreadContextCleanup(&tv->perf_public_ctx);
1316
0
    StatsReleasePrivateThreadContext(&tv->perf_private_ctx);
1317
0
}
1318
1319
/*----------------------------------Unit_Tests--------------------------------*/
1320
1321
#ifdef UNITTESTS
1322
/** \internal
1323
 * \brief Registers a normal, unqualified counter
1324
 *
1325
 * \param name   Name of the counter, to be registered
1326
 * \param tm_name Name of the engine module under which the counter has to be
1327
 *                registered
1328
 * \param type    Datatype of this counter variable
1329
 * \param pctx    StatsPublicThreadContext corresponding to the tm_name key under which the
1330
 *                key has to be registered
1331
 *
1332
 * \retval id Counter id for the newly registered counter, or the already
1333
 *            present counter
1334
 */
1335
static uint16_t RegisterCounter(const char *name, const char *tm_name,
1336
                               StatsPublicThreadContext *pctx)
1337
{
1338
    uint16_t id = StatsRegisterQualifiedCounter(name, tm_name, pctx,
1339
                                                STATS_TYPE_NORMAL, NULL);
1340
    return id;
1341
}
1342
1343
static int StatsTestCounterReg02(void)
1344
{
1345
    StatsPublicThreadContext pctx;
1346
1347
    memset(&pctx, 0, sizeof(StatsPublicThreadContext));
1348
1349
    return RegisterCounter(NULL, NULL, &pctx) == 0;
1350
}
1351
1352
static int StatsTestCounterReg03(void)
1353
{
1354
    StatsPublicThreadContext pctx;
1355
    int result;
1356
1357
    memset(&pctx, 0, sizeof(StatsPublicThreadContext));
1358
1359
    result = RegisterCounter("t1", "c1", &pctx);
1360
1361
    FAIL_IF_NOT(result);
1362
1363
    StatsReleaseCounters(pctx.head);
1364
1365
    PASS;
1366
}
1367
1368
static int StatsTestCounterReg04(void)
1369
{
1370
    StatsPublicThreadContext pctx;
1371
    int result;
1372
1373
    memset(&pctx, 0, sizeof(StatsPublicThreadContext));
1374
1375
    RegisterCounter("t1", "c1", &pctx);
1376
    RegisterCounter("t2", "c2", &pctx);
1377
    RegisterCounter("t3", "c3", &pctx);
1378
1379
    result = RegisterCounter("t1", "c1", &pctx);
1380
1381
    FAIL_IF_NOT(result);
1382
1383
    StatsReleaseCounters(pctx.head);
1384
1385
    PASS;
1386
}
1387
1388
static int StatsTestGetCntArray05(void)
1389
{
1390
    ThreadVars tv;
1391
    int id;
1392
1393
    memset(&tv, 0, sizeof(ThreadVars));
1394
1395
    id = RegisterCounter("t1", "c1", &tv.perf_public_ctx);
1396
    FAIL_IF(id != 1);
1397
1398
    int r = StatsGetAllCountersArray(NULL, &tv.perf_private_ctx);
1399
    FAIL_IF_NOT(r == -1);
1400
    PASS;
1401
}
1402
1403
static int StatsTestGetCntArray06(void)
1404
{
1405
    ThreadVars tv;
1406
    int id;
1407
1408
    memset(&tv, 0, sizeof(ThreadVars));
1409
1410
    id = RegisterCounter("t1", "c1", &tv.perf_public_ctx);
1411
    FAIL_IF(id != 1);
1412
1413
    int r = StatsGetAllCountersArray(&tv.perf_public_ctx, &tv.perf_private_ctx);
1414
    FAIL_IF_NOT(r == 0);
1415
1416
    StatsReleaseCounters(tv.perf_public_ctx.head);
1417
    StatsReleasePrivateThreadContext(&tv.perf_private_ctx);
1418
1419
    PASS;
1420
}
1421
1422
static int StatsTestCntArraySize07(void)
1423
{
1424
    ThreadVars tv;
1425
    StatsPrivateThreadContext *pca = NULL;
1426
1427
    memset(&tv, 0, sizeof(ThreadVars));
1428
1429
    //pca = (StatsPrivateThreadContext *)&tv.perf_private_ctx;
1430
1431
    RegisterCounter("t1", "c1", &tv.perf_public_ctx);
1432
    RegisterCounter("t2", "c2", &tv.perf_public_ctx);
1433
1434
    StatsGetAllCountersArray(&tv.perf_public_ctx, &tv.perf_private_ctx);
1435
    pca = &tv.perf_private_ctx;
1436
1437
    StatsIncr(&tv, 1);
1438
    StatsIncr(&tv, 2);
1439
1440
    FAIL_IF_NOT(pca->size == 2);
1441
1442
    StatsReleaseCounters(tv.perf_public_ctx.head);
1443
    StatsReleasePrivateThreadContext(pca);
1444
1445
    PASS;
1446
}
1447
1448
static int StatsTestUpdateCounter08(void)
1449
{
1450
    ThreadVars tv;
1451
    StatsPrivateThreadContext *pca = NULL;
1452
    int id;
1453
1454
    memset(&tv, 0, sizeof(ThreadVars));
1455
1456
    id = RegisterCounter("t1", "c1", &tv.perf_public_ctx);
1457
1458
    StatsGetAllCountersArray(&tv.perf_public_ctx, &tv.perf_private_ctx);
1459
    pca = &tv.perf_private_ctx;
1460
1461
    StatsIncr(&tv, id);
1462
    StatsAddUI64(&tv, id, 100);
1463
1464
    FAIL_IF_NOT(pca->head[id].value == 101);
1465
1466
    StatsReleaseCounters(tv.perf_public_ctx.head);
1467
    StatsReleasePrivateThreadContext(pca);
1468
1469
    PASS;
1470
}
1471
1472
static int StatsTestUpdateCounter09(void)
1473
{
1474
    ThreadVars tv;
1475
    StatsPrivateThreadContext *pca = NULL;
1476
    uint16_t id1, id2;
1477
1478
    memset(&tv, 0, sizeof(ThreadVars));
1479
1480
    id1 = RegisterCounter("t1", "c1", &tv.perf_public_ctx);
1481
    RegisterCounter("t2", "c2", &tv.perf_public_ctx);
1482
    RegisterCounter("t3", "c3", &tv.perf_public_ctx);
1483
    RegisterCounter("t4", "c4", &tv.perf_public_ctx);
1484
    id2 = RegisterCounter("t5", "c5", &tv.perf_public_ctx);
1485
1486
    StatsGetAllCountersArray(&tv.perf_public_ctx, &tv.perf_private_ctx);
1487
    pca = &tv.perf_private_ctx;
1488
1489
    StatsIncr(&tv, id2);
1490
    StatsAddUI64(&tv, id2, 100);
1491
1492
    FAIL_IF_NOT((pca->head[id1].value == 0) && (pca->head[id2].value == 101));
1493
1494
    StatsReleaseCounters(tv.perf_public_ctx.head);
1495
    StatsReleasePrivateThreadContext(pca);
1496
1497
    PASS;
1498
}
1499
1500
static int StatsTestUpdateGlobalCounter10(void)
1501
{
1502
    ThreadVars tv;
1503
    StatsPrivateThreadContext *pca = NULL;
1504
1505
    int result = 1;
1506
    uint16_t id1, id2, id3;
1507
1508
    memset(&tv, 0, sizeof(ThreadVars));
1509
1510
    id1 = RegisterCounter("t1", "c1", &tv.perf_public_ctx);
1511
    id2 = RegisterCounter("t2", "c2", &tv.perf_public_ctx);
1512
    id3 = RegisterCounter("t3", "c3", &tv.perf_public_ctx);
1513
1514
    StatsGetAllCountersArray(&tv.perf_public_ctx, &tv.perf_private_ctx);
1515
    pca = &tv.perf_private_ctx;
1516
1517
    StatsIncr(&tv, id1);
1518
    StatsAddUI64(&tv, id2, 100);
1519
    StatsIncr(&tv, id3);
1520
    StatsAddUI64(&tv, id3, 100);
1521
1522
    StatsUpdateCounterArray(pca, &tv.perf_public_ctx);
1523
1524
    result = (1 == tv.perf_public_ctx.head->value);
1525
    result &= (100 == tv.perf_public_ctx.head->next->value);
1526
    result &= (101 == tv.perf_public_ctx.head->next->next->value);
1527
    FAIL_IF_NOT(result);
1528
1529
    StatsReleaseCounters(tv.perf_public_ctx.head);
1530
    StatsReleasePrivateThreadContext(pca);
1531
1532
    PASS;
1533
}
1534
1535
static int StatsTestCounterValues11(void)
1536
{
1537
    ThreadVars tv;
1538
    StatsPrivateThreadContext *pca = NULL;
1539
1540
    int result = 1;
1541
    uint16_t id1, id2, id3, id4;
1542
1543
    memset(&tv, 0, sizeof(ThreadVars));
1544
1545
    id1 = RegisterCounter("t1", "c1", &tv.perf_public_ctx);
1546
    id2 = RegisterCounter("t2", "c2", &tv.perf_public_ctx);
1547
    id3 = RegisterCounter("t3", "c3", &tv.perf_public_ctx);
1548
    id4 = RegisterCounter("t4", "c4", &tv.perf_public_ctx);
1549
1550
    StatsGetAllCountersArray(&tv.perf_public_ctx, &tv.perf_private_ctx);
1551
    pca = &tv.perf_private_ctx;
1552
1553
    StatsIncr(&tv, id1);
1554
    StatsAddUI64(&tv, id2, 256);
1555
    StatsAddUI64(&tv, id3, 257);
1556
    StatsAddUI64(&tv, id4, 16843024);
1557
1558
    StatsUpdateCounterArray(pca, &tv.perf_public_ctx);
1559
1560
    result &= (1 == tv.perf_public_ctx.head->value);
1561
    result &= (256 == tv.perf_public_ctx.head->next->value);
1562
    result &= (257 == tv.perf_public_ctx.head->next->next->value);
1563
    result &= (16843024 == tv.perf_public_ctx.head->next->next->next->value);
1564
    FAIL_IF_NOT(result);
1565
1566
    StatsReleaseCounters(tv.perf_public_ctx.head);
1567
    StatsReleasePrivateThreadContext(pca);
1568
1569
    PASS;
1570
}
1571
1572
#endif
1573
1574
void StatsRegisterTests(void)
1575
0
{
1576
#ifdef UNITTESTS
1577
    UtRegisterTest("StatsTestCounterReg02", StatsTestCounterReg02);
1578
    UtRegisterTest("StatsTestCounterReg03", StatsTestCounterReg03);
1579
    UtRegisterTest("StatsTestCounterReg04", StatsTestCounterReg04);
1580
    UtRegisterTest("StatsTestGetCntArray05", StatsTestGetCntArray05);
1581
    UtRegisterTest("StatsTestGetCntArray06", StatsTestGetCntArray06);
1582
    UtRegisterTest("StatsTestCntArraySize07", StatsTestCntArraySize07);
1583
    UtRegisterTest("StatsTestUpdateCounter08", StatsTestUpdateCounter08);
1584
    UtRegisterTest("StatsTestUpdateCounter09", StatsTestUpdateCounter09);
1585
    UtRegisterTest("StatsTestUpdateGlobalCounter10",
1586
                   StatsTestUpdateGlobalCounter10);
1587
    UtRegisterTest("StatsTestCounterValues11", StatsTestCounterValues11);
1588
#endif
1589
0
}