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