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