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