MemoryContext::MemoryContext(MemoryTracker&) { }
MemoryContext::~MemoryContext() = default;
-bool TimeProfilerStats::enabled = false;
+THREAD_LOCAL bool TimeProfilerStats::enabled = false;
}
extern const BaseApi* ips_regex;
PacketTracer::thread_init();
HostAttributesManager::initialize();
RuleContext::set_enabled(sc->profiler->rule.show);
+ TimeProfilerStats::set_enabled(sc->profiler->time.show);
// in case there are HA messages waiting, process them first
HighAvailabilityManager::process_receive();
void Profiler::start() { }
void Profiler::stop(uint64_t) { }
-void Profiler::consolidate_stats() { }
+void Profiler::consolidate_stats(snort::ProfilerType) { }
void Swapper::apply(Analyzer&) { }
Swapper::~Swapper() = default;
void OopsHandler::tinit() { }
static struct timeval s_packet_time = { 0, 0 };
THREAD_LOCAL PacketTracer* s_pkt_trace;
THREAD_LOCAL TimeContext* ProfileContext::curr_time = nullptr;
-bool TimeProfilerStats::enabled = false;
+THREAD_LOCAL bool TimeProfilerStats::enabled = false;
THREAD_LOCAL PacketCount pc;
void packet_gettimeofday(struct timeval* tv) { *tv = s_packet_time; }
// Profiler mock functions
void Profiler::register_module(Module*) { }
void Profiler::register_module(const char*, const char*, Module*) { }
-void Profiler::consolidate_stats() { }
-void Profiler::reset_stats() { }
+void Profiler::consolidate_stats(snort::ProfilerType) { }
+void Profiler::reset_stats(snort::ProfilerType) { }
void Profiler::show_stats() { }
OdpContext::OdpContext(const AppIdConfig&, snort::SnortConfig*) { }
#include "profiler_nodes.h"
#include "profiler_printer.h"
#include "memory_defs.h"
+#include "control/control.h"
#ifdef UNIT_TEST
#include "catch/snort_catch.h"
t << v.avg_alloc();
}
+struct s_print_table
+{
+ ControlConn* ctrlcon;
+
+ s_print_table(ControlConn* ctrlcon) : ctrlcon(ctrlcon) {}
+
+ void operator()(const char* l) const
+ { LogRespond(ctrlcon, "%s", l); }
+};
+
} // namespace memory_stats
void show_memory_profiler_stats(ProfilerNodeMap& nodes, const MemoryProfilerConfig& config)
return;
const auto& sorter = memory_stats::sorters[config.sort];
+ const auto& printer_t = memory_stats::s_print_table(nullptr);
- ProfilerPrinter<memory_stats::View> printer(memory_stats::fields, memory_stats::print_fn, sorter);
+ ProfilerPrinter<memory_stats::View> printer(memory_stats::fields, memory_stats::print_fn, sorter, printer_t);
printer.print_table(s_memory_table_title, root, config.count, config.max_depth);
}
THREAD_LOCAL TimeContext* ProfileContext::curr_time = nullptr;
THREAD_LOCAL Stopwatch<SnortClock>* run_timer = nullptr;
+THREAD_LOCAL uint64_t first_pkt_num = 0;
+THREAD_LOCAL bool consolidated_once = false;
static ProfilerNodeMap s_profiler_nodes;
void Profiler::start()
{
+ first_pkt_num = (uint64_t)get_packet_number();
run_timer = new Stopwatch<SnortClock>;
run_timer->start();
+ consolidated_once = false;
}
void Profiler::stop(uint64_t checks)
{
- run_timer->stop();
- totalPerfStats.time.elapsed = run_timer->get();
- totalPerfStats.time.checks = checks;
+ if ( run_timer )
+ {
+ run_timer->stop();
+ totalPerfStats.time.elapsed = run_timer->get();
+ totalPerfStats.time.checks = checks - first_pkt_num;
- delete run_timer;
- run_timer = nullptr;
+ delete run_timer;
+ run_timer = nullptr;
+ }
}
-void Profiler::consolidate_stats()
+void Profiler::consolidate_stats(snort::ProfilerType type)
{
- s_profiler_nodes.accumulate_nodes();
- MemoryProfiler::consolidate_fallthrough_stats();
+ if ( !consolidated_once and type == snort::PROFILER_TYPE_TIME )
+ {
+ s_profiler_nodes.accumulate_nodes(snort::PROFILER_TYPE_TIME);
+ consolidated_once = true;
+ }
+ else if ( !consolidated_once and type == snort::PROFILER_TYPE_BOTH )
+ {
+ s_profiler_nodes.accumulate_nodes();
+ }
+
+ if ( consolidated_once and type == snort::PROFILER_TYPE_BOTH )
+ {
+#ifdef ENABLE_MEMORY_PROFILER
+ s_profiler_nodes.accumulate_nodes(PROFILER_TYPE_MEMORY);
+ MemoryProfiler::consolidate_fallthrough_stats();
+#endif
+ }
}
-void Profiler::reset_stats()
+void Profiler::reset_stats(snort::ProfilerType type)
{
- s_profiler_nodes.reset_nodes();
- reset_rule_profiler_stats();
+ if ( type == snort::PROFILER_TYPE_TIME )
+ {
+ totalPerfStats.reset_time();
+ otherPerfStats.reset_time();
+ }
+ else
+ {
+ totalPerfStats.reset();
+ otherPerfStats.reset();
+ }
+
+ s_profiler_nodes.reset_nodes(type);
}
-void Profiler::show_stats()
+void Profiler::prepare_stats()
{
const ProfilerNode& root = s_profiler_nodes.get_root();
auto children = root.get_children();
hr_duration runtime = root.get_stats().time.elapsed;
hr_duration sum = 0_ticks;
+ s_profiler_nodes.clear_flex();
+
for ( auto pn : children )
sum += pn->get_stats().time.elapsed;
otherPerfStats.time.elapsed = (runtime > sum) ? (runtime - sum) : 0_ticks;
s_profiler_nodes.accumulate_flex();
+}
+
+ProfilerNodeMap& Profiler::get_profiler_nodes()
+{
+ return s_profiler_nodes;
+}
+void Profiler::show_stats()
+{
+ prepare_stats();
const auto* config = SnortConfig::get_conf()->get_profiler();
assert(config);
{
class Module;
}
-
+class ControlConn;
+class ProfilerNodeMap;
class Profiler
{
public:
static void start();
static void stop(uint64_t);
- static void consolidate_stats();
+ static void consolidate_stats(snort::ProfilerType = snort::PROFILER_TYPE_BOTH);
- static void reset_stats();
+ static void reset_stats(snort::ProfilerType = snort::PROFILER_TYPE_BOTH);
+ static void prepare_stats();
static void show_stats();
+ static ProfilerNodeMap& get_profiler_nodes();
};
extern THREAD_LOCAL snort::ProfileStats totalPerfStats;
#define ROOT_NODE "total"
#define FLEX_NODE "other"
+enum ProfilerType
+{
+ PROFILER_TYPE_TIME,
+ PROFILER_TYPE_MEMORY,
+ PROFILER_TYPE_BOTH
+};
+
struct ProfilerConfig
{
TimeProfilerConfig time;
memory.reset();
}
+ void reset_time()
+ {
+ time.reset();
+ }
+
bool operator==(const ProfileStats&) const;
bool operator!=(const ProfileStats& rhs) const
{ return !(*this == rhs); }
ProfileStats& operator+=(const ProfileStats&);
+ ProfileStats& operator+=(const TimeProfilerStats&);
+ ProfileStats& operator+=(const MemoryTracker&);
constexpr ProfileStats() = default;
constexpr ProfileStats(const TimeProfilerStats& time, const MemoryTracker& memory) :
return *this;
}
+inline ProfileStats& ProfileStats::operator+=(const TimeProfilerStats& rhs)
+{
+ time += rhs;
+ return *this;
+}
+
+inline ProfileStats& ProfileStats::operator+=(const MemoryTracker& rhs)
+{
+ memory.stats += rhs.stats;
+ return *this;
+}
+
class SO_PUBLIC ProfileContext
{
public:
#include "rule_profiler.h"
#include "rule_profiler_defs.h"
+#include "time_profiler.h"
+#include "time_profiler_defs.h"
using namespace snort;
{ nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
};
+static void time_profiling_start_cmd()
+{
+ // Because profiler is always started in Analyzer::operator()
+ Profiler::stop(0);
+ TimeProfilerStats::set_enabled(true);
+ Profiler::reset_stats(snort::PROFILER_TYPE_TIME);
+ Profiler::start();
+}
+
+static void time_profiling_stop_cmd()
+{
+ TimeProfilerStats::set_enabled(false);
+ Profiler::stop((uint64_t)get_packet_number());
+ Profiler::consolidate_stats(snort::PROFILER_TYPE_TIME);
+}
+
+class ProfilerTimeCmd : public AnalyzerCommand
+{
+public:
+ ProfilerTimeCmd(bool en) : enable(en) { }
+ bool execute(Analyzer&, void**) override
+ {
+ if ( enable )
+ time_profiling_start_cmd();
+ else
+ time_profiling_stop_cmd();
+
+ return true;
+ }
+ const char* stringify() override { return "TIME_PROFILING"; }
+private:
+ bool enable;
+};
+
+static int time_profiling_start(lua_State* L)
+{
+ ControlConn* ctrlcon = ControlConn::query_from_lua(L);
+ if ( TimeProfilerStats::is_enabled() )
+ {
+ LogRespond(ctrlcon, "Time profiling is already started.\n");
+ return 0;
+ }
+ Profiler::reset_stats(snort::PROFILER_TYPE_TIME);
+ TimeProfilerStats::set_enabled(true);
+ main_broadcast_command(new ProfilerTimeCmd(true), ctrlcon);
+ LogRespond(ctrlcon, "Time profiling is started.\n");
+ return 0;
+}
+
+static int time_profiling_stop(lua_State* L)
+{
+ ControlConn* ctrlcon = ControlConn::query_from_lua(L);
+ if ( !TimeProfilerStats::is_enabled() )
+ {
+ LogRespond(ctrlcon, "Time profiling is not started.\n");
+ return 0;
+ }
+
+ TimeProfilerStats::set_enabled(false);
+ main_broadcast_command(new ProfilerTimeCmd(false), ctrlcon);
+ LogRespond(ctrlcon, "Time profiling is stopped.\n");
+ return 0;
+}
+
+static int time_profiling_dump(lua_State* L)
+{
+ ControlConn* ctrlcon = ControlConn::query_from_lua(L);
+ if ( TimeProfilerStats::is_enabled() )
+ {
+ LogRespond(ctrlcon, "Time profiling is still running.\n");
+ return 0;
+ }
+
+ Profiler::prepare_stats();
+
+ const auto* config = SnortConfig::get_conf()->get_profiler();
+ assert(config);
+
+ print_time_profiler_stats(Profiler::get_profiler_nodes(), config->time, ctrlcon);
+
+ return 0;
+}
+
+static int time_profiling_status(lua_State* L)
+{
+ ControlConn* ctrlcon = ControlConn::query_from_lua(L);
+
+ if ( TimeProfilerStats::is_enabled() )
+ LogRespond(ctrlcon, "Time profiling is enabled.\n");
+ else
+ LogRespond(ctrlcon, "Time profiling is disabled.\n");
+
+ return 0;
+}
+
static const Command profiler_cmds[] =
{
{ "rule_start", rule_profiling_start,
{ "rule_dump", rule_profiling_dump,
profiler_dump_params, "print rule statistics in table or json format (json format prints dates as Unix epoch)" },
+ { "module_start", time_profiling_start,
+ nullptr, "enable module time profiling" },
+
+ { "module_stop", time_profiling_stop,
+ nullptr, "disable module time profiling" },
+
+ { "module_dump", time_profiling_dump,
+ nullptr, "print module time profiling statistics" },
+
+ { "module_status", time_profiling_status,
+ nullptr, "show module time profiler status" },
+
{ nullptr, nullptr, nullptr, nullptr }
};
class ProfilerReloadTuner : public snort::ReloadResourceTuner
{
public:
- explicit ProfilerReloadTuner(bool enable) : enable(enable)
+ explicit ProfilerReloadTuner(bool enable_rule, bool enable_time)
+ : enable_rule(enable_rule), enable_time(enable_time)
{}
~ProfilerReloadTuner() override = default;
bool tinit() override
{
- RuleContext::set_enabled(enable);
+ RuleContext::set_enabled(enable_rule);
+
+ if ( enable_time && !TimeProfilerStats::is_enabled() )
+ time_profiling_start_cmd();
+
+ else if ( !enable_time && TimeProfilerStats::is_enabled() )
+ time_profiling_stop_cmd();
+
return false;
}
{ return true; }
private:
- bool enable = false;
+ bool enable_rule = false;
+ bool enable_time = false;
};
template<typename T>
RuleContext::set_enabled(sc->profiler->rule.show);
if ( Snort::is_reloading() && strcmp(fqn, "profiler") == 0 )
- sc->register_reload_handler(new ProfilerReloadTuner(sc->profiler->rule.show));
+ sc->register_reload_handler(new ProfilerReloadTuner(sc->profiler->rule.show,
+ sc->profiler->time.show));
return true;
}
GetProfileFunctor(const std::string& name) : name(name) { }
virtual ~GetProfileFunctor() = default;
- virtual const ProfileStats* operator()() = 0;
+ virtual ProfileStats* operator()() = 0;
const std::string name;
};
GetProfileFromModule(const std::string& pn, Module* m) :
GetProfileFunctor(pn), m(m) { }
- const ProfileStats* operator()() override
+ ProfileStats* operator()() override
{
// const auto *ps = m->get_profiler_stats();
- const auto *ps = m->get_profile();
+ auto *ps = m->get_profile();
if ( ps )
return ps;
GetProfileFromFunction(const std::string& pn, get_profile_stats_fn fn) :
GetProfileFunctor(pn), fn(fn) { }
- const ProfileStats* operator()() override
+ ProfileStats* operator()() override
{ return fn(name.c_str()); }
get_profile_stats_fn fn;
void ProfilerNode::set(get_profile_stats_fn fn)
{ getter = std::make_shared<GetProfileFromFunction>(name, fn); }
-void ProfilerNode::accumulate()
+void ProfilerNode::accumulate(snort::ProfilerType type)
{
if ( is_set() )
{
return;
get_stats();
- stats += *local_stats;
+
+ if ( type == snort::PROFILER_TYPE_TIME )
+ stats += local_stats->time;
+ else if ( type == snort::PROFILER_TYPE_MEMORY )
+ stats += local_stats->memory;
+ else
+ stats += *local_stats;
+ }
+}
+
+void ProfilerNode::reset(ProfilerType type)
+{
+ if ( is_set() )
+ {
+ auto* local_stats = (*getter)();
+
+ if ( !local_stats )
+ return;
+
+ if ( type == snort::PROFILER_TYPE_TIME )
+ {
+ stats.reset_time();
+ local_stats->reset_time();
+ }
+ else
+ {
+ stats.reset();
+ local_stats->reset();
+ }
}
}
void ProfilerNodeMap::register_node(const std::string &n, const char* pn, Module* m)
{ setup_node(get_node(n), get_node(pn ? pn : ROOT_NODE), m); }
-void ProfilerNodeMap::accumulate_nodes()
+void ProfilerNodeMap::accumulate_nodes(ProfilerType type)
{
static std::mutex stats_mutex;
std::lock_guard<std::mutex> lock(stats_mutex);
for ( auto it = nodes.begin(); it != nodes.end(); ++it )
- it->second.accumulate();
+ it->second.accumulate(type);
}
void ProfilerNodeMap::accumulate_flex()
it->second.accumulate();
}
-void ProfilerNodeMap::reset_nodes()
+void ProfilerNodeMap::clear_flex()
{
- for ( auto it = nodes.begin(); it != nodes.end(); ++it )
+ auto it = nodes.find(FLEX_NODE);
+
+ if ( it != nodes.end() )
it->second.reset();
}
+void ProfilerNodeMap::reset_nodes(ProfilerType type)
+{
+ static std::mutex reset_mutex;
+ std::lock_guard<std::mutex> lock(reset_mutex);
+ for ( auto it = nodes.begin(); it != nodes.end(); ++it )
+ it->second.reset(type);
+}
+
const ProfilerNode& ProfilerNodeMap::get_root()
{ return get_node(ROOT_NODE); }
{ return bool(getter); }
// thread local call
- void accumulate();
+ void accumulate(snort::ProfilerType = snort::PROFILER_TYPE_BOTH);
const snort::ProfileStats& get_stats() const
{ return stats; }
void set_stats(const snort::ProfileStats& ps)
{ stats = ps; }
- void reset()
- { stats.reset(); }
+ void reset(snort::ProfilerType = snort::PROFILER_TYPE_BOTH);
void add_child(ProfilerNode* node)
{ children.emplace_back(node); }
void register_node(const std::string&, const char*, snort::Module*);
- void accumulate_nodes();
+ void accumulate_nodes(snort::ProfilerType = snort::PROFILER_TYPE_BOTH);
void accumulate_flex();
- void reset_nodes();
+ void clear_flex();
+ void reset_nodes(snort::ProfilerType = snort::PROFILER_TYPE_BOTH);
const ProfilerNode& get_root();
#include "profiler_stats_table.h"
#include "profiler_tree_builder.h"
+class ControlConn;
template<typename View>
struct ProfilerSorter
{
using Entry = typename ProfilerBuilder<View>::Entry;
using Sorter = ProfilerSorter<View>;
using PrintFn = std::function<void(StatsTable&, const View&)>;
+ using PrintTableFn = std::function<void(const char*)>;
- ProfilerPrinter(const StatsTable::Field* fields, const PrintFn print, const Sorter& sort) :
- fields(fields), print(print), sort(sort) { }
+ ProfilerPrinter(const StatsTable::Field* fields, const PrintFn print, const Sorter& sort,
+ const PrintTableFn print_t) : fields(fields), print(print), sort(sort), print_t(print_t){ }
void print_table(const std::string& title, Entry& root, unsigned count, int max_depth = -1)
{
table << StatsTable::HEADER;
}
- snort::LogMessage("%s", ss.str().c_str());
+ print_t(ss.str().c_str());
print_recursive(root, root, 1, count, max_depth);
print_row(root, root, 0, 0);
}
}
- snort::LogMessage("%s", ss.str().c_str());
+ print_t(ss.str().c_str());
}
private:
const PrintFn print;
const Sorter& sort;
float total = 0;
+ const PrintTableFn print_t;
};
#endif
#include "profiler_printer.h"
#include "profiler_stats_table.h"
#include "time_profiler_defs.h"
+#include "control/control.h"
#ifdef UNIT_TEST
#include "catch/snort_catch.h"
// enabled is not in SnortConfig to avoid that ugly dependency
// enabled is not in TimeContext because declaring it SO_PUBLIC made TimeContext visible
// putting enabled in TimeProfilerStats seems to be the best solution
-bool TimeProfilerStats::enabled = false;
+THREAD_LOCAL bool TimeProfilerStats::enabled = false;
namespace time_stats
{
t << clock_usecs(TO_USECS(v.avg_check()));
}
+struct s_print_table
+{
+ ControlConn* ctrlcon;
+
+ s_print_table(ControlConn* ctrlcon) : ctrlcon(ctrlcon) {}
+
+ void operator()(const char* l) const
+ { LogRespond(ctrlcon, "%s", l); }
+};
+
} // namespace time_stats
void show_time_profiler_stats(ProfilerNodeMap& nodes, const TimeProfilerConfig& config)
{
- if ( !config.show )
+ if ( !TimeProfilerStats::is_enabled() )
return;
+ print_time_profiler_stats(nodes, config, nullptr);
+}
+
+void print_time_profiler_stats(ProfilerNodeMap& nodes, const TimeProfilerConfig& config, ControlConn* ctrlcon)
+{
ProfilerBuilder<time_stats::View> builder(time_stats::include_fn);
auto root = builder.build(nodes.get_root());
return;
const auto& sorter = time_stats::sorters[config.sort];
+ const auto& printer_t = time_stats::s_print_table(ctrlcon);
- ProfilerPrinter<time_stats::View> printer(time_stats::fields, time_stats::print_fn, sorter);
+ ProfilerPrinter<time_stats::View> printer(time_stats::fields, time_stats::print_fn, sorter, printer_t);
printer.print_table(s_time_table_title, root, config.count, config.max_depth);
}
class ProfilerNodeMap;
struct TimeProfilerConfig;
+class ControlConn;
void show_time_profiler_stats(ProfilerNodeMap&, const TimeProfilerConfig&);
+void print_time_profiler_stats(ProfilerNodeMap&, const TimeProfilerConfig&, ControlConn*);
#endif
hr_duration elapsed;
uint64_t checks;
mutable unsigned int ref_count;
- static bool enabled;
+ static THREAD_LOCAL bool enabled;
static void set_enabled(bool b)
{ enabled = b; }