suricata
util-profiling-prefilter.c
Go to the documentation of this file.
1 /* Copyright (C) 2007-2022 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
32 #include "util-conf.h"
33 #include "util-time.h"
34 
35 typedef struct SCProfilePrefilterData_ {
36  uint64_t called;
37  uint64_t total;
38  uint64_t max;
39  uint64_t total_bytes;
40  uint64_t max_bytes;
41  uint64_t bytes_called; /**< number of times total_bytes was updated. Differs from `called` as a
42  prefilter engine may skip mpm if the smallest pattern is bigger than
43  the buffer to inspect. */
44  const char *name;
46 
48  uint32_t id;
49  uint32_t size; /**< size in elements */
51  pthread_mutex_t data_m;
53 
54 static int profiling_prefilter_output_to_file = 0;
56 thread_local int profiling_prefilter_entered = 0;
57 static char profiling_file_name[PATH_MAX];
58 static const char *profiling_file_mode = "a";
59 
61 {
62  ConfNode *conf;
63 
64  conf = ConfGetNode("profiling.prefilter");
65  if (conf != NULL) {
66  if (ConfNodeChildValueIsTrue(conf, "enabled")) {
68  const char *filename = ConfNodeLookupChildValue(conf, "filename");
69  if (filename != NULL) {
70  const char *log_dir;
71  log_dir = ConfigGetLogDirectory();
72 
73  snprintf(profiling_file_name, sizeof(profiling_file_name), "%s/%s",
74  log_dir, filename);
75 
76  const char *v = ConfNodeLookupChildValue(conf, "append");
77  if (v == NULL || ConfValIsTrue(v)) {
78  profiling_file_mode = "a";
79  } else {
80  profiling_file_mode = "w";
81  }
82 
83  profiling_prefilter_output_to_file = 1;
84  }
85  }
86  }
87 }
88 
89 static void DoDump(SCProfilePrefilterDetectCtx *rules_ctx, FILE *fp, const char *name)
90 {
91  int i;
92  fprintf(fp, " ----------------------------------------------"
93  "------------------------------------------------------"
94  "----------------------------\n");
95  fprintf(fp, " Stats for: %s\n", name);
96  fprintf(fp, " ----------------------------------------------"
97  "------------------------------------------------------"
98  "----------------------------\n");
99  fprintf(fp, " %-32s %-15s %-15s %-15s %-15s %-15s %-15s %-15s %-15s %-15s\n", "Prefilter",
100  "Ticks", "Called", "Max Ticks", "Avg", "Bytes", "Called", "Max Bytes", "Avg Bytes",
101  "Ticks/Byte");
102  fprintf(fp, " -------------------------------- "
103  "--------------- "
104  "--------------- "
105  "--------------- "
106  "--------------- "
107  "--------------- "
108  "--------------- "
109  "--------------- "
110  "--------------- "
111  "--------------- "
112  "\n");
113  for (i = 0; i < (int)rules_ctx->size; i++) {
114  SCProfilePrefilterData *d = &rules_ctx->data[i];
115  if (d == NULL || d->called== 0)
116  continue;
117 
118  uint64_t ticks = d->total;
119  double avgticks = 0;
120  if (ticks && d->called) {
121  avgticks = (double)(ticks / d->called);
122  }
123  double avgbytes = 0;
124  if (d->total_bytes && d->bytes_called) {
125  avgbytes = (double)(d->total_bytes / d->bytes_called);
126  }
127  double ticks_per_byte = 0;
128  if (ticks && d->total_bytes) {
129  ticks_per_byte = (double)(ticks / d->total_bytes);
130  }
131 
132  fprintf(fp,
133  " %-32s %-15" PRIu64 " %-15" PRIu64 " %-15" PRIu64 " %-15.2f %-15" PRIu64
134  " %-15" PRIu64 " %-15" PRIu64 " %-15.2f %-15.2f\n",
135  d->name, ticks, d->called, d->max, avgticks, d->total_bytes, d->bytes_called,
136  d->max_bytes, avgbytes, ticks_per_byte);
137  }
138 }
139 
140 static void
141 SCProfilingPrefilterDump(DetectEngineCtx *de_ctx)
142 {
143  FILE *fp;
144  struct timeval tval;
145  struct tm *tms;
146  struct tm local_tm;
147 
149  return;
150 
151  gettimeofday(&tval, NULL);
152  tms = SCLocalTime(tval.tv_sec, &local_tm);
153 
154  if (profiling_prefilter_output_to_file == 1) {
155  SCLogDebug("file %s mode %s", profiling_file_name, profiling_file_mode);
156 
157  fp = fopen(profiling_file_name, profiling_file_mode);
158 
159  if (fp == NULL) {
160  SCLogError("failed to open %s: %s", profiling_file_name, strerror(errno));
161  return;
162  }
163  } else {
164  fp = stdout;
165  }
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  /* global stats first */
175  DoDump(de_ctx->profile_prefilter_ctx, fp, "total");
176 
177  fprintf(fp,"\n");
178  if (fp != stdout)
179  fclose(fp);
180 
181  SCLogPerf("Done dumping prefilter profiling data.");
182 }
183 
184 /**
185  * \brief Update a rule counter.
186  *
187  * \param id The ID of this counter.
188  * \param ticks Number of CPU ticks for this rule.
189  * \param match Did the rule match?
190  */
191 void SCProfilingPrefilterUpdateCounter(DetectEngineThreadCtx *det_ctx, int id, uint64_t ticks,
192  uint64_t bytes, uint64_t bytes_called)
193 {
194  if (det_ctx != NULL && det_ctx->prefilter_perf_data != NULL &&
195  id < (int)det_ctx->de_ctx->prefilter_id)
196  {
197  SCProfilePrefilterData *p = &det_ctx->prefilter_perf_data[id];
198 
199  p->called++;
200  if (ticks > p->max)
201  p->max = ticks;
202  p->total += ticks;
203 
204  p->bytes_called += bytes_called;
205  if (bytes > p->max_bytes)
206  p->max_bytes = bytes;
207  p->total_bytes += bytes;
208  }
209 }
210 
211 static SCProfilePrefilterDetectCtx *SCProfilingPrefilterInitCtx(void)
212 {
214  if (ctx != NULL) {
215  if (pthread_mutex_init(&ctx->data_m, NULL) != 0) {
216  FatalError("Failed to initialize hash table mutex.");
217  }
218  }
219 
220  return ctx;
221 }
222 
223 static void DetroyCtx(SCProfilePrefilterDetectCtx *ctx)
224 {
225  if (ctx) {
226  if (ctx->data != NULL)
227  SCFree(ctx->data);
228  pthread_mutex_destroy(&ctx->data_m);
229  SCFree(ctx);
230  }
231 }
232 
234 {
235  if (de_ctx != NULL) {
236  SCProfilingPrefilterDump(de_ctx);
237 
238  DetroyCtx(de_ctx->profile_prefilter_ctx);
239  }
240 }
241 
243 {
244  if (ctx == NULL)
245  return;
246 
247  const uint32_t size = det_ctx->de_ctx->prefilter_id;
248 
250  if (a != NULL) {
251  det_ctx->prefilter_perf_data = a;
252  }
253 }
254 
255 static void SCProfilingPrefilterThreadMerge(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx)
256 {
257  if (de_ctx == NULL || de_ctx->profile_prefilter_ctx == NULL ||
258  de_ctx->profile_prefilter_ctx->data == NULL || det_ctx == NULL ||
259  det_ctx->prefilter_perf_data == NULL)
260  return;
261 
262  for (uint32_t i = 0; i < de_ctx->prefilter_id; i++) {
268  det_ctx->prefilter_perf_data[i].total_bytes;
269  if (det_ctx->prefilter_perf_data[i].max_bytes >
272  det_ctx->prefilter_perf_data[i].max_bytes;
274  det_ctx->prefilter_perf_data[i].bytes_called;
275  }
276 }
277 
279 {
280  if (det_ctx == NULL || det_ctx->de_ctx == NULL || det_ctx->prefilter_perf_data == NULL)
281  return;
282 
283  pthread_mutex_lock(&det_ctx->de_ctx->profile_prefilter_ctx->data_m);
284  SCProfilingPrefilterThreadMerge(det_ctx->de_ctx, det_ctx);
285  pthread_mutex_unlock(&det_ctx->de_ctx->profile_prefilter_ctx->data_m);
286 
287  SCFree(det_ctx->prefilter_perf_data);
288  det_ctx->prefilter_perf_data = NULL;
289 }
290 
291 /**
292  * \brief Register the prefilter profiling counters.
293  *
294  * \param de_ctx The active DetectEngineCtx, used to get at the loaded rules.
295  */
296 void
298 {
300  return;
301 
302  const uint32_t size = de_ctx->prefilter_id;
303  if (size == 0)
304  return;
305 
306  de_ctx->profile_prefilter_ctx = SCProfilingPrefilterInitCtx();
309 
312 
314  for ( ; hb != NULL; hb = HashListTableGetListNext(hb)) {
316  de_ctx->profile_prefilter_ctx->data[ctx->id].name = ctx->name;
317  SCLogDebug("prefilter %s set up", de_ctx->profile_prefilter_ctx->data[ctx->id].name);
318  }
319  SCLogDebug("size alloc'd %u", (uint32_t)size * (uint32_t)sizeof(SCProfilePrefilterData));
320 
321  SCLogPerf("Registered %"PRIu32" prefilter profiling counters.", size);
322 }
323 
324 #endif /* PROFILING */
HashListTableGetListData
#define HashListTableGetListData(hb)
Definition: util-hashlist.h:57
SCProfilePrefilterDetectCtx_::size
uint32_t size
Definition: util-profiling-prefilter.c:49
profiling_prefilter_entered
thread_local int profiling_prefilter_entered
Definition: util-profiling-prefilter.c:56
ConfNodeChildValueIsTrue
int ConfNodeChildValueIsTrue(const ConfNode *node, const char *key)
Test if a configuration node has a true value.
Definition: conf.c:859
PrefilterStore_
Definition: detect-engine-prefilter.h:44
SCLogDebug
#define SCLogDebug(...)
Definition: util-debug.h:269
SCProfilePrefilterDetectCtx_::data
SCProfilePrefilterData * data
Definition: util-profiling-prefilter.c:50
SCProfilePrefilterDetectCtx_::id
uint32_t id
Definition: util-profiling-prefilter.c:48
ConfGetNode
ConfNode * ConfGetNode(const char *name)
Get a ConfNode by name.
Definition: conf.c:181
DetectEngineCtx_
main detection engine ctx
Definition: detect.h:836
SCProfilePrefilterDetectCtx_
Definition: util-profiling-prefilter.c:47
SCProfilePrefilterData_::name
const char * name
Definition: util-profiling-prefilter.c:44
HashListTableGetListHead
HashListTableBucket * HashListTableGetListHead(HashListTable *ht)
Definition: util-hashlist.c:295
SCProfilePrefilterDetectCtx
struct SCProfilePrefilterDetectCtx_ SCProfilePrefilterDetectCtx
detect-engine-prefilter.h
ConfValIsTrue
int ConfValIsTrue(const char *val)
Check if a value is true.
Definition: conf.c:537
PrefilterStore_::id
uint32_t id
Definition: detect-engine-prefilter.h:47
DetectEngineCtx_::prefilter_id
uint32_t prefilter_id
Definition: detect.h:1003
profiling_prefilter_enabled
int profiling_prefilter_enabled
Definition: util-profiling-prefilter.c:55
HashListTableGetListNext
#define HashListTableGetListNext(hb)
Definition: util-hashlist.h:56
DetectEngineCtx_::prefilter_hash_table
HashListTable * prefilter_hash_table
Definition: detect.h:1004
SCProfilePrefilterData_::total
uint64_t total
Definition: util-profiling-prefilter.c:37
de_ctx
DetectEngineCtx * de_ctx
Definition: fuzz_siginit.c:17
SCProfilingPrefilterDestroyCtx
void SCProfilingPrefilterDestroyCtx(DetectEngineCtx *de_ctx)
Definition: util-profiling-prefilter.c:233
DetectEngineThreadCtx_
Definition: detect.h:1092
SCProfilingPrefilterInitCounters
void SCProfilingPrefilterInitCounters(DetectEngineCtx *de_ctx)
Register the prefilter profiling counters.
Definition: util-profiling-prefilter.c:297
util-time.h
SCProfilingPrefilterGlobalInit
void SCProfilingPrefilterGlobalInit(void)
Definition: util-profiling-prefilter.c:60
BUG_ON
#define BUG_ON(x)
Definition: suricata-common.h:300
util-profiling.h
SCProfilePrefilterData_::max_bytes
uint64_t max_bytes
Definition: util-profiling-prefilter.c:40
SCLocalTime
struct tm * SCLocalTime(time_t timep, struct tm *result)
Definition: util-time.c:267
SCProfilingPrefilterThreadCleanup
void SCProfilingPrefilterThreadCleanup(DetectEngineThreadCtx *det_ctx)
Definition: util-profiling-prefilter.c:278
SCProfilePrefilterData_::total_bytes
uint64_t total_bytes
Definition: util-profiling-prefilter.c:39
util-conf.h
DetectEngineCtx_::profile_prefilter_ctx
struct SCProfilePrefilterDetectCtx_ * profile_prefilter_ctx
Definition: detect.h:954
suricata-common.h
SCProfilingPrefilterUpdateCounter
void SCProfilingPrefilterUpdateCounter(DetectEngineThreadCtx *det_ctx, int id, uint64_t ticks, uint64_t bytes, uint64_t bytes_called)
Update a rule counter.
Definition: util-profiling-prefilter.c:191
DetectEngineThreadCtx_::prefilter_perf_data
struct SCProfilePrefilterData_ * prefilter_perf_data
Definition: detect.h:1249
SCLogPerf
#define SCLogPerf(...)
Definition: util-debug.h:230
FatalError
#define FatalError(...)
Definition: util-debug.h:502
SCProfilePrefilterData_::max
uint64_t max
Definition: util-profiling-prefilter.c:38
ConfigGetLogDirectory
const char * ConfigGetLogDirectory(void)
Definition: util-conf.c:38
SCLogError
#define SCLogError(...)
Macro used to log ERROR messages.
Definition: util-debug.h:261
SCProfilePrefilterData_::called
uint64_t called
Definition: util-profiling-prefilter.c:36
PrefilterStore_::name
const char * name
Definition: detect-engine-prefilter.h:45
SCFree
#define SCFree(p)
Definition: util-mem.h:61
ConfNode_
Definition: conf.h:32
HashListTableBucket_
Definition: util-hashlist.h:28
SCProfilePrefilterDetectCtx_::data_m
pthread_mutex_t data_m
Definition: util-profiling-prefilter.c:51
SCProfilePrefilterData_
Definition: util-profiling-prefilter.c:35
DetectEngineThreadCtx_::de_ctx
DetectEngineCtx * de_ctx
Definition: detect.h:1216
SCProfilePrefilterData
struct SCProfilePrefilterData_ SCProfilePrefilterData
SCCalloc
#define SCCalloc(nm, sz)
Definition: util-mem.h:53
SCProfilePrefilterData_::bytes_called
uint64_t bytes_called
Definition: util-profiling-prefilter.c:41
SCProfilingPrefilterThreadSetup
void SCProfilingPrefilterThreadSetup(SCProfilePrefilterDetectCtx *ctx, DetectEngineThreadCtx *det_ctx)
Definition: util-profiling-prefilter.c:242
ConfNodeLookupChildValue
const char * ConfNodeLookupChildValue(const ConfNode *node, const char *name)
Lookup the value of a child configuration node by name.
Definition: conf.c:814