suricata
util-profiling-rulegroups.c
Go to the documentation of this file.
1 /* Copyright (C) 2007-2015 Open Information Security Foundation
2  *
3  * You can copy, redistribute or modify this Program under the terms of
4  * the GNU General Public License version 2 as published by the Free
5  * Software Foundation.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * version 2 along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
15  * 02110-1301, USA.
16  */
17 
18 /**
19  * \file
20  *
21  * \author 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 "util-profiling.h"
29 
30 #ifdef PROFILING
31 #include "util-conf.h"
32 #include "util-path.h"
33 #include "util-time.h"
34 
35 /**
36  * Extra data for rule profiling.
37  */
38 typedef struct SCProfileSghData_ {
39  uint64_t checks;
40 
41  uint64_t non_mpm_generic;
42  uint64_t non_mpm_syn;
43 
46 
49 
51 
52 typedef struct SCProfileSghDetectCtx_ {
53  uint32_t cnt;
55  pthread_mutex_t data_m;
57 
58 static int profiling_sghs_output_to_file = 0;
60 static char profiling_file_name[PATH_MAX];
61 static const char *profiling_file_mode = "a";
62 static int profiling_rulegroup_json = 0;
63 
65 {
66  ConfNode *conf;
67 
68  conf = ConfGetNode("profiling.rulegroups");
69  if (conf != NULL) {
70  if (ConfNodeChildValueIsTrue(conf, "enabled")) {
72  const char *filename = ConfNodeLookupChildValue(conf, "filename");
73  if (filename != NULL) {
74  if (PathIsAbsolute(filename)) {
75  strlcpy(profiling_file_name, filename, sizeof(profiling_file_name));
76  } else {
77  const char *log_dir = ConfigGetLogDirectory();
78  snprintf(profiling_file_name, sizeof(profiling_file_name), "%s/%s", log_dir,
79  filename);
80  }
81 
82  const char *v = ConfNodeLookupChildValue(conf, "append");
83  if (v == NULL || ConfValIsTrue(v)) {
84  profiling_file_mode = "a";
85  } else {
86  profiling_file_mode = "w";
87  }
88 
89  profiling_sghs_output_to_file = 1;
90  }
91  if (ConfNodeChildValueIsTrue(conf, "json")) {
92  profiling_rulegroup_json = 1;
93  }
94  }
95  }
96 }
97 
98 static void DoDumpJSON(SCProfileSghDetectCtx *rules_ctx, FILE *fp, const char *name)
99 {
100  char timebuf[64];
101  uint32_t i;
102  struct timeval tval;
103 
104  json_t *js = json_object();
105  if (js == NULL)
106  return;
107  json_t *jsa = json_array();
108  if (jsa == NULL) {
109  json_decref(js);
110  return;
111  }
112 
113  gettimeofday(&tval, NULL);
114  CreateIsoTimeString(SCTIME_FROM_TIMEVAL(&tval), timebuf, sizeof(timebuf));
115  json_object_set_new(js, "timestamp", json_string(timebuf));
116 
117  for (i = 0; i < rules_ctx->cnt; i++) {
118  SCProfileSghData *d = &rules_ctx->data[i];
119  if (d == NULL || d->checks == 0)
120  continue;
121 
122  double avgsigs = 0;
123  double avgmpms = 0;
124 
125  if (d->post_prefilter_sigs_total && d->checks) {
126  avgsigs = (double)((double)d->post_prefilter_sigs_total / (double)d->checks);
127  }
128  if (d->mpm_match_cnt_total && d->checks) {
129  avgmpms = (double)((double)d->mpm_match_cnt_total / (double)d->checks);
130  }
131 
132  json_t *jsm = json_object();
133  if (jsm) {
134  json_object_set_new(jsm, "id", json_integer(i));
135  json_object_set_new(jsm, "checks", json_integer(d->checks));
136  json_object_set_new(jsm, "non_mpm_generic", json_integer(d->non_mpm_generic));
137  json_object_set_new(jsm, "non_mpm_syn", json_integer(d->non_mpm_syn));
138  json_object_set_new(jsm, "avgmpms", json_real(avgmpms));
139  json_object_set_new(jsm, "mpm_match_cnt_max", json_integer(d->mpm_match_cnt_max));
140  json_object_set_new(jsm, "avgsigs", json_real(avgsigs));
141  json_object_set_new(jsm, "post_prefilter_sigs_max", json_integer(d->post_prefilter_sigs_max));
142  json_array_append_new(jsa, jsm);
143  }
144  }
145  json_object_set_new(js, "rule_groups", jsa);
146 
147  char *js_s = json_dumps(js,
148  JSON_PRESERVE_ORDER|JSON_COMPACT|JSON_ENSURE_ASCII|
150  if (likely(js_s != NULL)) {
151  fprintf(fp, "%s", js_s);
152  free(js_s);
153  }
154  json_decref(js);
155 }
156 
157 static void DoDump(SCProfileSghDetectCtx *rules_ctx, FILE *fp, const char *name)
158 {
159  uint32_t i;
160  struct timeval tval;
161  struct tm *tms;
162  struct tm local_tm;
163 
164  gettimeofday(&tval, NULL);
165  tms = SCLocalTime(tval.tv_sec, &local_tm);
166 
167  fprintf(fp, " ----------------------------------------------"
168  "------------------------------------------------------"
169  "----------------------------\n");
170  fprintf(fp, " Date: %" PRId32 "/%" PRId32 "/%04d -- "
171  "%02d:%02d:%02d\n", tms->tm_mon + 1, tms->tm_mday, tms->tm_year + 1900,
172  tms->tm_hour,tms->tm_min, tms->tm_sec);
173 
174  fprintf(fp, " ----------------------------------------------"
175  "------------------------------------------------------"
176  "----------------------------\n");
177  fprintf(fp, " Stats for: %s %u\n", name, rules_ctx->cnt);
178  fprintf(fp, " ----------------------------------------------"
179  "------------------------------------------------------"
180  "----------------------------\n");
181  fprintf(fp, " %-16s %-15s %-15s %-15s %-15s %-15s %-15s %-15s\n", "Sgh", "Checks", "Non-MPM(gen)", "Non-Mpm(syn)", "MPM Matches", "MPM Match Max", "Post-Filter", "Post-Filter Max");
182  fprintf(fp, " ---------------- "
183  "--------------- "
184  "--------------- "
185  "--------------- "
186  "--------------- "
187  "--------------- "
188  "--------------- "
189  "--------------- "
190  "\n");
191  for (i = 0; i < rules_ctx->cnt; i++) {
192  SCProfileSghData *d = &rules_ctx->data[i];
193  if (d == NULL || d->checks == 0)
194  continue;
195 
196  double avgsigs = 0;
197  double avgmpms = 0;
198 
199  if (d->post_prefilter_sigs_total && d->checks) {
200  avgsigs = (double)((double)d->post_prefilter_sigs_total / (double)d->checks);
201  }
202  if (d->mpm_match_cnt_total && d->checks) {
203  avgmpms = (double)((double)d->mpm_match_cnt_total / (double)d->checks);
204  }
205 
206  fprintf(fp,
207  " %-16u %-15"PRIu64" %-15"PRIu64" %-15"PRIu64" %-15.2f %-15"PRIu64" %-15.2f %-15"PRIu64"\n",
208  i,
209  d->checks,
210  d->non_mpm_generic,
211  d->non_mpm_syn,
212  avgmpms,
214  avgsigs,
216  }
217  fprintf(fp,"\n");
218 }
219 
220 static void
221 SCProfilingSghDump(DetectEngineCtx *de_ctx)
222 {
223  FILE *fp;
224 
225  if (profiling_sghs_enabled == 0)
226  return;
227 
228  if (profiling_sghs_output_to_file == 1) {
229  SCLogDebug("file %s mode %s", profiling_file_name, profiling_file_mode);
230 
231  fp = fopen(profiling_file_name, profiling_file_mode);
232 
233  if (fp == NULL) {
234  SCLogError("failed to open %s: %s", profiling_file_name, strerror(errno));
235  return;
236  }
237  } else {
238  fp = stdout;
239  }
240 
241  if (profiling_rulegroup_json) {
242  DoDumpJSON(de_ctx->profile_sgh_ctx, fp, "rule groups");
243  } else {
244  DoDump(de_ctx->profile_sgh_ctx, fp, "rule groups");
245  }
246 
247  if (fp != stdout)
248  fclose(fp);
249 
250  SCLogPerf("Done dumping rulegroup profiling data.");
251 }
252 
253 /**
254  * \brief Update a rule counter.
255  *
256  * \param id The ID of this counter.
257  * \param ticks Number of CPU ticks for this rule.
258  * \param match Did the rule match?
259  */
260 void
262 {
263  if (det_ctx != NULL && det_ctx->sgh_perf_data != NULL && sgh->id < det_ctx->de_ctx->sgh_array_cnt) {
264  SCProfileSghData *p = &det_ctx->sgh_perf_data[sgh->id];
265  p->checks++;
266 
267  if (det_ctx->non_pf_store_cnt > 0) {
268  if (det_ctx->non_pf_store_ptr == sgh->non_pf_syn_store_array)
269  p->non_mpm_syn++;
270  else
271  p->non_mpm_generic++;
272  }
274  if (det_ctx->match_array_cnt > p->post_prefilter_sigs_max)
277  if (det_ctx->pmq.rule_id_array_cnt > p->mpm_match_cnt_max)
279  }
280 }
281 
282 static SCProfileSghDetectCtx *SCProfilingSghInitCtx(void)
283 {
285  if (ctx != NULL) {
286  if (pthread_mutex_init(&ctx->data_m, NULL) != 0) {
287  FatalError("Failed to initialize mutex.");
288  }
289  }
290 
291  return ctx;
292 }
293 
294 static void DetroyCtx(SCProfileSghDetectCtx *ctx)
295 {
296  if (ctx) {
297  if (ctx->data != NULL)
298  SCFree(ctx->data);
299  pthread_mutex_destroy(&ctx->data_m);
300  SCFree(ctx);
301  }
302 }
303 
305 {
306  if (de_ctx != NULL) {
307  SCProfilingSghDump(de_ctx);
308 
309  DetroyCtx(de_ctx->profile_sgh_ctx);
310  }
311 }
312 
314 {
315  if (ctx == NULL)
316  return;
317 
318  uint32_t array_size = det_ctx->de_ctx->sgh_array_cnt;
319 
320  SCProfileSghData *a = SCCalloc(array_size, sizeof(SCProfileSghData));
321  if (a != NULL) {
322  det_ctx->sgh_perf_data = a;
323  }
324 }
325 
326 static void SCProfilingSghThreadMerge(DetectEngineCtx *de_ctx, const DetectEngineThreadCtx *det_ctx)
327 {
328  if (de_ctx == NULL || de_ctx->profile_sgh_ctx == NULL ||
329  de_ctx->profile_sgh_ctx->data == NULL || det_ctx == NULL ||
330  det_ctx->sgh_perf_data == NULL)
331  return;
332 
333 #define ADD(name) de_ctx->profile_sgh_ctx->data[i].name += det_ctx->sgh_perf_data[i].name
334  uint32_t i;
335  for (i = 0; i < de_ctx->sgh_array_cnt; i++) {
336  ADD(checks);
337  ADD(non_mpm_generic);
338  ADD(non_mpm_syn);
339  ADD(post_prefilter_sigs_total);
340  ADD(mpm_match_cnt_total);
341 
346  }
347 #undef ADD
348 }
349 
351 {
352  if (det_ctx == NULL || det_ctx->de_ctx == NULL || det_ctx->sgh_perf_data == NULL)
353  return;
354 
355  pthread_mutex_lock(&det_ctx->de_ctx->profile_sgh_ctx->data_m);
356  SCProfilingSghThreadMerge(det_ctx->de_ctx, det_ctx);
357  pthread_mutex_unlock(&det_ctx->de_ctx->profile_sgh_ctx->data_m);
358 
359  SCFree(det_ctx->sgh_perf_data);
360  det_ctx->sgh_perf_data = NULL;
361 }
362 
363 /**
364  * \brief Register the keyword profiling counters.
365  *
366  * \param de_ctx The active DetectEngineCtx, used to get at the loaded rules.
367  */
368 void
370 {
371  if (profiling_sghs_enabled == 0)
372  return;
373 
374  de_ctx->profile_sgh_ctx = SCProfilingSghInitCtx();
375  BUG_ON(de_ctx->profile_sgh_ctx == NULL);
376 
378  BUG_ON(de_ctx->profile_sgh_ctx->data == NULL);
379 
381 
382  SCLogPerf("Registered %"PRIu32" rulegroup profiling counters.", de_ctx->sgh_array_cnt);
383 }
384 
385 #endif /* PROFILING */
SCProfilingSghThreadCleanup
void SCProfilingSghThreadCleanup(DetectEngineThreadCtx *det_ctx)
Definition: util-profiling-rulegroups.c:350
DetectEngineThreadCtx_::non_pf_store_ptr
SignatureNonPrefilterStore * non_pf_store_ptr
Definition: detect.h:1196
ConfNodeChildValueIsTrue
int ConfNodeChildValueIsTrue(const ConfNode *node, const char *key)
Test if a configuration node has a true value.
Definition: conf.c:854
DetectEngineThreadCtx_::match_array_cnt
SigIntId match_array_cnt
Definition: detect.h:1191
SCProfilingSghThreadSetup
void SCProfilingSghThreadSetup(SCProfileSghDetectCtx *ctx, DetectEngineThreadCtx *det_ctx)
Definition: util-profiling-rulegroups.c:313
CreateIsoTimeString
void CreateIsoTimeString(const SCTime_t ts, char *str, size_t size)
Definition: util-time.c:209
PrefilterRuleStore_::rule_id_array_cnt
uint32_t rule_id_array_cnt
Definition: util-prefilter.h:40
SigGroupHead_
Container for matching data for a signature group.
Definition: detect.h:1460
DetectEngineThreadCtx_::sgh_perf_data
struct SCProfileSghData_ * sgh_perf_data
Definition: detect.h:1258
SCProfileSghDetectCtx_
Definition: util-profiling-rulegroups.c:52
SCProfileSghDetectCtx_::data_m
pthread_mutex_t data_m
Definition: util-profiling-rulegroups.c:55
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:269
ConfGetNode
ConfNode * ConfGetNode(const char *name)
Get a ConfNode by name.
Definition: conf.c:181
DetectEngineThreadCtx_::pmq
PrefilterRuleStore pmq
Definition: detect.h:1200
SCProfilingSghInitCounters
void SCProfilingSghInitCounters(DetectEngineCtx *de_ctx)
Register the keyword profiling counters.
Definition: util-profiling-rulegroups.c:369
ctx
struct Thresholds ctx
DetectEngineCtx_
main detection engine ctx
Definition: detect.h:841
DetectEngineCtx_::profile_sgh_ctx
struct SCProfileSghDetectCtx_ * profile_sgh_ctx
Definition: detect.h:957
JSON_ESCAPE_SLASH
#define JSON_ESCAPE_SLASH
Definition: suricata-common.h:277
SCProfileSghData_::mpm_match_cnt_total
uint64_t mpm_match_cnt_total
Definition: util-profiling-rulegroups.c:47
ConfValIsTrue
int ConfValIsTrue(const char *val)
Check if a value is true.
Definition: conf.c:536
SCProfileSghData_::checks
uint64_t checks
Definition: util-profiling-rulegroups.c:39
strlcpy
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition: util-strlcpyu.c:43
SCProfileSghData_::post_prefilter_sigs_max
uint64_t post_prefilter_sigs_max
Definition: util-profiling-rulegroups.c:45
de_ctx
DetectEngineCtx * de_ctx
Definition: fuzz_siginit.c:17
profiling_sghs_enabled
int profiling_sghs_enabled
Definition: util-profiling-rulegroups.c:59
DetectEngineThreadCtx_
Definition: detect.h:1093
SCProfileSghData_
Definition: util-profiling-rulegroups.c:38
SCTIME_FROM_TIMEVAL
#define SCTIME_FROM_TIMEVAL(tv)
Definition: util-time.h:79
util-time.h
BUG_ON
#define BUG_ON(x)
Definition: suricata-common.h:300
util-profiling.h
SCLocalTime
struct tm * SCLocalTime(time_t timep, struct tm *result)
Definition: util-time.c:267
DetectEngineCtx_::sgh_array_cnt
uint32_t sgh_array_cnt
Definition: detect.h:910
SCProfileSghData
struct SCProfileSghData_ SCProfileSghData
SCProfileSghData_::non_mpm_generic
uint64_t non_mpm_generic
Definition: util-profiling-rulegroups.c:41
util-conf.h
suricata-common.h
util-path.h
SCProfileSghDetectCtx
struct SCProfileSghDetectCtx_ SCProfileSghDetectCtx
SCLogPerf
#define SCLogPerf(...)
Definition: util-debug.h:230
PathIsAbsolute
int PathIsAbsolute(const char *path)
Check if a path is absolute.
Definition: util-path.c:44
FatalError
#define FatalError(...)
Definition: util-debug.h:502
ConfigGetLogDirectory
const char * ConfigGetLogDirectory(void)
Definition: util-conf.c:38
SCLogError
#define SCLogError(...)
Macro used to log ERROR messages.
Definition: util-debug.h:261
DetectEngineThreadCtx_::non_pf_store_cnt
uint32_t non_pf_store_cnt
Definition: detect.h:1197
SCFree
#define SCFree(p)
Definition: util-mem.h:61
ADD
#define ADD(name)
ConfNode_
Definition: conf.h:32
SCProfilingSghDestroyCtx
void SCProfilingSghDestroyCtx(DetectEngineCtx *de_ctx)
Definition: util-profiling-rulegroups.c:304
SCProfileSghData_::mpm_match_cnt_max
uint64_t mpm_match_cnt_max
Definition: util-profiling-rulegroups.c:48
DetectEngineThreadCtx_::de_ctx
DetectEngineCtx * de_ctx
Definition: detect.h:1215
SCProfileSghDetectCtx_::cnt
uint32_t cnt
Definition: util-profiling-rulegroups.c:53
SigGroupHead_::non_pf_syn_store_array
SignatureNonPrefilterStore * non_pf_syn_store_array
Definition: detect.h:1475
SCProfileSghData_::non_mpm_syn
uint64_t non_mpm_syn
Definition: util-profiling-rulegroups.c:42
likely
#define likely(expr)
Definition: util-optimize.h:32
SCProfilingSghUpdateCounter
void SCProfilingSghUpdateCounter(DetectEngineThreadCtx *det_ctx, const SigGroupHead *sgh)
Update a rule counter.
Definition: util-profiling-rulegroups.c:261
SCProfilingSghsGlobalInit
void SCProfilingSghsGlobalInit(void)
Definition: util-profiling-rulegroups.c:64
SCCalloc
#define SCCalloc(nm, sz)
Definition: util-mem.h:53
SigGroupHead_::id
uint32_t id
Definition: detect.h:1468
SCProfileSghDetectCtx_::data
SCProfileSghData * data
Definition: util-profiling-rulegroups.c:54
SCProfileSghData_::post_prefilter_sigs_total
uint64_t post_prefilter_sigs_total
Definition: util-profiling-rulegroups.c:44
ConfNodeLookupChildValue
const char * ConfNodeLookupChildValue(const ConfNode *node, const char *name)
Lookup the value of a child configuration node by name.
Definition: conf.c:809