suricata
util-profiling-prefilter.c
Go to the documentation of this file.
1 /* Copyright (C) 2007-2017 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 "detect-engine.h"
32 #include "conf.h"
33 
34 #include "tm-threads.h"
35 
36 #include "util-unittest.h"
37 #include "util-byte.h"
38 #include "util-profiling.h"
39 #include "util-profiling-locks.h"
40 
41 #ifdef PROFILING
42 
43 typedef struct SCProfilePrefilterData_ {
44  uint64_t called;
45  uint64_t total;
46  uint64_t max;
47  const char *name;
49 
51  uint32_t id;
52  uint32_t size; /**< size in elements */
54  pthread_mutex_t data_m;
56 
57 static int profiling_prefilter_output_to_file = 0;
59 __thread int profiling_prefilter_entered = 0;
60 static char profiling_file_name[PATH_MAX];
61 static const char *profiling_file_mode = "a";
62 
64 {
65  ConfNode *conf;
66 
67  conf = ConfGetNode("profiling.prefilter");
68  if (conf != NULL) {
69  if (ConfNodeChildValueIsTrue(conf, "enabled")) {
71  const char *filename = ConfNodeLookupChildValue(conf, "filename");
72  if (filename != NULL) {
73  const char *log_dir;
74  log_dir = ConfigGetLogDirectory();
75 
76  snprintf(profiling_file_name, sizeof(profiling_file_name), "%s/%s",
77  log_dir, filename);
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\n", "Prefilter", "Ticks", "Called", "Max Ticks", "Avg");
103  fprintf(fp, " -------------------------------- "
104  "--------------- "
105  "--------------- "
106  "--------------- "
107  "--------------- "
108  "\n");
109  for (i = 0; i < (int)rules_ctx->size; i++) {
110  SCProfilePrefilterData *d = &rules_ctx->data[i];
111  if (d == NULL || d->called== 0)
112  continue;
113 
114  uint64_t ticks = d->total;
115  double avgticks = 0;
116  if (ticks && d->called) {
117  avgticks = (ticks / d->called);
118  }
119 
120  fprintf(fp,
121  " %-32s %-15"PRIu64" %-15"PRIu64" %-15"PRIu64" %-15.2f\n",
122  d->name,
123  ticks,
124  d->called,
125  d->max,
126  avgticks);
127  }
128 }
129 
130 static void
131 SCProfilingPrefilterDump(DetectEngineCtx *de_ctx)
132 {
133  FILE *fp;
134  struct timeval tval;
135  struct tm *tms;
136  struct tm local_tm;
137 
138  if (profiling_prefilter_enabled == 0 || de_ctx->profile_prefilter_ctx == NULL)
139  return;
140 
141  gettimeofday(&tval, NULL);
142  tms = SCLocalTime(tval.tv_sec, &local_tm);
143 
144  if (profiling_prefilter_output_to_file == 1) {
145  SCLogDebug("file %s mode %s", profiling_file_name, profiling_file_mode);
146 
147  fp = fopen(profiling_file_name, profiling_file_mode);
148 
149  if (fp == NULL) {
150  SCLogError(SC_ERR_FOPEN, "failed to open %s: %s", profiling_file_name,
151  strerror(errno));
152  return;
153  }
154  } else {
155  fp = stdout;
156  }
157 
158  fprintf(fp, " ----------------------------------------------"
159  "------------------------------------------------------"
160  "----------------------------\n");
161  fprintf(fp, " Date: %" PRId32 "/%" PRId32 "/%04d -- "
162  "%02d:%02d:%02d\n", tms->tm_mon + 1, tms->tm_mday, tms->tm_year + 1900,
163  tms->tm_hour,tms->tm_min, tms->tm_sec);
164 
165  /* global stats first */
166  DoDump(de_ctx->profile_prefilter_ctx, fp, "total");
167 
168  fprintf(fp,"\n");
169  if (fp != stdout)
170  fclose(fp);
171 
172  SCLogPerf("Done dumping prefilter profiling data.");
173 }
174 
175 /**
176  * \brief Update a rule counter.
177  *
178  * \param id The ID of this counter.
179  * \param ticks Number of CPU ticks for this rule.
180  * \param match Did the rule match?
181  */
182 void
184 {
185  if (det_ctx != NULL && det_ctx->prefilter_perf_data != NULL &&
186  id < (int)det_ctx->de_ctx->prefilter_id)
187  {
188  SCProfilePrefilterData *p = &det_ctx->prefilter_perf_data[id];
189 
190  p->called++;
191  if (ticks > p->max)
192  p->max = ticks;
193  p->total += ticks;
194  }
195 }
196 
197 static SCProfilePrefilterDetectCtx *SCProfilingPrefilterInitCtx(void)
198 {
200  if (ctx != NULL) {
201  memset(ctx, 0x00, sizeof(SCProfilePrefilterDetectCtx));
202 
203  if (pthread_mutex_init(&ctx->data_m, NULL) != 0) {
205  "Failed to initialize hash table mutex.");
206  exit(EXIT_FAILURE);
207  }
208  }
209 
210  return ctx;
211 }
212 
213 static void DetroyCtx(SCProfilePrefilterDetectCtx *ctx)
214 {
215  if (ctx) {
216  if (ctx->data != NULL)
217  SCFree(ctx->data);
218  pthread_mutex_destroy(&ctx->data_m);
219  SCFree(ctx);
220  }
221 }
222 
224 {
225  if (de_ctx != NULL) {
226  SCProfilingPrefilterDump(de_ctx);
227 
228  DetroyCtx(de_ctx->profile_prefilter_ctx);
229  }
230 }
231 
233 {
234  if (ctx == NULL)
235  return;
236 
237  const uint32_t size = det_ctx->de_ctx->prefilter_id;
238 
240  if (a != NULL) {
241  memset(a, 0x00, sizeof(SCProfilePrefilterData) * size);
242  det_ctx->prefilter_perf_data = a;
243  }
244 }
245 
246 static void SCProfilingPrefilterThreadMerge(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx)
247 {
248  if (de_ctx == NULL || de_ctx->profile_prefilter_ctx == NULL ||
249  de_ctx->profile_prefilter_ctx->data == NULL || det_ctx == NULL ||
250  det_ctx->prefilter_perf_data == NULL)
251  return;
252 
253  for (uint32_t i = 0; i < de_ctx->prefilter_id; i++) {
254  de_ctx->profile_prefilter_ctx->data[i].called += det_ctx->prefilter_perf_data[i].called;
255  de_ctx->profile_prefilter_ctx->data[i].total += det_ctx->prefilter_perf_data[i].total;
256  if (det_ctx->prefilter_perf_data[i].max > de_ctx->profile_prefilter_ctx->data[i].max)
257  de_ctx->profile_prefilter_ctx->data[i].max = det_ctx->prefilter_perf_data[i].max;
258  }
259 }
260 
262 {
263  if (det_ctx == NULL || det_ctx->de_ctx == NULL || det_ctx->prefilter_perf_data == NULL)
264  return;
265 
266  pthread_mutex_lock(&det_ctx->de_ctx->profile_prefilter_ctx->data_m);
267  SCProfilingPrefilterThreadMerge(det_ctx->de_ctx, det_ctx);
268  pthread_mutex_unlock(&det_ctx->de_ctx->profile_prefilter_ctx->data_m);
269 
270  SCFree(det_ctx->prefilter_perf_data);
271  det_ctx->prefilter_perf_data = NULL;
272 }
273 
274 /**
275  * \brief Register the prefilter profiling counters.
276  *
277  * \param de_ctx The active DetectEngineCtx, used to get at the loaded rules.
278  */
279 void
281 {
283  return;
284 
285  const uint32_t size = de_ctx->prefilter_id;
286  if (size == 0)
287  return;
288 
289  de_ctx->profile_prefilter_ctx = SCProfilingPrefilterInitCtx();
290  BUG_ON(de_ctx->profile_prefilter_ctx == NULL);
291  de_ctx->profile_prefilter_ctx->size = size;
292 
293  de_ctx->profile_prefilter_ctx->data = SCMalloc(sizeof(SCProfilePrefilterData) * size);
294  BUG_ON(de_ctx->profile_prefilter_ctx->data == NULL);
295  memset(de_ctx->profile_prefilter_ctx->data, 0x00, sizeof(SCProfilePrefilterData) * size);
296 
298  for ( ; hb != NULL; hb = HashListTableGetListNext(hb)) {
300  de_ctx->profile_prefilter_ctx->data[ctx->id].name = ctx->name;
301  SCLogDebug("prefilter %s set up", de_ctx->profile_prefilter_ctx->data[ctx->id].name);
302  }
303  SCLogDebug("size alloc'd %u", (uint32_t)size * (uint32_t)sizeof(SCProfilePrefilterData));
304 
305  SCLogPerf("Registered %"PRIu32" prefilter profiling counters.", size);
306 }
307 
308 #endif /* PROFILING */
#define SCLogDebug(...)
Definition: util-debug.h:335
void SCProfilingPrefilterDestroyCtx(DetectEngineCtx *de_ctx)
HashListTableBucket * HashListTableGetListHead(HashListTable *ht)
struct SCProfilePrefilterData_ SCProfilePrefilterData
int profiling_prefilter_enabled
#define BUG_ON(x)
__thread int profiling_prefilter_entered
#define HashListTableGetListData(hb)
Definition: util-hashlist.h:59
void SCProfilingPrefilterUpdateCounter(DetectEngineThreadCtx *det_ctx, int id, uint64_t ticks)
Update a rule counter.
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:756
const char * ConfNodeLookupChildValue(const ConfNode *node, const char *name)
Lookup the value of a child configuration node by name.
Definition: conf.c:843
#define HashListTableGetListNext(hb)
Definition: util-hashlist.h:58
struct tm * SCLocalTime(time_t timep, struct tm *result)
Definition: util-time.c:240
#define SCLogError(err_code,...)
Macro used to log ERROR messages.
Definition: util-debug.h:294
void SCProfilingPrefilterThreadCleanup(DetectEngineThreadCtx *det_ctx)
void SCProfilingPrefilterGlobalInit(void)
struct SCProfilePrefilterData_ * prefilter_perf_data
Definition: detect.h:1154
uint32_t prefilter_id
Definition: detect.h:928
struct SCProfilePrefilterDetectCtx_ * profile_prefilter_ctx
Definition: detect.h:878
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
struct SCProfilePrefilterDetectCtx_ SCProfilePrefilterDetectCtx
#define SCMalloc(a)
Definition: util-mem.h:222
DetectEngineCtx * de_ctx
Definition: detect.h:1120
#define SCFree(a)
Definition: util-mem.h:322
#define SCLogPerf(...)
Definition: util-debug.h:261
HashListTable * prefilter_hash_table
Definition: detect.h:929
ConfNode * ConfGetNode(const char *name)
Get a ConfNode by name.
Definition: conf.c:176
void SCProfilingPrefilterInitCounters(DetectEngineCtx *de_ctx)
Register the prefilter profiling counters.
void SCProfilingPrefilterThreadSetup(SCProfilePrefilterDetectCtx *ctx, DetectEngineThreadCtx *det_ctx)