mirror of https://github.com/OISF/suricata
Basic rule profiling even though the results may be skewed by a bad rule in a grouping of rules.
parent
b629b7c5c1
commit
18e5ac8cde
@ -0,0 +1,352 @@
|
||||
/* Copyright (c) 2010 Open Information Security Foundation */
|
||||
|
||||
/**
|
||||
* \file
|
||||
* \author Endace Technology Limited.
|
||||
*
|
||||
* An API for profiling operations.
|
||||
*
|
||||
* Really just a wrapper around the existing perf counters.
|
||||
*/
|
||||
|
||||
#include "suricata-common.h"
|
||||
#include "detect.h"
|
||||
#include "counters.h"
|
||||
#include "conf.h"
|
||||
#include "util-unittest.h"
|
||||
#include "util-byte.h"
|
||||
#include "util-profiling.h"
|
||||
|
||||
#ifdef PROFILING
|
||||
|
||||
#define MIN(a, b) (((a) < (b)) ? (a) : (b))
|
||||
|
||||
/**
|
||||
* Sort orders for dumping profiled rules.
|
||||
*/
|
||||
enum {
|
||||
SC_PROFILING_RULES_SORT_BY_TICKS = 0,
|
||||
SC_PROFILING_RULES_SORT_BY_AVG_TICKS,
|
||||
SC_PROFILING_RULES_SORT_BY_CHECKS,
|
||||
SC_PROFILING_RULES_SORT_BY_MATCHES,
|
||||
};
|
||||
static int profiling_rules_sort_order = SC_PROFILING_RULES_SORT_BY_TICKS;
|
||||
|
||||
/**
|
||||
* Maximum number of rules to dump.
|
||||
*/
|
||||
static uint32_t profiling_rules_limit = UINT32_MAX;
|
||||
|
||||
static SCPerfContext rules_ctx;
|
||||
static SCPerfCounterArray *rules_pca;
|
||||
|
||||
/**
|
||||
* Extra data for rule profiling.
|
||||
*/
|
||||
typedef struct SCProfileData_ {
|
||||
uint64_t matches;
|
||||
} SCProfileData;
|
||||
SCProfileData rules_profile_data[0xffff];
|
||||
|
||||
/**
|
||||
* Used for generating the summary data to print.
|
||||
*/
|
||||
typedef struct SCProfileSummary_ {
|
||||
char *name;
|
||||
uint64_t ticks;
|
||||
double avgticks;
|
||||
uint64_t checks;
|
||||
uint64_t matches;
|
||||
} SCProfileSummary;
|
||||
|
||||
int profiling_rules_enabled = 0;
|
||||
|
||||
/**
|
||||
* \brief Initialize profiling.
|
||||
*/
|
||||
void
|
||||
SCProfilingInit(void)
|
||||
{
|
||||
ConfNode *conf;
|
||||
const char *val;
|
||||
|
||||
conf = ConfGetNode("profiling.rules");
|
||||
if (conf == NULL) {
|
||||
return;
|
||||
}
|
||||
if (ConfNodeChildValueIsTrue(conf, "enabled")) {
|
||||
memset(rules_profile_data, 0, sizeof(rules_profile_data));
|
||||
memset(&rules_ctx, 0, sizeof(rules_ctx));
|
||||
rules_pca = SCPerfGetAllCountersArray(NULL);
|
||||
if (SCMutexInit(&rules_ctx.m, NULL) != 0) {
|
||||
SCLogError(SC_ERR_MEM_ALLOC,
|
||||
"Failed to initialize hash table mutex.");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
profiling_rules_enabled = 1;
|
||||
|
||||
val = ConfNodeLookupChildValue(conf, "sort");
|
||||
if (val != NULL) {
|
||||
if (strcmp(val, "ticks") == 0) {
|
||||
profiling_rules_sort_order =
|
||||
SC_PROFILING_RULES_SORT_BY_TICKS;
|
||||
}
|
||||
else if (strcmp(val, "avgticks") == 0) {
|
||||
profiling_rules_sort_order =
|
||||
SC_PROFILING_RULES_SORT_BY_AVG_TICKS;
|
||||
}
|
||||
else if (strcmp(val, "checks") == 0) {
|
||||
profiling_rules_sort_order =
|
||||
SC_PROFILING_RULES_SORT_BY_CHECKS;
|
||||
}
|
||||
else if (strcmp(val, "matches") == 0) {
|
||||
profiling_rules_sort_order =
|
||||
SC_PROFILING_RULES_SORT_BY_MATCHES;
|
||||
}
|
||||
}
|
||||
|
||||
val = ConfNodeLookupChildValue(conf, "limit");
|
||||
if (val != NULL) {
|
||||
if (ByteExtractStringUint32(&profiling_rules_limit, 10,
|
||||
(uint16_t)strlen(val), val) <= 0) {
|
||||
SCLogError(SC_ERR_INVALID_ARGUMENT, "Invalid limit: %s", val);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Free resources used by profiling.
|
||||
*/
|
||||
void
|
||||
SCProfilingDestroy(void)
|
||||
{
|
||||
if (profiling_rules_enabled) {
|
||||
SCPerfReleasePerfCounterS(rules_ctx.head);
|
||||
SCPerfReleasePCA(rules_pca);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Qsort comparison function to sort by ticks.
|
||||
*/
|
||||
static int
|
||||
SCProfileSummarySortByTicks(const void *a, const void *b)
|
||||
{
|
||||
const SCProfileSummary *s0 = a;
|
||||
const SCProfileSummary *s1 = b;
|
||||
return s1->ticks - s0->ticks;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Qsort comparison function to sort by average ticks.
|
||||
*/
|
||||
static int
|
||||
SCProfileSummarySortByAvgTicks(const void *a, const void *b)
|
||||
{
|
||||
const SCProfileSummary *s0 = a;
|
||||
const SCProfileSummary *s1 = b;
|
||||
return s1->avgticks - s0->avgticks;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Qsort comparison function to sort by checks.
|
||||
*/
|
||||
static int
|
||||
SCProfileSummarySortByChecks(const void *a, const void *b)
|
||||
{
|
||||
const SCProfileSummary *s0 = a;
|
||||
const SCProfileSummary *s1 = b;
|
||||
return s1->checks - s0->checks;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Qsort comparison function to sort by matches.
|
||||
*/
|
||||
static int
|
||||
SCProfileSummarySortByMatches(const void *a, const void *b)
|
||||
{
|
||||
const SCProfileSummary *s0 = a;
|
||||
const SCProfileSummary *s1 = b;
|
||||
return s1->matches - s0->matches;
|
||||
}
|
||||
|
||||
void
|
||||
SCProfilingDump(FILE *output)
|
||||
{
|
||||
uint32_t i;
|
||||
SCProfileSummary summary[rules_pca->size];
|
||||
uint32_t count = rules_pca->size;
|
||||
|
||||
SCLogInfo("Dumping profiling data.");
|
||||
|
||||
memset(summary, 0, sizeof(summary));
|
||||
for (i = 1; i < count + 1; i++) {
|
||||
summary[i - 1].name = rules_pca->head[i].pc->name->cname;
|
||||
summary[i - 1].ticks = rules_pca->head[i].ui64_cnt;
|
||||
if (rules_pca->head[i].ui64_cnt)
|
||||
summary[i - 1].avgticks = (long double)rules_pca->head[i].ui64_cnt /
|
||||
(long double)rules_pca->head[i].syncs;
|
||||
summary[i - 1].checks = rules_pca->head[i].syncs;
|
||||
summary[i - 1].matches = rules_profile_data[i].matches;
|
||||
}
|
||||
|
||||
switch (profiling_rules_sort_order) {
|
||||
case SC_PROFILING_RULES_SORT_BY_TICKS:
|
||||
qsort(summary, count, sizeof(SCProfileSummary),
|
||||
SCProfileSummarySortByTicks);
|
||||
break;
|
||||
case SC_PROFILING_RULES_SORT_BY_AVG_TICKS:
|
||||
qsort(summary, count, sizeof(SCProfileSummary),
|
||||
SCProfileSummarySortByAvgTicks);
|
||||
break;
|
||||
case SC_PROFILING_RULES_SORT_BY_CHECKS:
|
||||
qsort(summary, count, sizeof(SCProfileSummary),
|
||||
SCProfileSummarySortByChecks);
|
||||
break;
|
||||
case SC_PROFILING_RULES_SORT_BY_MATCHES:
|
||||
qsort(summary, count, sizeof(SCProfileSummary),
|
||||
SCProfileSummarySortByMatches);
|
||||
break;
|
||||
}
|
||||
|
||||
fprintf(output, " %-12s %-12s %-8s %-8s %-11s\n", "Rule", "Ticks", "Checks", "Matches", "Avg Ticks");
|
||||
fprintf(output, " ------------ "
|
||||
"------------ "
|
||||
"-------- "
|
||||
"-------- "
|
||||
"----------- "
|
||||
"\n");
|
||||
for (i = 0; i < MIN(count, profiling_rules_limit); i++) {
|
||||
fprintf(output, " %-12s %-12"PRIu64" %-8"PRIu64" %-8"PRIu64" %-8.2f\n",
|
||||
summary[i].name,
|
||||
summary[i].ticks,
|
||||
summary[i].checks,
|
||||
summary[i].matches,
|
||||
summary[i].avgticks);
|
||||
}
|
||||
|
||||
SCLogInfo("Done dumping profiling data.");
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Register a rule profiling counter.
|
||||
*
|
||||
* \param gid The GID of the rule.
|
||||
* \param sid The SID of the rule.
|
||||
*
|
||||
* \retval Returns the ID of the counter on success, 0 on failure.
|
||||
*/
|
||||
static uint16_t
|
||||
SCProfilingRegisterRuleCounter(Signature *sig)
|
||||
{
|
||||
char name[12];
|
||||
uint16_t id;
|
||||
|
||||
/* Don't use GID right now... */
|
||||
//snprintf(name, sizeof(name), "%"PRIu32":%"PRIu32, gid, sid);
|
||||
snprintf(name, sizeof(name), "%"PRIu32, sig->id);
|
||||
|
||||
id = SCPerfRegisterCounter(name, "profiler", SC_PERF_TYPE_UINT64, NULL,
|
||||
&rules_ctx);
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Register the rule profiling counters.
|
||||
*
|
||||
* \param de_ctx The active DetectEngineCtx, used to get at the loaded rules.
|
||||
*/
|
||||
void
|
||||
SCProfilingInitRuleCounters(DetectEngineCtx *de_ctx)
|
||||
{
|
||||
Signature *sig = de_ctx->sig_list;
|
||||
uint32_t count = 0;
|
||||
while (sig != NULL) {
|
||||
sig->profiling_id = SCProfilingRegisterRuleCounter(sig);
|
||||
sig = sig->next;
|
||||
count++;
|
||||
}
|
||||
rules_pca = SCPerfGetAllCountersArray(&rules_ctx);
|
||||
SCLogInfo("Registered %"PRIu32" rule profiling counters.\n", count);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Add a uint64_t value to the current value of the rule
|
||||
* counter ID.
|
||||
*
|
||||
* \param id The ID of the rule profiling counter.
|
||||
* \paral val The value to add to the counter.
|
||||
*/
|
||||
void
|
||||
SCProfilingCounterAddUI64(uint16_t id, uint64_t val)
|
||||
{
|
||||
SCPerfCounterAddUI64(id, rules_pca, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Update a rule counter.
|
||||
*
|
||||
* \param id The ID of this counter.
|
||||
* \param ticks Number of CPU ticks for this rule.
|
||||
* \param match Did the rule match?
|
||||
*/
|
||||
void
|
||||
SCProfilingUpdateRuleCounter(uint16_t id, uint64_t ticks, int match)
|
||||
{
|
||||
SCMutexLock(&rules_ctx.m);
|
||||
SCProfilingCounterAddUI64(id, ticks);
|
||||
rules_profile_data[id].matches += match;
|
||||
SCMutexUnlock(&rules_ctx.m);
|
||||
}
|
||||
|
||||
#ifdef UNITTESTS
|
||||
|
||||
static int
|
||||
ProfilingTest01(void)
|
||||
{
|
||||
uint16_t counter1;
|
||||
|
||||
Signature sig;
|
||||
sig.gid = 1;
|
||||
sig.id = 1;
|
||||
|
||||
SCProfilingInit();
|
||||
counter1 = SCProfilingRegisterRuleCounter(&sig);
|
||||
if (counter1 == 0)
|
||||
return 0;
|
||||
rules_pca = SCPerfGetAllCountersArray(&rules_ctx);
|
||||
if (rules_pca == NULL)
|
||||
return 0;
|
||||
SCProfilingCounterAddUI64(counter1, 64);
|
||||
if (rules_pca->head[counter1].ui64_cnt != 64)
|
||||
return 0;
|
||||
if (rules_pca->head[counter1].syncs != 1)
|
||||
return 0;
|
||||
SCProfilingCounterAddUI64(counter1, 64);
|
||||
if (rules_pca->head[counter1].ui64_cnt != 128)
|
||||
return 0;
|
||||
if (rules_pca->head[counter1].syncs != 2)
|
||||
return 0;
|
||||
if (rules_pca->head[counter1].wrapped_syncs != 0)
|
||||
return 0;
|
||||
|
||||
SCProfilingDump(stdout);
|
||||
|
||||
SCProfilingDestroy();
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
#endif /* UNITTESTS */
|
||||
|
||||
void
|
||||
SCProfilingRegisterTests(void)
|
||||
{
|
||||
#ifdef UNITTESTS
|
||||
UtRegisterTest("ProfilingTest01", ProfilingTest01, 1);
|
||||
#endif /* UNITTESTS */
|
||||
}
|
||||
|
||||
#endif /* PROFILING */
|
@ -0,0 +1,39 @@
|
||||
#ifndef __UTIL_PROFILE_H__
|
||||
#define __UTIL_PROFILE_H__
|
||||
|
||||
#ifdef PROFILING
|
||||
|
||||
#include "util-cpu.h"
|
||||
|
||||
extern int profiling_rules_enabled;
|
||||
|
||||
#define PROFILING_START \
|
||||
uint64_t profile_start_ = 0; \
|
||||
uint64_t profile_end_ = 0; \
|
||||
if (profiling_rules_enabled) { \
|
||||
profile_start_ = UtilCpuGetTicks(); \
|
||||
}
|
||||
|
||||
#define RULE_PROFILING_END(r, m) \
|
||||
if (profiling_rules_enabled) { \
|
||||
profile_end_ = UtilCpuGetTicks(); \
|
||||
SCProfilingUpdateRuleCounter(r->profiling_id, \
|
||||
profile_end_ - profile_start_, m); \
|
||||
}
|
||||
|
||||
void SCProfilingInit(void);
|
||||
void SCProfilingDestroy(void);
|
||||
void SCProfilingInitRuleCounters(DetectEngineCtx *);
|
||||
void SCProfilingCounterAddUI64(uint16_t, uint64_t);
|
||||
void SCProfilingRegisterTests(void);
|
||||
void SCProfilingDump(FILE *);
|
||||
void SCProfilingUpdateRuleCounter(uint16_t, uint64_t, int);
|
||||
|
||||
#else
|
||||
|
||||
#define PROFILING_START
|
||||
#define RULE_PROFILING_END(r, m)
|
||||
|
||||
#endif /* PROFILING */
|
||||
|
||||
#endif /* ! __UTIL_PROFILE_H__ */
|
Loading…
Reference in New Issue