1 /* Copyright (C) 2007-2012 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 Endace Technology Limited.
22  * \author Victor Julien <victor@inliniac.net>
23  *
24  * An API for rule profiling operations.
25  */
26 
27 #include "suricata-common.h"
28 #include "decode.h"
29 #include "detect.h"
30 #include "conf.h"
31 
32 #include "tm-threads.h"
33 
34 #include "util-unittest.h"
35 #include "util-byte.h"
36 #include "util-profiling.h"
37 #include "util-profiling-locks.h"
38 
39 #ifdef PROFILING
40 
41 /**
42  * Extra data for rule profiling.
43  */
44 typedef struct SCProfileData_ {
45     uint32_t sid;
46     uint32_t gid;
47     uint32_t rev;
48     uint64_t checks;
49     uint64_t matches;
50     uint64_t max;
51     uint64_t ticks_match;
52     uint64_t ticks_no_match;
53 } SCProfileData;
54 
55 typedef struct SCProfileDetectCtx_ {
56     uint32_t size;
57     uint32_t id;
58     SCProfileData *data;
59     pthread_mutex_t data_m;
60 } SCProfileDetectCtx;
61 
62 /**
63  * Used for generating the summary data to print.
64  */
65 typedef struct SCProfileSummary_ {
66     uint32_t sid;
67     uint32_t gid;
68     uint32_t rev;
69     uint64_t ticks;
70     double avgticks;
71     double avgticks_match;
72     double avgticks_no_match;
73     uint64_t checks;
74     uint64_t matches;
75     uint64_t max;
76     uint64_t ticks_match;
77     uint64_t ticks_no_match;
78 } SCProfileSummary;
79 
80 extern int profiling_output_to_file;
81 int profiling_rules_enabled = 0;
82 static char profiling_file_name[PATH_MAX] = "";
83 static const char *profiling_file_mode = "a";
84 static int profiling_rule_json = 0;
85 
86 /**
87  * Sort orders for dumping profiled rules.
88  */
89 enum {
90     SC_PROFILING_RULES_SORT_BY_TICKS = 0,
91     SC_PROFILING_RULES_SORT_BY_AVG_TICKS,
92     SC_PROFILING_RULES_SORT_BY_CHECKS,
93     SC_PROFILING_RULES_SORT_BY_MATCHES,
94     SC_PROFILING_RULES_SORT_BY_MAX_TICKS,
95     SC_PROFILING_RULES_SORT_BY_AVG_TICKS_MATCH,
96     SC_PROFILING_RULES_SORT_BY_AVG_TICKS_NO_MATCH,
97 };
98 
99 static int profiling_rules_sort_orders[8] = {
100     SC_PROFILING_RULES_SORT_BY_TICKS,
101     SC_PROFILING_RULES_SORT_BY_AVG_TICKS,
102     SC_PROFILING_RULES_SORT_BY_AVG_TICKS_MATCH,
103     SC_PROFILING_RULES_SORT_BY_AVG_TICKS_NO_MATCH,
104     SC_PROFILING_RULES_SORT_BY_CHECKS,
105     SC_PROFILING_RULES_SORT_BY_MATCHES,
106     SC_PROFILING_RULES_SORT_BY_MAX_TICKS,
107     -1 };
108 
109 /**
110  * Maximum number of rules to dump.
111  */
112 static uint32_t profiling_rules_limit = UINT32_MAX;
113 
SCProfilingRulesGlobalInit(void)114 void SCProfilingRulesGlobalInit(void)
115 {
116 #define SET_ONE(x) { \
117         profiling_rules_sort_orders[0] = (x); \
118         profiling_rules_sort_orders[1] = -1;  \
119     }
120 
121     ConfNode *conf;
122     const char *val;
123 
124     conf = ConfGetNode("profiling.rules");
125     if (conf != NULL) {
126         if (ConfNodeChildValueIsTrue(conf, "enabled")) {
127             profiling_rules_enabled = 1;
128 
129             val = ConfNodeLookupChildValue(conf, "sort");
130             if (val != NULL) {
131                 if (strcmp(val, "ticks") == 0) {
132                     SET_ONE(SC_PROFILING_RULES_SORT_BY_TICKS);
133                 }
134                 else if (strcmp(val, "avgticks") == 0) {
135                     SET_ONE(SC_PROFILING_RULES_SORT_BY_AVG_TICKS);
136                 }
137                 else if (strcmp(val, "avgticks_match") == 0) {
138                     SET_ONE(SC_PROFILING_RULES_SORT_BY_AVG_TICKS_MATCH);
139                 }
140                 else if (strcmp(val, "avgticks_no_match") == 0) {
141                     SET_ONE(SC_PROFILING_RULES_SORT_BY_AVG_TICKS_NO_MATCH);
142                 }
143                 else if (strcmp(val, "checks") == 0) {
144                     SET_ONE(SC_PROFILING_RULES_SORT_BY_CHECKS);
145                 }
146                 else if (strcmp(val, "matches") == 0) {
147                     SET_ONE(SC_PROFILING_RULES_SORT_BY_MATCHES);
148                 }
149                 else if (strcmp(val, "maxticks") == 0) {
150                     SET_ONE(SC_PROFILING_RULES_SORT_BY_MAX_TICKS);
151                 }
152                 else {
153                     SCLogError(SC_ERR_INVALID_ARGUMENT,
154                             "Invalid profiling sort order: %s", val);
155                     exit(EXIT_FAILURE);
156                 }
157             }
158 
159             val = ConfNodeLookupChildValue(conf, "limit");
160             if (val != NULL) {
161                 if (StringParseUint32(&profiling_rules_limit, 10,
162                             (uint16_t)strlen(val), val) <= 0) {
163                     SCLogError(SC_ERR_INVALID_ARGUMENT, "Invalid limit: %s", val);
164                     exit(EXIT_FAILURE);
165                 }
166             }
167             const char *filename = ConfNodeLookupChildValue(conf, "filename");
168             if (filename != NULL) {
169 
170                 const char *log_dir;
171                 log_dir = ConfigGetLogDirectory();
172 
173                 snprintf(profiling_file_name, sizeof(profiling_file_name),
174                         "%s/%s", log_dir, filename);
175 
176                 const char *v = ConfNodeLookupChildValue(conf, "append");
177                 if (v == NULL || ConfValIsTrue(v)) {
178                     profiling_file_mode = "a";
179                 } else {
180                     profiling_file_mode = "w";
181                 }
182 
183                 profiling_output_to_file = 1;
184             }
185             if (ConfNodeChildValueIsTrue(conf, "json")) {
186                 profiling_rule_json = 1;
187             }
188         }
189     }
190 #undef SET_ONE
191 }
192 
193 /**
194  * \brief Qsort comparison function to sort by ticks.
195  */
196 static int
SCProfileSummarySortByTicks(const void * a,const void * b)197 SCProfileSummarySortByTicks(const void *a, const void *b)
198 {
199     const SCProfileSummary *s0 = a;
200     const SCProfileSummary *s1 = b;
201     if (s1->ticks == s0->ticks)
202         return 0;
203     else
204         return s0->ticks > s1->ticks ? -1 : 1;
205 }
206 
207 /**
208  * \brief Qsort comparison function to sort by average ticks per match.
209  */
210 static int
SCProfileSummarySortByAvgTicksMatch(const void * a,const void * b)211 SCProfileSummarySortByAvgTicksMatch(const void *a, const void *b)
212 {
213     const SCProfileSummary *s0 = a;
214     const SCProfileSummary *s1 = b;
215     if (s1->avgticks_match == s0->avgticks_match)
216         return 0;
217     else
218         return s0->avgticks_match > s1->avgticks_match ? -1 : 1;
219 }
220 
221 /**
222  * \brief Qsort comparison function to sort by average ticks per non match.
223  */
224 static int
SCProfileSummarySortByAvgTicksNoMatch(const void * a,const void * b)225 SCProfileSummarySortByAvgTicksNoMatch(const void *a, const void *b)
226 {
227     const SCProfileSummary *s0 = a;
228     const SCProfileSummary *s1 = b;
229     if (s1->avgticks_no_match == s0->avgticks_no_match)
230         return 0;
231     else
232         return s0->avgticks_no_match > s1->avgticks_no_match ? -1 : 1;
233 }
234 
235 /**
236  * \brief Qsort comparison function to sort by average ticks.
237  */
238 static int
SCProfileSummarySortByAvgTicks(const void * a,const void * b)239 SCProfileSummarySortByAvgTicks(const void *a, const void *b)
240 {
241     const SCProfileSummary *s0 = a;
242     const SCProfileSummary *s1 = b;
243     if (s1->avgticks == s0->avgticks)
244         return 0;
245     else
246         return s0->avgticks > s1->avgticks ? -1 : 1;
247 }
248 
249 /**
250  * \brief Qsort comparison function to sort by checks.
251  */
252 static int
SCProfileSummarySortByChecks(const void * a,const void * b)253 SCProfileSummarySortByChecks(const void *a, const void *b)
254 {
255     const SCProfileSummary *s0 = a;
256     const SCProfileSummary *s1 = b;
257     if (s1->checks == s0->checks)
258         return 0;
259     else
260         return s0->checks > s1->checks ? -1 : 1;
261 }
262 
263 /**
264  * \brief Qsort comparison function to sort by matches.
265  */
266 static int
SCProfileSummarySortByMatches(const void * a,const void * b)267 SCProfileSummarySortByMatches(const void *a, const void *b)
268 {
269     const SCProfileSummary *s0 = a;
270     const SCProfileSummary *s1 = b;
271     if (s1->matches == s0->matches)
272         return 0;
273     else
274         return s0->matches > s1->matches ? -1 : 1;
275 }
276 
277 /**
278  * \brief Qsort comparison function to sort by max ticks.
279  */
280 static int
SCProfileSummarySortByMaxTicks(const void * a,const void * b)281 SCProfileSummarySortByMaxTicks(const void *a, const void *b)
282 {
283     const SCProfileSummary *s0 = a;
284     const SCProfileSummary *s1 = b;
285     if (s1->max == s0->max)
286         return 0;
287     else
288         return s0->max > s1->max ? -1 : 1;
289 }
290 
DumpJson(FILE * fp,SCProfileSummary * summary,uint32_t count,uint64_t total_ticks,const char * sort_desc)291 static void DumpJson(FILE *fp, SCProfileSummary *summary,
292         uint32_t count, uint64_t total_ticks,
293         const char *sort_desc)
294 {
295     char timebuf[64];
296     uint32_t i;
297     struct timeval tval;
298 
299     json_t *js = json_object();
300     if (js == NULL)
301         return;
302     json_t *jsa = json_array();
303     if (jsa == NULL) {
304         json_decref(js);
305         return;
306     }
307 
308     gettimeofday(&tval, NULL);
309     CreateIsoTimeString(&tval, timebuf, sizeof(timebuf));
310     json_object_set_new(js, "timestamp", json_string(timebuf));
311     json_object_set_new(js, "sort", json_string(sort_desc));
312 
313     for (i = 0; i < MIN(count, profiling_rules_limit); i++) {
314         /* Stop dumping when we hit our first rule with 0 checks.  Due
315          * to sorting this will be the beginning of all the rules with
316          * 0 checks. */
317         if (summary[i].checks == 0)
318             break;
319 
320         json_t *jsm = json_object();
321         if (jsm) {
322             json_object_set_new(jsm, "signature_id", json_integer(summary[i].sid));
323             json_object_set_new(jsm, "gid", json_integer(summary[i].gid));
324             json_object_set_new(jsm, "rev", json_integer(summary[i].rev));
325 
326             json_object_set_new(jsm, "checks", json_integer(summary[i].checks));
327             json_object_set_new(jsm, "matches", json_integer(summary[i].matches));
328 
329             json_object_set_new(jsm, "ticks_total", json_integer(summary[i].ticks));
330             json_object_set_new(jsm, "ticks_max", json_integer(summary[i].max));
331             json_object_set_new(jsm, "ticks_avg", json_integer(summary[i].avgticks));
332             json_object_set_new(jsm, "ticks_avg_match", json_integer(summary[i].avgticks_match));
333             json_object_set_new(jsm, "ticks_avg_nomatch", json_integer(summary[i].avgticks_no_match));
334 
335             double percent = (long double)summary[i].ticks /
336                 (long double)total_ticks * 100;
337             json_object_set_new(jsm, "percent", json_integer(percent));
338             json_array_append_new(jsa, jsm);
339         }
340     }
341     json_object_set_new(js, "rules", jsa);
342 
343     char *js_s = json_dumps(js,
344             JSON_PRESERVE_ORDER|JSON_COMPACT|JSON_ENSURE_ASCII|
345             JSON_ESCAPE_SLASH);
346 
347     if (unlikely(js_s == NULL))
348         return;
349     fprintf(fp, "%s", js_s);
350     free(js_s);
351     json_decref(js);
352 }
353 
DumpText(FILE * fp,SCProfileSummary * summary,uint32_t count,uint64_t total_ticks,const char * sort_desc)354 static void DumpText(FILE *fp, SCProfileSummary *summary,
355         uint32_t count, uint64_t total_ticks,
356         const char *sort_desc)
357 {
358     uint32_t i;
359     struct timeval tval;
360     struct tm *tms;
361     gettimeofday(&tval, NULL);
362     struct tm local_tm;
363     tms = SCLocalTime(tval.tv_sec, &local_tm);
364 
365     fprintf(fp, "  ----------------------------------------------"
366             "----------------------------\n");
367     fprintf(fp, "  Date: %" PRId32 "/%" PRId32 "/%04d -- "
368             "%02d:%02d:%02d.", tms->tm_mon + 1, tms->tm_mday, tms->tm_year + 1900,
369             tms->tm_hour,tms->tm_min, tms->tm_sec);
370     fprintf(fp, " Sorted by: %s.\n", sort_desc);
371     fprintf(fp, "  ----------------------------------------------"
372             "----------------------------\n");
373     fprintf(fp, "   %-8s %-12s %-8s %-8s %-12s %-6s %-8s %-8s %-11s %-11s %-11s %-11s\n", "Num", "Rule", "Gid", "Rev", "Ticks", "%", "Checks", "Matches", "Max Ticks", "Avg Ticks", "Avg Match", "Avg No Match");
374     fprintf(fp, "  -------- "
375         "------------ "
376         "-------- "
377         "-------- "
378         "------------ "
379         "------ "
380         "-------- "
381         "-------- "
382         "----------- "
383         "----------- "
384         "----------- "
385         "-------------- "
386         "\n");
387     for (i = 0; i < MIN(count, profiling_rules_limit); i++) {
388 
389         /* Stop dumping when we hit our first rule with 0 checks.  Due
390          * to sorting this will be the beginning of all the rules with
391          * 0 checks. */
392         if (summary[i].checks == 0)
393             break;
394 
395         double percent = (long double)summary[i].ticks /
396             (long double)total_ticks * 100;
397         fprintf(fp,
398             "  %-8"PRIu32" %-12u %-8"PRIu32" %-8"PRIu32" %-12"PRIu64" %-6.2f %-8"PRIu64" %-8"PRIu64" %-11"PRIu64" %-11.2f %-11.2f %-11.2f\n",
399             i + 1,
400             summary[i].sid,
401             summary[i].gid,
402             summary[i].rev,
403             summary[i].ticks,
404             percent,
405             summary[i].checks,
406             summary[i].matches,
407             summary[i].max,
408             summary[i].avgticks,
409             summary[i].avgticks_match,
410             summary[i].avgticks_no_match);
411     }
412 
413     fprintf(fp,"\n");
414 }
415 
416 /**
417  * \brief Dump rule profiling information to file
418  *
419  * \param de_ctx The active DetectEngineCtx, used to get at the loaded rules.
420  */
421 static void
SCProfilingRuleDump(SCProfileDetectCtx * rules_ctx)422 SCProfilingRuleDump(SCProfileDetectCtx *rules_ctx)
423 {
424     uint32_t i;
425     FILE *fp;
426 
427     if (rules_ctx == NULL)
428         return;
429 
430     if (profiling_output_to_file == 1) {
431         fp = fopen(profiling_file_name, profiling_file_mode);
432 
433         if (fp == NULL) {
434             SCLogError(SC_ERR_FOPEN, "failed to open %s: %s", profiling_file_name,
435                     strerror(errno));
436             return;
437         }
438     } else {
439        fp = stdout;
440     }
441 
442     int summary_size = sizeof(SCProfileSummary) * rules_ctx->size;
443     SCProfileSummary *summary = SCMalloc(summary_size);
444     if (unlikely(summary == NULL)) {
445         SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory for profiling summary");
446         return;
447     }
448 
449     uint32_t count = rules_ctx->size;
450     uint64_t total_ticks = 0;
451 
452     SCLogPerf("Dumping profiling data for %u rules.", count);
453 
454     memset(summary, 0, summary_size);
455     for (i = 0; i < count; i++) {
456         summary[i].sid = rules_ctx->data[i].sid;
457         summary[i].rev = rules_ctx->data[i].rev;
458         summary[i].gid = rules_ctx->data[i].gid;
459 
460         summary[i].ticks = rules_ctx->data[i].ticks_match + rules_ctx->data[i].ticks_no_match;
461         summary[i].checks = rules_ctx->data[i].checks;
462 
463         if (summary[i].ticks > 0) {
464             summary[i].avgticks = (long double)summary[i].ticks / (long double)summary[i].checks;
465         }
466 
467         summary[i].matches = rules_ctx->data[i].matches;
468         summary[i].max = rules_ctx->data[i].max;
469         summary[i].ticks_match = rules_ctx->data[i].ticks_match;
470         summary[i].ticks_no_match = rules_ctx->data[i].ticks_no_match;
471         if (summary[i].ticks_match > 0) {
472             summary[i].avgticks_match = (long double)summary[i].ticks_match /
473                 (long double)summary[i].matches;
474         }
475 
476         if (summary[i].ticks_no_match > 0) {
477             summary[i].avgticks_no_match = (long double)summary[i].ticks_no_match /
478                 ((long double)summary[i].checks - (long double)summary[i].matches);
479         }
480         total_ticks += summary[i].ticks;
481     }
482 
483     int *order = profiling_rules_sort_orders;
484     while (*order != -1) {
485         const char *sort_desc = NULL;
486         switch (*order) {
487             case SC_PROFILING_RULES_SORT_BY_TICKS:
488                 qsort(summary, count, sizeof(SCProfileSummary),
489                         SCProfileSummarySortByTicks);
490                 sort_desc = "ticks";
491                 break;
492             case SC_PROFILING_RULES_SORT_BY_AVG_TICKS:
493                 qsort(summary, count, sizeof(SCProfileSummary),
494                         SCProfileSummarySortByAvgTicks);
495                 sort_desc = "average ticks";
496                 break;
497             case SC_PROFILING_RULES_SORT_BY_CHECKS:
498                 qsort(summary, count, sizeof(SCProfileSummary),
499                         SCProfileSummarySortByChecks);
500                 sort_desc = "number of checks";
501                 break;
502             case SC_PROFILING_RULES_SORT_BY_MATCHES:
503                 qsort(summary, count, sizeof(SCProfileSummary),
504                         SCProfileSummarySortByMatches);
505                 sort_desc = "number of matches";
506                 break;
507             case SC_PROFILING_RULES_SORT_BY_MAX_TICKS:
508                 qsort(summary, count, sizeof(SCProfileSummary),
509                         SCProfileSummarySortByMaxTicks);
510                 sort_desc = "max ticks";
511                 break;
512             case SC_PROFILING_RULES_SORT_BY_AVG_TICKS_MATCH:
513                 qsort(summary, count, sizeof(SCProfileSummary),
514                         SCProfileSummarySortByAvgTicksMatch);
515                 sort_desc = "average ticks (match)";
516                 break;
517             case SC_PROFILING_RULES_SORT_BY_AVG_TICKS_NO_MATCH:
518                 qsort(summary, count, sizeof(SCProfileSummary),
519                         SCProfileSummarySortByAvgTicksNoMatch);
520                 sort_desc = "average ticks (no match)";
521                 break;
522         }
523         if (profiling_rule_json) {
524             DumpJson(fp, summary, count, total_ticks, sort_desc);
525         } else {
526             DumpText(fp, summary, count, total_ticks, sort_desc);
527         }
528         order++;
529     }
530 
531     if (fp != stdout)
532         fclose(fp);
533     SCFree(summary);
534     SCLogPerf("Done dumping profiling data.");
535 }
536 
537 /**
538  * \brief Register a rule profiling counter.
539  *
540  * \retval Returns the ID of the counter on success, 0 on failure.
541  */
542 static uint16_t
SCProfilingRegisterRuleCounter(SCProfileDetectCtx * ctx)543 SCProfilingRegisterRuleCounter(SCProfileDetectCtx *ctx)
544 {
545     ctx->size++;
546     return ctx->id++;
547 }
548 
549 /**
550  * \brief Update a rule counter.
551  *
552  * \param id The ID of this counter.
553  * \param ticks Number of CPU ticks for this rule.
554  * \param match Did the rule match?
555  */
556 void
SCProfilingRuleUpdateCounter(DetectEngineThreadCtx * det_ctx,uint16_t id,uint64_t ticks,int match)557 SCProfilingRuleUpdateCounter(DetectEngineThreadCtx *det_ctx, uint16_t id, uint64_t ticks, int match)
558 {
559     if (det_ctx != NULL && det_ctx->rule_perf_data != NULL && det_ctx->rule_perf_data_size > id) {
560         SCProfileData *p = &det_ctx->rule_perf_data[id];
561 
562         p->checks++;
563         p->matches += match;
564         if (ticks > p->max)
565             p->max = ticks;
566         if (match == 1)
567             p->ticks_match += ticks;
568         else
569             p->ticks_no_match += ticks;
570     }
571 }
572 
SCProfilingRuleInitCtx(void)573 static SCProfileDetectCtx *SCProfilingRuleInitCtx(void)
574 {
575     SCProfileDetectCtx *ctx = SCMalloc(sizeof(SCProfileDetectCtx));
576     if (ctx != NULL) {
577         memset(ctx, 0x00, sizeof(SCProfileDetectCtx));
578 
579         if (pthread_mutex_init(&ctx->data_m, NULL) != 0) {
580                     FatalError(SC_ERR_FATAL,
581                                "Failed to initialize hash table mutex.");
582         }
583     }
584 
585     return ctx;
586 }
587 
SCProfilingRuleDestroyCtx(SCProfileDetectCtx * ctx)588 void SCProfilingRuleDestroyCtx(SCProfileDetectCtx *ctx)
589 {
590     if (ctx != NULL) {
591         SCProfilingRuleDump(ctx);
592         if (ctx->data != NULL)
593             SCFree(ctx->data);
594         pthread_mutex_destroy(&ctx->data_m);
595         SCFree(ctx);
596     }
597 }
598 
SCProfilingRuleThreadSetup(SCProfileDetectCtx * ctx,DetectEngineThreadCtx * det_ctx)599 void SCProfilingRuleThreadSetup(SCProfileDetectCtx *ctx, DetectEngineThreadCtx *det_ctx)
600 {
601     if (ctx == NULL|| ctx->size == 0)
602         return;
603 
604     SCProfileData *a = SCMalloc(sizeof(SCProfileData) * ctx->size);
605     if (a != NULL) {
606         memset(a, 0x00, sizeof(SCProfileData) * ctx->size);
607 
608         det_ctx->rule_perf_data = a;
609         det_ctx->rule_perf_data_size = ctx->size;
610     }
611 }
612 
SCProfilingRuleThreadMerge(DetectEngineCtx * de_ctx,DetectEngineThreadCtx * det_ctx)613 static void SCProfilingRuleThreadMerge(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx)
614 {
615     if (de_ctx == NULL || de_ctx->profile_ctx == NULL || de_ctx->profile_ctx->data == NULL ||
616         det_ctx == NULL || det_ctx->rule_perf_data == NULL)
617         return;
618 
619     int i;
620     for (i = 0; i < det_ctx->rule_perf_data_size; i++) {
621         de_ctx->profile_ctx->data[i].checks += det_ctx->rule_perf_data[i].checks;
622         de_ctx->profile_ctx->data[i].matches += det_ctx->rule_perf_data[i].matches;
623         de_ctx->profile_ctx->data[i].ticks_match += det_ctx->rule_perf_data[i].ticks_match;
624         de_ctx->profile_ctx->data[i].ticks_no_match += det_ctx->rule_perf_data[i].ticks_no_match;
625         if (det_ctx->rule_perf_data[i].max > de_ctx->profile_ctx->data[i].max)
626             de_ctx->profile_ctx->data[i].max = det_ctx->rule_perf_data[i].max;
627     }
628 }
629 
SCProfilingRuleThreadCleanup(DetectEngineThreadCtx * det_ctx)630 void SCProfilingRuleThreadCleanup(DetectEngineThreadCtx *det_ctx)
631 {
632     if (det_ctx == NULL || det_ctx->de_ctx == NULL || det_ctx->rule_perf_data == NULL)
633         return;
634 
635     pthread_mutex_lock(&det_ctx->de_ctx->profile_ctx->data_m);
636     SCProfilingRuleThreadMerge(det_ctx->de_ctx, det_ctx);
637     pthread_mutex_unlock(&det_ctx->de_ctx->profile_ctx->data_m);
638 
639     SCFree(det_ctx->rule_perf_data);
640     det_ctx->rule_perf_data = NULL;
641     det_ctx->rule_perf_data_size = 0;
642 }
643 
644 /**
645  * \brief Register the rule profiling counters.
646  *
647  * \param de_ctx The active DetectEngineCtx, used to get at the loaded rules.
648  */
649 void
SCProfilingRuleInitCounters(DetectEngineCtx * de_ctx)650 SCProfilingRuleInitCounters(DetectEngineCtx *de_ctx)
651 {
652     if (profiling_rules_enabled == 0)
653         return;
654 
655     de_ctx->profile_ctx = SCProfilingRuleInitCtx();
656     BUG_ON(de_ctx->profile_ctx == NULL);
657 
658     Signature *sig = de_ctx->sig_list;
659     uint32_t count = 0;
660     while (sig != NULL) {
661         sig->profiling_id = SCProfilingRegisterRuleCounter(de_ctx->profile_ctx);
662         sig = sig->next;
663         count++;
664     }
665 
666     if (count > 0) {
667         de_ctx->profile_ctx->data = SCMalloc(sizeof(SCProfileData) * de_ctx->profile_ctx->size);
668         BUG_ON(de_ctx->profile_ctx->data == NULL);
669         memset(de_ctx->profile_ctx->data, 0x00, sizeof(SCProfileData) * de_ctx->profile_ctx->size);
670 
671         sig = de_ctx->sig_list;
672         while (sig != NULL) {
673             de_ctx->profile_ctx->data[sig->profiling_id].sid = sig->id;
674             de_ctx->profile_ctx->data[sig->profiling_id].gid = sig->gid;
675             de_ctx->profile_ctx->data[sig->profiling_id].rev = sig->rev;
676             sig = sig->next;
677         }
678     }
679 
680     SCLogPerf("Registered %"PRIu32" rule profiling counters.", count);
681 }
682 
683 #endif /* PROFILING */
684 
685