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"
31 #include "detect-engine-prefilter.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;
48 } SCProfilePrefilterData;
49
50 typedef struct SCProfilePrefilterDetectCtx_ {
51 uint32_t id;
52 uint32_t size; /**< size in elements */
53 SCProfilePrefilterData *data;
54 pthread_mutex_t data_m;
55 } SCProfilePrefilterDetectCtx;
56
57 static int profiling_prefilter_output_to_file = 0;
58 int profiling_prefilter_enabled = 0;
59 thread_local int profiling_prefilter_entered = 0;
60 static char profiling_file_name[PATH_MAX];
61 static const char *profiling_file_mode = "a";
62
SCProfilingPrefilterGlobalInit(void)63 void SCProfilingPrefilterGlobalInit(void)
64 {
65 ConfNode *conf;
66
67 conf = ConfGetNode("profiling.prefilter");
68 if (conf != NULL) {
69 if (ConfNodeChildValueIsTrue(conf, "enabled")) {
70 profiling_prefilter_enabled = 1;
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
DoDump(SCProfilePrefilterDetectCtx * rules_ctx,FILE * fp,const char * name)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
SCProfilingPrefilterDump(DetectEngineCtx * de_ctx)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
SCProfilingPrefilterUpdateCounter(DetectEngineThreadCtx * det_ctx,int id,uint64_t ticks)183 SCProfilingPrefilterUpdateCounter(DetectEngineThreadCtx *det_ctx, int id, uint64_t ticks)
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
SCProfilingPrefilterInitCtx(void)197 static SCProfilePrefilterDetectCtx *SCProfilingPrefilterInitCtx(void)
198 {
199 SCProfilePrefilterDetectCtx *ctx = SCMalloc(sizeof(SCProfilePrefilterDetectCtx));
200 if (ctx != NULL) {
201 memset(ctx, 0x00, sizeof(SCProfilePrefilterDetectCtx));
202
203 if (pthread_mutex_init(&ctx->data_m, NULL) != 0) {
204 FatalError(SC_ERR_FATAL,
205 "Failed to initialize hash table mutex.");
206 }
207 }
208
209 return ctx;
210 }
211
DetroyCtx(SCProfilePrefilterDetectCtx * ctx)212 static void DetroyCtx(SCProfilePrefilterDetectCtx *ctx)
213 {
214 if (ctx) {
215 if (ctx->data != NULL)
216 SCFree(ctx->data);
217 pthread_mutex_destroy(&ctx->data_m);
218 SCFree(ctx);
219 }
220 }
221
SCProfilingPrefilterDestroyCtx(DetectEngineCtx * de_ctx)222 void SCProfilingPrefilterDestroyCtx(DetectEngineCtx *de_ctx)
223 {
224 if (de_ctx != NULL) {
225 SCProfilingPrefilterDump(de_ctx);
226
227 DetroyCtx(de_ctx->profile_prefilter_ctx);
228 }
229 }
230
SCProfilingPrefilterThreadSetup(SCProfilePrefilterDetectCtx * ctx,DetectEngineThreadCtx * det_ctx)231 void SCProfilingPrefilterThreadSetup(SCProfilePrefilterDetectCtx *ctx, DetectEngineThreadCtx *det_ctx)
232 {
233 if (ctx == NULL)
234 return;
235
236 const uint32_t size = det_ctx->de_ctx->prefilter_id;
237
238 SCProfilePrefilterData *a = SCMalloc(sizeof(SCProfilePrefilterData) * size);
239 if (a != NULL) {
240 memset(a, 0x00, sizeof(SCProfilePrefilterData) * size);
241 det_ctx->prefilter_perf_data = a;
242 }
243 }
244
SCProfilingPrefilterThreadMerge(DetectEngineCtx * de_ctx,DetectEngineThreadCtx * det_ctx)245 static void SCProfilingPrefilterThreadMerge(DetectEngineCtx *de_ctx, DetectEngineThreadCtx *det_ctx)
246 {
247 if (de_ctx == NULL || de_ctx->profile_prefilter_ctx == NULL ||
248 de_ctx->profile_prefilter_ctx->data == NULL || det_ctx == NULL ||
249 det_ctx->prefilter_perf_data == NULL)
250 return;
251
252 for (uint32_t i = 0; i < de_ctx->prefilter_id; i++) {
253 de_ctx->profile_prefilter_ctx->data[i].called += det_ctx->prefilter_perf_data[i].called;
254 de_ctx->profile_prefilter_ctx->data[i].total += det_ctx->prefilter_perf_data[i].total;
255 if (det_ctx->prefilter_perf_data[i].max > de_ctx->profile_prefilter_ctx->data[i].max)
256 de_ctx->profile_prefilter_ctx->data[i].max = det_ctx->prefilter_perf_data[i].max;
257 }
258 }
259
SCProfilingPrefilterThreadCleanup(DetectEngineThreadCtx * det_ctx)260 void SCProfilingPrefilterThreadCleanup(DetectEngineThreadCtx *det_ctx)
261 {
262 if (det_ctx == NULL || det_ctx->de_ctx == NULL || det_ctx->prefilter_perf_data == NULL)
263 return;
264
265 pthread_mutex_lock(&det_ctx->de_ctx->profile_prefilter_ctx->data_m);
266 SCProfilingPrefilterThreadMerge(det_ctx->de_ctx, det_ctx);
267 pthread_mutex_unlock(&det_ctx->de_ctx->profile_prefilter_ctx->data_m);
268
269 SCFree(det_ctx->prefilter_perf_data);
270 det_ctx->prefilter_perf_data = NULL;
271 }
272
273 /**
274 * \brief Register the prefilter profiling counters.
275 *
276 * \param de_ctx The active DetectEngineCtx, used to get at the loaded rules.
277 */
278 void
SCProfilingPrefilterInitCounters(DetectEngineCtx * de_ctx)279 SCProfilingPrefilterInitCounters(DetectEngineCtx *de_ctx)
280 {
281 if (profiling_prefilter_enabled == 0)
282 return;
283
284 const uint32_t size = de_ctx->prefilter_id;
285 if (size == 0)
286 return;
287
288 de_ctx->profile_prefilter_ctx = SCProfilingPrefilterInitCtx();
289 BUG_ON(de_ctx->profile_prefilter_ctx == NULL);
290 de_ctx->profile_prefilter_ctx->size = size;
291
292 de_ctx->profile_prefilter_ctx->data = SCMalloc(sizeof(SCProfilePrefilterData) * size);
293 BUG_ON(de_ctx->profile_prefilter_ctx->data == NULL);
294 memset(de_ctx->profile_prefilter_ctx->data, 0x00, sizeof(SCProfilePrefilterData) * size);
295
296 HashListTableBucket *hb = HashListTableGetListHead(de_ctx->prefilter_hash_table);
297 for ( ; hb != NULL; hb = HashListTableGetListNext(hb)) {
298 PrefilterStore *ctx = HashListTableGetListData(hb);
299 de_ctx->profile_prefilter_ctx->data[ctx->id].name = ctx->name;
300 SCLogDebug("prefilter %s set up", de_ctx->profile_prefilter_ctx->data[ctx->id].name);
301 }
302 SCLogDebug("size alloc'd %u", (uint32_t)size * (uint32_t)sizeof(SCProfilePrefilterData));
303
304 SCLogPerf("Registered %"PRIu32" prefilter profiling counters.", size);
305 }
306
307 #endif /* PROFILING */
308