40 typedef struct SCProfileSummary_ {
46 double avgticks_match;
47 double avgticks_no_match;
52 uint64_t ticks_no_match;
57 static char profiling_file_name[PATH_MAX] =
"";
58 static const char *profiling_file_mode =
"a";
59 static bool profiling_rule_json =
true;
65 SC_PROFILING_RULES_SORT_BY_TICKS = 0,
66 SC_PROFILING_RULES_SORT_BY_AVG_TICKS,
67 SC_PROFILING_RULES_SORT_BY_CHECKS,
68 SC_PROFILING_RULES_SORT_BY_MATCHES,
69 SC_PROFILING_RULES_SORT_BY_MAX_TICKS,
70 SC_PROFILING_RULES_SORT_BY_AVG_TICKS_MATCH,
71 SC_PROFILING_RULES_SORT_BY_AVG_TICKS_NO_MATCH,
74 static int profiling_rules_sort_orders[8] = {
75 SC_PROFILING_RULES_SORT_BY_TICKS,
76 SC_PROFILING_RULES_SORT_BY_AVG_TICKS,
77 SC_PROFILING_RULES_SORT_BY_AVG_TICKS_MATCH,
78 SC_PROFILING_RULES_SORT_BY_AVG_TICKS_NO_MATCH,
79 SC_PROFILING_RULES_SORT_BY_CHECKS,
80 SC_PROFILING_RULES_SORT_BY_MATCHES,
81 SC_PROFILING_RULES_SORT_BY_MAX_TICKS,
87 static uint32_t profiling_rules_limit = UINT32_MAX;
89 void SCProfilingRulesGlobalInit(
void)
91 #define SET_ONE(x) { \
92 profiling_rules_sort_orders[0] = (x); \
93 profiling_rules_sort_orders[1] = -1; \
106 if (strcmp(val,
"ticks") == 0) {
107 SET_ONE(SC_PROFILING_RULES_SORT_BY_TICKS);
109 else if (strcmp(val,
"avgticks") == 0) {
110 SET_ONE(SC_PROFILING_RULES_SORT_BY_AVG_TICKS);
112 else if (strcmp(val,
"avgticks_match") == 0) {
113 SET_ONE(SC_PROFILING_RULES_SORT_BY_AVG_TICKS_MATCH);
115 else if (strcmp(val,
"avgticks_no_match") == 0) {
116 SET_ONE(SC_PROFILING_RULES_SORT_BY_AVG_TICKS_NO_MATCH);
118 else if (strcmp(val,
"checks") == 0) {
119 SET_ONE(SC_PROFILING_RULES_SORT_BY_CHECKS);
121 else if (strcmp(val,
"matches") == 0) {
122 SET_ONE(SC_PROFILING_RULES_SORT_BY_MATCHES);
124 else if (strcmp(val,
"maxticks") == 0) {
125 SET_ONE(SC_PROFILING_RULES_SORT_BY_MAX_TICKS);
128 SCLogError(
"Invalid profiling sort order: %s", val);
136 (uint16_t)strlen(val), val) <= 0) {
142 if (filename != NULL) {
144 strlcpy(profiling_file_name, filename,
sizeof(profiling_file_name));
147 snprintf(profiling_file_name,
sizeof(profiling_file_name),
"%s/%s", log_dir,
153 profiling_file_mode =
"a";
155 profiling_file_mode =
"w";
162 profiling_rule_json =
false;
173 SCProfileSummarySortByTicks(
const void *a,
const void *b)
175 const SCProfileSummary *s0 = a;
176 const SCProfileSummary *s1 = b;
177 if (s1->ticks == s0->ticks)
180 return s0->ticks > s1->ticks ? -1 : 1;
187 SCProfileSummarySortByAvgTicksMatch(
const void *a,
const void *b)
189 const SCProfileSummary *s0 = a;
190 const SCProfileSummary *s1 = b;
191 if (s1->avgticks_match == s0->avgticks_match)
194 return s0->avgticks_match > s1->avgticks_match ? -1 : 1;
201 SCProfileSummarySortByAvgTicksNoMatch(
const void *a,
const void *b)
203 const SCProfileSummary *s0 = a;
204 const SCProfileSummary *s1 = b;
205 if (s1->avgticks_no_match == s0->avgticks_no_match)
208 return s0->avgticks_no_match > s1->avgticks_no_match ? -1 : 1;
215 SCProfileSummarySortByAvgTicks(
const void *a,
const void *b)
217 const SCProfileSummary *s0 = a;
218 const SCProfileSummary *s1 = b;
219 if (s1->avgticks == s0->avgticks)
222 return s0->avgticks > s1->avgticks ? -1 : 1;
229 SCProfileSummarySortByChecks(
const void *a,
const void *b)
231 const SCProfileSummary *s0 = a;
232 const SCProfileSummary *s1 = b;
233 if (s1->checks == s0->checks)
236 return s0->checks > s1->checks ? -1 : 1;
243 SCProfileSummarySortByMatches(
const void *a,
const void *b)
245 const SCProfileSummary *s0 = a;
246 const SCProfileSummary *s1 = b;
247 if (s1->matches == s0->matches)
250 return s0->matches > s1->matches ? -1 : 1;
257 SCProfileSummarySortByMaxTicks(
const void *a,
const void *b)
259 const SCProfileSummary *s0 = a;
260 const SCProfileSummary *s1 = b;
261 if (s1->max == s0->max)
264 return s0->max > s1->max ? -1 : 1;
267 static json_t *BuildJson(
268 SCProfileSummary *summary, uint32_t count, uint64_t total_ticks,
const char *sort_desc)
275 json_t *js = json_object();
278 json_t *jsa = json_array();
284 gettimeofday(&tval, NULL);
286 json_object_set_new(js,
"timestamp", json_string(timebuf));
287 json_object_set_new(js,
"sort", json_string(sort_desc));
289 for (i = 0; i <
MIN(count, profiling_rules_limit); i++) {
293 if (summary[i].checks == 0)
296 json_t *jsm = json_object();
298 json_object_set_new(jsm,
"signature_id", json_integer(summary[i].sid));
299 json_object_set_new(jsm,
"gid", json_integer(summary[i].gid));
300 json_object_set_new(jsm,
"rev", json_integer(summary[i].rev));
302 json_object_set_new(jsm,
"checks", json_integer(summary[i].checks));
303 json_object_set_new(jsm,
"matches", json_integer(summary[i].matches));
305 json_object_set_new(jsm,
"ticks_total", json_integer(summary[i].ticks));
306 json_object_set_new(jsm,
"ticks_max", json_integer(summary[i].max));
307 json_object_set_new(jsm,
"ticks_avg", json_integer(summary[i].avgticks));
308 json_object_set_new(jsm,
"ticks_avg_match", json_integer(summary[i].avgticks_match));
309 json_object_set_new(jsm,
"ticks_avg_nomatch", json_integer(summary[i].avgticks_no_match));
311 double percent = (
long double)summary[i].ticks /
312 (
long double)total_ticks * 100;
313 json_object_set_new(jsm,
"percent", json_integer(percent));
314 json_array_append_new(jsa, jsm);
317 json_object_set_new(js,
"rules", jsa);
321 static void DumpJson(FILE *fp, SCProfileSummary *summary, uint32_t count, uint64_t total_ticks,
322 const char *sort_desc)
324 json_t *js = BuildJson(summary, count, total_ticks, sort_desc);
327 char *js_s = json_dumps(js,
328 JSON_PRESERVE_ORDER|JSON_COMPACT|JSON_ENSURE_ASCII|
333 fprintf(fp,
"%s\n", js_s);
338 static void DumpText(FILE *fp, SCProfileSummary *summary,
339 uint32_t count, uint64_t total_ticks,
340 const char *sort_desc)
345 gettimeofday(&tval, NULL);
349 fprintf(fp,
" ----------------------------------------------"
350 "----------------------------\n");
351 fprintf(fp,
" Date: %" PRId32
"/%" PRId32
"/%04d -- "
352 "%02d:%02d:%02d.", tms->tm_mon + 1, tms->tm_mday, tms->tm_year + 1900,
353 tms->tm_hour,tms->tm_min, tms->tm_sec);
354 fprintf(fp,
" Sorted by: %s.\n", sort_desc);
355 fprintf(fp,
" ----------------------------------------------"
356 "----------------------------\n");
357 fprintf(fp,
" %-8s %-12s %-8s %-8s %-12s %-6s %-8s %-8s %-11s %-11s %-11s %-11s\n",
"Num",
"Rule",
"Gid",
"Rev",
"Ticks",
"%",
"Checks",
"Matches",
"Max Ticks",
"Avg Ticks",
"Avg Match",
"Avg No Match");
358 fprintf(fp,
" -------- "
371 for (i = 0; i <
MIN(count, profiling_rules_limit); i++) {
376 if (summary[i].checks == 0)
379 double percent = (
long double)summary[i].ticks /
380 (
long double)total_ticks * 100;
382 " %-8"PRIu32
" %-12u %-8"PRIu32
" %-8"PRIu32
" %-12"PRIu64
" %-6.2f %-8"PRIu64
" %-8"PRIu64
" %-11"PRIu64
" %-11.2f %-11.2f %-11.2f\n",
393 summary[i].avgticks_match,
394 summary[i].avgticks_no_match);
405 static void *SCProfilingRuleDump(SCProfileDetectCtx *rules_ctx,
int file_output)
410 if (rules_ctx == NULL)
413 if (file_output != 0) {
415 fp = fopen(profiling_file_name, profiling_file_mode);
418 SCLogError(
"failed to open %s: %s", profiling_file_name, strerror(errno));
426 int summary_size =
sizeof(SCProfileSummary) * rules_ctx->size;
427 SCProfileSummary *summary =
SCMalloc(summary_size);
429 SCLogError(
"Error allocating memory for profiling summary");
433 uint32_t count = rules_ctx->size;
434 uint64_t total_ticks = 0;
436 SCLogPerf(
"Dumping profiling data for %u rules.", count);
438 memset(summary, 0, summary_size);
439 for (i = 0; i < count; i++) {
440 summary[i].sid = rules_ctx->data[i].sid;
441 summary[i].rev = rules_ctx->data[i].rev;
442 summary[i].gid = rules_ctx->data[i].gid;
444 summary[i].ticks = rules_ctx->data[i].ticks_match + rules_ctx->data[i].ticks_no_match;
445 summary[i].checks = rules_ctx->data[i].checks;
447 if (summary[i].checks > 0) {
448 summary[i].avgticks = (
long double)summary[i].ticks / (
long double)summary[i].checks;
451 summary[i].matches = rules_ctx->data[i].matches;
452 summary[i].max = rules_ctx->data[i].max;
453 summary[i].ticks_match = rules_ctx->data[i].ticks_match;
454 summary[i].ticks_no_match = rules_ctx->data[i].ticks_no_match;
455 if (summary[i].ticks_match > 0) {
456 summary[i].avgticks_match = (
long double)summary[i].ticks_match /
457 (
long double)summary[i].matches;
460 if (summary[i].ticks_no_match > 0) {
461 summary[i].avgticks_no_match = (
long double)summary[i].ticks_no_match /
462 ((
long double)summary[i].checks - (
long double)summary[i].matches);
464 total_ticks += summary[i].ticks;
467 int *order = profiling_rules_sort_orders;
468 while (*order != -1) {
469 const char *sort_desc = NULL;
471 case SC_PROFILING_RULES_SORT_BY_TICKS:
472 qsort(summary, count,
sizeof(SCProfileSummary),
473 SCProfileSummarySortByTicks);
476 case SC_PROFILING_RULES_SORT_BY_AVG_TICKS:
477 qsort(summary, count,
sizeof(SCProfileSummary),
478 SCProfileSummarySortByAvgTicks);
479 sort_desc =
"average ticks";
481 case SC_PROFILING_RULES_SORT_BY_CHECKS:
482 qsort(summary, count,
sizeof(SCProfileSummary),
483 SCProfileSummarySortByChecks);
484 sort_desc =
"number of checks";
486 case SC_PROFILING_RULES_SORT_BY_MATCHES:
487 qsort(summary, count,
sizeof(SCProfileSummary),
488 SCProfileSummarySortByMatches);
489 sort_desc =
"number of matches";
491 case SC_PROFILING_RULES_SORT_BY_MAX_TICKS:
492 qsort(summary, count,
sizeof(SCProfileSummary),
493 SCProfileSummarySortByMaxTicks);
494 sort_desc =
"max ticks";
496 case SC_PROFILING_RULES_SORT_BY_AVG_TICKS_MATCH:
497 qsort(summary, count,
sizeof(SCProfileSummary),
498 SCProfileSummarySortByAvgTicksMatch);
499 sort_desc =
"average ticks (match)";
501 case SC_PROFILING_RULES_SORT_BY_AVG_TICKS_NO_MATCH:
502 qsort(summary, count,
sizeof(SCProfileSummary),
503 SCProfileSummarySortByAvgTicksNoMatch);
504 sort_desc =
"average ticks (no match)";
507 if (profiling_rule_json) {
508 if (file_output != 1) {
509 json_t *js = BuildJson(summary, count, total_ticks, sort_desc);
513 DumpJson(fp, summary, count, total_ticks, sort_desc);
516 DumpText(fp, summary, count, total_ticks, sort_desc);
521 if (file_output != 0) {
526 SCLogPerf(
"Done dumping profiling data.");
536 SCProfilingRegisterRuleCounter(SCProfileDetectCtx *
ctx)
552 if (det_ctx != NULL && det_ctx->rule_perf_data != NULL && det_ctx->rule_perf_data_size >
id) {
553 SCProfileData *p = &det_ctx->rule_perf_data[
id];
560 p->ticks_match += ticks;
562 p->ticks_no_match += ticks;
566 static SCProfileDetectCtx *SCProfilingRuleInitCtx(
void)
568 SCProfileDetectCtx *
ctx =
SCCalloc(1,
sizeof(SCProfileDetectCtx));
570 if (pthread_mutex_init(&
ctx->data_m, NULL) != 0) {
571 FatalError(
"Failed to initialize hash table mutex.");
578 void SCProfilingRuleDestroyCtx(SCProfileDetectCtx *
ctx)
581 SCProfilingRuleDump(
ctx, 1);
582 if (
ctx->data != NULL)
584 pthread_mutex_destroy(&
ctx->data_m);
591 if (
ctx == NULL||
ctx->size == 0)
594 SCProfileData *a =
SCCalloc(
ctx->size,
sizeof(SCProfileData));
596 det_ctx->rule_perf_data = a;
597 det_ctx->rule_perf_data_size =
ctx->size;
601 static void SCProfilingRuleThreadMerge(
604 if (
de_ctx == NULL ||
de_ctx->profile_ctx == NULL ||
de_ctx->profile_ctx->data == NULL ||
605 det_ctx == NULL || det_ctx->rule_perf_data == NULL)
608 for (
int i = 0; i < det_ctx->rule_perf_data_size; i++) {
609 de_ctx->profile_ctx->data[i].checks += det_ctx->rule_perf_data[i].checks;
610 de_ctx->profile_ctx->data[i].matches += det_ctx->rule_perf_data[i].matches;
611 de_ctx->profile_ctx->data[i].ticks_match += det_ctx->rule_perf_data[i].ticks_match;
612 de_ctx->profile_ctx->data[i].ticks_no_match += det_ctx->rule_perf_data[i].ticks_no_match;
614 det_ctx->rule_perf_data[i].checks = 0;
615 det_ctx->rule_perf_data[i].matches = 0;
616 det_ctx->rule_perf_data[i].ticks_match = 0;
617 det_ctx->rule_perf_data[i].ticks_no_match = 0;
619 if (det_ctx->rule_perf_data[i].max >
de_ctx->profile_ctx->data[i].max)
620 de_ctx->profile_ctx->data[i].max = det_ctx->rule_perf_data[i].max;
626 if (det_ctx == NULL || det_ctx->
de_ctx == NULL || det_ctx->rule_perf_data == NULL)
629 pthread_mutex_lock(&det_ctx->
de_ctx->profile_ctx->data_m);
630 SCProfilingRuleThreadMerge(det_ctx->
de_ctx, det_ctx,
false);
631 pthread_mutex_unlock(&det_ctx->
de_ctx->profile_ctx->data_m);
633 SCFree(det_ctx->rule_perf_data);
634 det_ctx->rule_perf_data = NULL;
635 det_ctx->rule_perf_data_size = 0;
641 if (det_ctx == NULL || det_ctx->
de_ctx == NULL || det_ctx->
de_ctx->profile_ctx == NULL)
643 pthread_mutex_lock(&det_ctx->
de_ctx->profile_ctx->data_m);
644 SCProfilingRuleThreadMerge(det_ctx->
de_ctx, det_ctx,
true);
645 pthread_mutex_unlock(&det_ctx->
de_ctx->profile_ctx->data_m);
659 de_ctx->profile_ctx = SCProfilingRuleInitCtx();
664 while (sig != NULL) {
665 sig->profiling_id = SCProfilingRegisterRuleCounter(
de_ctx->profile_ctx);
675 while (sig != NULL) {
676 de_ctx->profile_ctx->data[sig->profiling_id].sid = sig->
id;
677 de_ctx->profile_ctx->data[sig->profiling_id].gid = sig->
gid;
678 de_ctx->profile_ctx->data[sig->profiling_id].rev = sig->
rev;
683 SCLogPerf(
"Registered %"PRIu32
" rule profiling counters.", count);
688 return SCProfilingRuleDump(
de_ctx->profile_ctx, 0);