suricata
util-profiling-rules.c
Go to the documentation of this file.
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;
54 
55 typedef struct SCProfileDetectCtx_ {
56  uint32_t size;
57  uint32_t id;
59  pthread_mutex_t data_m;
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;
73  uint64_t checks;
74  uint64_t matches;
75  uint64_t max;
76  uint64_t ticks_match;
77  uint64_t ticks_no_match;
79 
80 extern int profiling_output_to_file;
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 {
97 };
98 
99 static int profiling_rules_sort_orders[8] = {
107  -1 };
108 
109 /**
110  * Maximum number of rules to dump.
111  */
112 static uint32_t profiling_rules_limit = UINT32_MAX;
113 
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")) {
128 
129  val = ConfNodeLookupChildValue(conf, "sort");
130  if (val != NULL) {
131  if (strcmp(val, "ticks") == 0) {
133  }
134  else if (strcmp(val, "avgticks") == 0) {
136  }
137  else if (strcmp(val, "avgticks_match") == 0) {
139  }
140  else if (strcmp(val, "avgticks_no_match") == 0) {
142  }
143  else if (strcmp(val, "checks") == 0) {
145  }
146  else if (strcmp(val, "matches") == 0) {
148  }
149  else if (strcmp(val, "maxticks") == 0) {
151  }
152  else {
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 (ByteExtractStringUint32(&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 
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
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
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
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
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
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
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
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 
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|
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 
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
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) {
488  qsort(summary, count, sizeof(SCProfileSummary),
489  SCProfileSummarySortByTicks);
490  sort_desc = "ticks";
491  break;
493  qsort(summary, count, sizeof(SCProfileSummary),
494  SCProfileSummarySortByAvgTicks);
495  sort_desc = "average ticks";
496  break;
498  qsort(summary, count, sizeof(SCProfileSummary),
499  SCProfileSummarySortByChecks);
500  sort_desc = "number of checks";
501  break;
503  qsort(summary, count, sizeof(SCProfileSummary),
504  SCProfileSummarySortByMatches);
505  sort_desc = "number of matches";
506  break;
508  qsort(summary, count, sizeof(SCProfileSummary),
509  SCProfileSummarySortByMaxTicks);
510  sort_desc = "max ticks";
511  break;
513  qsort(summary, count, sizeof(SCProfileSummary),
514  SCProfileSummarySortByAvgTicksMatch);
515  sort_desc = "average ticks (match)";
516  break;
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
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
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 
573 static SCProfileDetectCtx *SCProfilingRuleInitCtx(void)
574 {
576  if (ctx != NULL) {
577  memset(ctx, 0x00, sizeof(SCProfileDetectCtx));
578 
579  if (pthread_mutex_init(&ctx->data_m, NULL) != 0) {
581  "Failed to initialize hash table mutex.");
582  exit(EXIT_FAILURE);
583  }
584  }
585 
586  return ctx;
587 }
588 
590 {
591  if (ctx != NULL) {
592  SCProfilingRuleDump(ctx);
593  if (ctx->data != NULL)
594  SCFree(ctx->data);
595  pthread_mutex_destroy(&ctx->data_m);
596  SCFree(ctx);
597  }
598 }
599 
601 {
602  if (ctx == NULL|| ctx->size == 0)
603  return;
604 
605  SCProfileData *a = SCMalloc(sizeof(SCProfileData) * ctx->size);
606  if (a != NULL) {
607  memset(a, 0x00, sizeof(SCProfileData) * ctx->size);
608 
609  det_ctx->rule_perf_data = a;
610  det_ctx->rule_perf_data_size = ctx->size;
611  }
612 }
613 
614 static void SCProfilingRuleThreadMerge(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx)
615 {
616  if (de_ctx == NULL || de_ctx->profile_ctx == NULL || de_ctx->profile_ctx->data == NULL ||
617  det_ctx == NULL || det_ctx->rule_perf_data == NULL)
618  return;
619 
620  int i;
621  for (i = 0; i < det_ctx->rule_perf_data_size; i++) {
622  de_ctx->profile_ctx->data[i].checks += det_ctx->rule_perf_data[i].checks;
623  de_ctx->profile_ctx->data[i].matches += det_ctx->rule_perf_data[i].matches;
624  de_ctx->profile_ctx->data[i].ticks_match += det_ctx->rule_perf_data[i].ticks_match;
625  de_ctx->profile_ctx->data[i].ticks_no_match += det_ctx->rule_perf_data[i].ticks_no_match;
626  if (det_ctx->rule_perf_data[i].max > de_ctx->profile_ctx->data[i].max)
627  de_ctx->profile_ctx->data[i].max = det_ctx->rule_perf_data[i].max;
628  }
629 }
630 
632 {
633  if (det_ctx == NULL || det_ctx->de_ctx == NULL || det_ctx->rule_perf_data == NULL)
634  return;
635 
636  pthread_mutex_lock(&det_ctx->de_ctx->profile_ctx->data_m);
637  SCProfilingRuleThreadMerge(det_ctx->de_ctx, det_ctx);
638  pthread_mutex_unlock(&det_ctx->de_ctx->profile_ctx->data_m);
639 
640  SCFree(det_ctx->rule_perf_data);
641  det_ctx->rule_perf_data = NULL;
642  det_ctx->rule_perf_data_size = 0;
643 }
644 
645 /**
646  * \brief Register the rule profiling counters.
647  *
648  * \param de_ctx The active DetectEngineCtx, used to get at the loaded rules.
649  */
650 void
652 {
653  if (profiling_rules_enabled == 0)
654  return;
655 
656  de_ctx->profile_ctx = SCProfilingRuleInitCtx();
657  BUG_ON(de_ctx->profile_ctx == NULL);
658 
659  Signature *sig = de_ctx->sig_list;
660  uint32_t count = 0;
661  while (sig != NULL) {
662  sig->profiling_id = SCProfilingRegisterRuleCounter(de_ctx->profile_ctx);
663  sig = sig->next;
664  count++;
665  }
666 
667  if (count > 0) {
668  de_ctx->profile_ctx->data = SCMalloc(sizeof(SCProfileData) * de_ctx->profile_ctx->size);
669  BUG_ON(de_ctx->profile_ctx->data == NULL);
670  memset(de_ctx->profile_ctx->data, 0x00, sizeof(SCProfileData) * de_ctx->profile_ctx->size);
671 
672  sig = de_ctx->sig_list;
673  while (sig != NULL) {
674  de_ctx->profile_ctx->data[sig->profiling_id].sid = sig->id;
675  de_ctx->profile_ctx->data[sig->profiling_id].gid = sig->gid;
676  de_ctx->profile_ctx->data[sig->profiling_id].rev = sig->rev;
677  sig = sig->next;
678  }
679  }
680 
681  SCLogPerf("Registered %"PRIu32" rule profiling counters.", count);
682 }
683 
684 #endif /* PROFILING */
685 
uint16_t profiling_id
Definition: detect.h:564
void SCProfilingRulesGlobalInit(void)
#define BUG_ON(x)
uint32_t id
Definition: detect.h:555
#define unlikely(expr)
Definition: util-optimize.h:35
void SCProfilingRuleThreadCleanup(DetectEngineThreadCtx *det_ctx)
Signature * sig_list
Definition: detect.h:767
#define MIN(x, y)
struct SCProfileData_ * rule_perf_data
Definition: detect.h:1154
int ByteExtractStringUint32(uint32_t *res, int base, uint16_t len, const char *str)
Definition: util-byte.c:244
Signature container.
Definition: detect.h:522
int profiling_rules_enabled
int ConfNodeChildValueIsTrue(const ConfNode *node, const char *key)
Test if a configuration node has a true value.
Definition: conf.c:888
main detection engine ctx
Definition: detect.h:761
const char * ConfNodeLookupChildValue(const ConfNode *node, const char *name)
Lookup the value of a child configuration node by name.
Definition: conf.c:843
void CreateIsoTimeString(const struct timeval *ts, char *str, size_t size)
Definition: util-time.c:187
struct tm * SCLocalTime(time_t timep, struct tm *result)
Definition: util-time.c:240
struct SCProfileDetectCtx_ SCProfileDetectCtx
void SCProfilingRuleThreadSetup(SCProfileDetectCtx *ctx, DetectEngineThreadCtx *det_ctx)
#define SCLogError(err_code,...)
Macro used to log ERROR messages.
Definition: util-debug.h:294
void SCProfilingRuleDestroyCtx(SCProfileDetectCtx *ctx)
struct SCProfileData_ SCProfileData
struct Signature_ * next
Definition: detect.h:594
uint32_t gid
Definition: detect.h:556
#define SET_ONE(x)
int ConfValIsTrue(const char *val)
Check if a value is true.
Definition: conf.c:566
Definition: conf.h:32
const char * ConfigGetLogDirectory()
Definition: util-conf.c:36
#define SCMalloc(a)
Definition: util-mem.h:222
DetectEngineCtx * de_ctx
Definition: detect.h:1127
#define JSON_ESCAPE_SLASH
struct SCProfileSummary_ SCProfileSummary
#define SCFree(a)
Definition: util-mem.h:322
#define SCLogPerf(...)
Definition: util-debug.h:261
void SCProfilingRuleUpdateCounter(DetectEngineThreadCtx *det_ctx, uint16_t id, uint64_t ticks, int match)
Update a rule counter.
uint32_t rev
Definition: detect.h:557
ConfNode * ConfGetNode(const char *name)
Get a ConfNode by name.
Definition: conf.c:176
struct SCProfileDetectCtx_ * profile_ctx
Definition: detect.h:882
void SCProfilingRuleInitCounters(DetectEngineCtx *de_ctx)
Register the rule profiling counters.
int profiling_output_to_file