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