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