From: Russ Combs (rucombs) Date: Tue, 27 Oct 2015 22:02:52 +0000 (-0400) Subject: Merge pull request #86 in SNORT/snort3 from oo_perf_profiler to master X-Git-Tag: 3.0.0-233~765 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0b00e4affbec4078df6c547eb174123f337d66cc;p=thirdparty%2Fsnort3.git Merge pull request #86 in SNORT/snort3 from oo_perf_profiler to master Squashed commit of the following: commit 5e0f250e8dd35b005b8d2ae375b0b57500d724fb Author: Joel Cornett Date: Tue Oct 27 16:57:35 2015 -0400 added ncrulePerfStats back as ruleNFPEvalPerfStats commit 40027f80c681c5cac953aa382cffdd36d9f8fcfa Author: Joel Cornett Date: Tue Oct 27 16:05:41 2015 -0400 Fixes per review comments and discussions Fixed ModStatsNode to *not* sum its child stats Moved rebuilt packet detection from stream_tcp to detect commit 5c2f4cefc2450c1c9dbb4d0185a285c8e95727cb Author: Joel Cornett Date: Mon Oct 26 13:32:22 2015 -0400 changed "Pct of..." field names to %/... to make fields easier to parse commit cd17b80620d6da228a5a341472617a9100869691 Author: Joel Cornett Date: Fri Oct 23 12:57:23 2015 -0400 updated per review comments 10/23 added some more unit tests added profiler macros/ifdefs to silence unused variable warnings changed ProfileConfig::num -> ProfileConfig::count and deleted PerfProfilerManager::init and PerfProfilerManager::term added more test cases/fixes for ModEntry commit 862758de4ccca4cfcafbd61195b5137661a443a6 Author: Joel Cornett Date: Thu Oct 22 21:15:24 2015 -0400 changed module stats printout to use a tree all the way through to allow multi-layered count output cleaned up includes, fixed some bugs, stats *look* correct fixed broken include dependencies revealed when cleaning up profiler.h headers minor fixes commit afbf334c3ee94c2534440ca967f268ec253b6f5b Author: Joel Cornett Date: Thu Oct 22 15:23:25 2015 -0400 successfully ported counting functionality of legacy code to new code. TODO: add/test sort functionality, verify that module stats is working correctly 99% ported. still working out the kinks in sorting module stats further cleanup commit c44712fadeda22eddaeb9ca2d9b7dab798e88ca0 Author: Joel Cornett Date: Wed Oct 21 21:23:40 2015 -0400 major rewrite, fixed totalling issue food some stuff up reverted most of 3c224 and 40ecf cleaned up legacy profiler code to prepare for porting commit f7239e47656cfa1dd293d75a7bb29ca86d0cfcd5 Author: Joel Cornett Date: Fri Oct 16 14:40:36 2015 -0400 rewrote module perf stats code removed legacy code added underscores in module names --- diff --git a/src/detection/detect.cc b/src/detection/detect.cc index 631f4c568..c079dc4a4 100644 --- a/src/detection/detect.cc +++ b/src/detection/detect.cc @@ -71,6 +71,7 @@ #ifdef PERF_PROFILING THREAD_LOCAL ProfileStats detectPerfStats; THREAD_LOCAL ProfileStats eventqPerfStats; +THREAD_LOCAL ProfileStats rebuiltPacketPerfStats; #endif THREAD_LOCAL int do_detect; diff --git a/src/detection/detect.h b/src/detection/detect.h index 48d7e0d9b..c8056dfdf 100644 --- a/src/detection/detect.h +++ b/src/detection/detect.h @@ -41,6 +41,7 @@ extern SO_PUBLIC THREAD_LOCAL int do_detect_content; #ifdef PERF_PROFILING extern THREAD_LOCAL ProfileStats eventqPerfStats; extern THREAD_LOCAL ProfileStats detectPerfStats; +extern THREAD_LOCAL ProfileStats rebuiltPacketPerfStats; #endif // main loop hooks diff --git a/src/detection/detection_options.cc b/src/detection/detection_options.cc index adac5a67c..b65dbb493 100644 --- a/src/detection/detection_options.cc +++ b/src/detection/detection_options.cc @@ -386,8 +386,11 @@ int detection_option_node_evaluate( return 0; dot_node_state_t* state = node->state + get_instance_id(); + +#ifdef PERF_PROFILING auto& node_stats = *state; NODE_PERF_PROFILE(node_stats); +#endif int result = 0; int rval = DETECTION_OPTION_NO_MATCH; @@ -611,8 +614,10 @@ int detection_option_node_evaluate( { // bail if we exceeded time +#ifdef PERF_PROFILING if ( result != DETECTION_OPTION_NO_MATCH ) NODE_PERF_PROFILE_STOP_MATCH(node_stats); +#endif state->last_check.result = result; return result; diff --git a/src/detection/fp_detect.cc b/src/detection/fp_detect.cc index 70a1acaad..96136aeb4 100644 --- a/src/detection/fp_detect.cc +++ b/src/detection/fp_detect.cc @@ -78,9 +78,9 @@ #ifdef PERF_PROFILING THREAD_LOCAL ProfileStats rulePerfStats; -THREAD_LOCAL ProfileStats ncrulePerfStats; THREAD_LOCAL ProfileStats ruleRTNEvalPerfStats; THREAD_LOCAL ProfileStats ruleOTNEvalPerfStats; +THREAD_LOCAL ProfileStats ruleNFPEvalPerfStats; #endif THREAD_LOCAL uint64_t rule_eval_pkt_count = 0; @@ -371,6 +371,7 @@ int fpAddMatch(OTNX_MATCH_DATA* omd_local, int pLen, const OptTreeNode* otn) */ int fpEvalRTN(RuleTreeNode* rtn, Packet* p, int check_ports) { + PERF_PROFILE(rulePerfStats); PERF_PROFILE(ruleRTNEvalPerfStats); if ( !rtn ) @@ -405,12 +406,9 @@ static int detection_option_tree_evaluate( detection_option_tree_root_t* root, detection_option_eval_data_t* eval_data) { - PERF_PROFILE(ruleOTNEvalPerfStats); - if (!root) return 0; - #ifdef PPM_MGR /* Start Rule Timer */ if ( PPM_RULES_ENABLED() ) @@ -501,7 +499,13 @@ static int rule_tree_match(void* id, void* tree, int index, void* data, void* ne last_check->rebuild_flag = (eval_data.p->packet_flags & PKT_REBUILT_STREAM); } - if ( detection_option_tree_evaluate(root, &eval_data) ) + int ret = 0; + PERF_PROFILE_BLOCK(ruleOTNEvalPerfStats) + { + ret = detection_option_tree_evaluate(root, &eval_data); + } + + if ( ret ) { // We have a qualified event from this tree pomd->pg->event_count++; @@ -1026,12 +1030,11 @@ static inline int fpEvalHeaderSW(PortGroup* port_group, Packet* p, eval_data.flowbit_noalert = 0; int rval = 0; - - PERF_PROFILE_BLOCK(ncrulePerfStats) + PERF_PROFILE_BLOCK(rulePerfStats) + PERF_PROFILE_BLOCK(ruleNFPEvalPerfStats) { rval = detection_option_tree_evaluate( - (detection_option_tree_root_t*)port_group->nfp_tree, - &eval_data); + (detection_option_tree_root_t*)port_group->nfp_tree, &eval_data); } if (rval) diff --git a/src/detection/fp_detect.h b/src/detection/fp_detect.h index cbd23edd6..e92a56b90 100644 --- a/src/detection/fp_detect.h +++ b/src/detection/fp_detect.h @@ -43,9 +43,9 @@ #ifdef PERF_PROFILING extern THREAD_LOCAL ProfileStats rulePerfStats; -extern THREAD_LOCAL ProfileStats ncrulePerfStats; extern THREAD_LOCAL ProfileStats ruleRTNEvalPerfStats; extern THREAD_LOCAL ProfileStats ruleOTNEvalPerfStats; +extern THREAD_LOCAL ProfileStats ruleNFPEvalPerfStats; #endif /* diff --git a/src/ips_options/ips_hash.cc b/src/ips_options/ips_hash.cc index 53052938d..302403857 100644 --- a/src/ips_options/ips_hash.cc +++ b/src/ips_options/ips_hash.cc @@ -199,8 +199,7 @@ int HashOption::match(Cursor& c) int HashOption::eval(Cursor& c, Packet*) { - auto& hash_option_stats = hash_ps[idx]; - PERF_PROFILE(hash_option_stats); + PERF_PROFILE_THREAD_LOCAL(hash_ps, idx); int found = match(c); diff --git a/src/ips_options/ips_http.cc b/src/ips_options/ips_http.cc index afc77b459..1e6f36edc 100644 --- a/src/ips_options/ips_http.cc +++ b/src/ips_options/ips_http.cc @@ -93,8 +93,7 @@ private: int HttpIpsOption::eval(Cursor& c, Packet* p) { - auto& http_option_stats = http_ps[idx]; - PERF_PROFILE(http_option_stats); + PERF_PROFILE_THREAD_LOCAL(http_ps, idx); InspectionBuffer hb; diff --git a/src/ips_options/ips_http_header.cc b/src/ips_options/ips_http_header.cc index 3bba43a4a..7a5b8ef0e 100644 --- a/src/ips_options/ips_http_header.cc +++ b/src/ips_options/ips_http_header.cc @@ -34,6 +34,7 @@ using namespace std; #include "framework/cursor.h" #include "framework/inspector.h" #include "framework/module.h" +#include "protocols/packet.h" #define s_name "http_header" diff --git a/src/ips_options/ips_luajit.cc b/src/ips_options/ips_luajit.cc index 8ca2cd850..96a34ee8d 100644 --- a/src/ips_options/ips_luajit.cc +++ b/src/ips_options/ips_luajit.cc @@ -29,8 +29,10 @@ #include "hash/sfhashfcn.h" #include "parser/parser.h" #include "framework/cursor.h" +#include "framework/decode_data.h" #include "framework/module.h" #include "framework/parameter.h" +#include "log/messages.h" #include "time/profiler.h" #include "detection/detection_defines.h" diff --git a/src/ips_options/ips_replace.cc b/src/ips_options/ips_replace.cc index 661daa8d5..d354519dc 100644 --- a/src/ips_options/ips_replace.cc +++ b/src/ips_options/ips_replace.cc @@ -27,6 +27,7 @@ using namespace std; #include "ips_content.h" +#include "main/snort_config.h" #include "main/snort_types.h" #include "main/snort_debug.h" #include "utils/snort_bounds.h" diff --git a/src/main/modules.cc b/src/main/modules.cc index d4e2c9435..2a493d3a4 100644 --- a/src/main/modules.cc +++ b/src/main/modules.cc @@ -367,10 +367,10 @@ public: bool ProfileModule::begin(const char* fqn, int, SnortConfig* sc) { if ( !strcmp(fqn, "profile.rules") ) - sc->profile_rules->num = -1; + sc->profile_rules->count = -1; else if ( !strcmp(fqn, "profile.modules") ) - sc->profile_modules->num = -1; + sc->profile_modules->count = -1; return true; } @@ -391,10 +391,10 @@ bool ProfileModule::set(const char* fqn, Value& v, SnortConfig* sc) return false; if ( v.is("count") ) - p->num = v.get_long(); + p->count = v.get_long(); else if ( v.is("sort") ) - p->sort = v.get_long() + 1; + p->sort = static_cast(v.get_long() + 1); else return false; diff --git a/src/main/snort.cc b/src/main/snort.cc index 84dff7403..fe7b7689d 100644 --- a/src/main/snort.cc +++ b/src/main/snort.cc @@ -133,15 +133,21 @@ static ProfileStats* get_profile(const char* key) if ( !strcmp(key, "mpse") ) return &mpsePerfStats; - if ( !strcmp(key, "rule eval") ) + if ( !strcmp(key, "rebuilt_packet") ) + return &rebuiltPacketPerfStats; + + if ( !strcmp(key, "rule_eval") ) return &rulePerfStats; - if ( !strcmp(key, "rtn eval") ) + if ( !strcmp(key, "rtn_eval") ) return &ruleRTNEvalPerfStats; - if ( !strcmp(key, "rule tree eval") ) + if ( !strcmp(key, "rule_tree_eval") ) return &ruleOTNEvalPerfStats; + if ( !strcmp(key, "nfp_rule_tree_eval") ) + return &ruleNFPEvalPerfStats; + if ( !strcmp(key, "decode") ) return &decodePerfStats; @@ -151,7 +157,7 @@ static ProfileStats* get_profile(const char* key) if ( !strcmp(key, "total") ) return &totalPerfStats; - if ( !strcmp(key, "daq meta") ) + if ( !strcmp(key, "daq_meta") ) return &metaPerfStats; return nullptr; @@ -164,13 +170,15 @@ static void register_profiles() #ifdef PERF_PROFILING PerfProfilerManager::register_module("detect", nullptr, get_profile); PerfProfilerManager::register_module("mpse", "detect", get_profile); - PerfProfilerManager::register_module("rule eval", "detect", get_profile); - PerfProfilerManager::register_module("rtn eval", "rule eval", get_profile); - PerfProfilerManager::register_module("rule tree eval", "rule eval", get_profile); + PerfProfilerManager::register_module("rebuilt_packet", "detect", get_profile); + PerfProfilerManager::register_module("rule_eval", "detect", get_profile); + PerfProfilerManager::register_module("rtn_eval", "rule_eval", get_profile); + PerfProfilerManager::register_module("rule_tree_eval", "rule_eval", get_profile); + PerfProfilerManager::register_module("nfp_rule_tree_eval", "rule_eval", get_profile); PerfProfilerManager::register_module("decode", nullptr, get_profile); PerfProfilerManager::register_module("eventq", nullptr, get_profile); PerfProfilerManager::register_module("total", nullptr, get_profile); - PerfProfilerManager::register_module("daq meta", nullptr, get_profile); + PerfProfilerManager::register_module("daq_meta", nullptr, get_profile); #endif } @@ -404,10 +412,6 @@ void Snort::term() periodic_release(); -#ifdef PERF_PROFILING - PerfProfilerManager::term(); -#endif - /* free allocated memory */ if (snort_conf == snort_cmd_line_conf) { @@ -685,6 +689,10 @@ void Snort::thread_term() void Snort::detect_rebuilt_packet(Packet* p) { + // Need to include this b/c call is outside the detect tree + PERF_PROFILE(detectPerfStats); + PERF_PROFILE(rebuiltPacketPerfStats); + int tmp_do_detect = do_detect; int tmp_do_detect_content = do_detect_content; @@ -796,7 +804,6 @@ DAQ_Verdict Snort::packet_callback( { PERF_PROFILE(totalPerfStats); - pc.total_from_daq++; rule_eval_pkt_count++; packet_time_update(&pkthdr->ts); diff --git a/src/network_inspectors/arp_spoof/arp_spoof.cc b/src/network_inspectors/arp_spoof/arp_spoof.cc index 1d2c96219..a797c2321 100644 --- a/src/network_inspectors/arp_spoof/arp_spoof.cc +++ b/src/network_inspectors/arp_spoof/arp_spoof.cc @@ -66,6 +66,8 @@ */ /* I N C L U D E S ************************************************/ +#include "arp_module.h" + #ifdef HAVE_CONFIG_H #include "config.h" #endif @@ -76,11 +78,11 @@ #include #include -#include "arp_module.h" #include "main/snort_types.h" #include "main/snort_debug.h" #include "detection/detect.h" #include "events/event.h" +#include "events/event_queue.h" #include "parser/parser.h" #include "utils/util.h" #include "time/profiler.h" diff --git a/src/network_inspectors/perf_monitor/perf_base.cc b/src/network_inspectors/perf_monitor/perf_base.cc index 58f36a9e1..839a75b4a 100644 --- a/src/network_inspectors/perf_monitor/perf_base.cc +++ b/src/network_inspectors/perf_monitor/perf_base.cc @@ -55,7 +55,7 @@ #include #include "perf.h" -#include "main/snort_types.h" +#include "main/snort_config.h" #include "main/snort_types.h" #include "framework/mpse.h" #include "packet_io/sfdaq.h" diff --git a/src/network_inspectors/perf_monitor/perf_monitor.cc b/src/network_inspectors/perf_monitor/perf_monitor.cc index cd5ab6d7d..ef898ebc4 100644 --- a/src/network_inspectors/perf_monitor/perf_monitor.cc +++ b/src/network_inspectors/perf_monitor/perf_monitor.cc @@ -41,6 +41,7 @@ #include "perf_module.h" #include "main/analyzer.h" +#include "main/snort_config.h" #include "main/snort_types.h" #include "main/snort_debug.h" #include "parser/parser.h" diff --git a/src/parser/config_file.cc b/src/parser/config_file.cc index 22ab4f344..45987ce43 100644 --- a/src/parser/config_file.cc +++ b/src/parser/config_file.cc @@ -41,6 +41,7 @@ #include "cmd_line.h" #include "mstring.h" +#include "main/snort_config.h" #include "main/snort_types.h" #include "main/snort_debug.h" #include "main/snort.h" diff --git a/src/service_inspectors/back_orifice/back_orifice.cc b/src/service_inspectors/back_orifice/back_orifice.cc index 647702fe8..17e35ee02 100644 --- a/src/service_inspectors/back_orifice/back_orifice.cc +++ b/src/service_inspectors/back_orifice/back_orifice.cc @@ -120,6 +120,7 @@ #include "main/snort_debug.h" #include "detection/detect.h" #include "events/event.h" +#include "events/event_queue.h" #include "parser/parser.h" #include "utils/util.h" #include "time/profiler.h" diff --git a/src/service_inspectors/dns/dns.cc b/src/service_inspectors/dns/dns.cc index 68e2a9e6b..ffbbe5ec4 100644 --- a/src/service_inspectors/dns/dns.cc +++ b/src/service_inspectors/dns/dns.cc @@ -32,6 +32,7 @@ #include #include +#include "events/event_queue.h" #include "main/snort_types.h" #include "main/snort_debug.h" #include "time/profiler.h" diff --git a/src/service_inspectors/gtp/gtp_inspect.cc b/src/service_inspectors/gtp/gtp_inspect.cc index 3c63e05b7..8bc74f45e 100644 --- a/src/service_inspectors/gtp/gtp_inspect.cc +++ b/src/service_inspectors/gtp/gtp_inspect.cc @@ -26,6 +26,7 @@ #endif #include "managers/inspector_manager.h" +#include "protocols/packet.h" #include "time/profiler.h" #include "gtp.h" diff --git a/src/service_inspectors/gtp/gtp_module.cc b/src/service_inspectors/gtp/gtp_module.cc index e9608399b..024b9a742 100644 --- a/src/service_inspectors/gtp/gtp_module.cc +++ b/src/service_inspectors/gtp/gtp_module.cc @@ -20,6 +20,8 @@ #include "gtp_module.h" +#include + #include "gtp.h" #include "time/profiler.h" diff --git a/src/service_inspectors/gtp/ips_gtp_info.cc b/src/service_inspectors/gtp/ips_gtp_info.cc index 46f757a8a..5119d6c5a 100644 --- a/src/service_inspectors/gtp/ips_gtp_info.cc +++ b/src/service_inspectors/gtp/ips_gtp_info.cc @@ -31,6 +31,7 @@ #include "framework/ips_option.h" #include "framework/module.h" #include "hash/sfhashfcn.h" +#include "protocols/packet.h" #include "time/profiler.h" #include "gtp.h" diff --git a/src/service_inspectors/gtp/ips_gtp_type.cc b/src/service_inspectors/gtp/ips_gtp_type.cc index 987e206fc..a02160830 100644 --- a/src/service_inspectors/gtp/ips_gtp_type.cc +++ b/src/service_inspectors/gtp/ips_gtp_type.cc @@ -31,6 +31,7 @@ #include "framework/ips_option.h" #include "framework/module.h" #include "hash/sfhashfcn.h" +#include "protocols/packet.h" #include "time/profiler.h" #include "gtp.h" diff --git a/src/service_inspectors/gtp/ips_gtp_version.cc b/src/service_inspectors/gtp/ips_gtp_version.cc index dc6ae6bd9..ea1547f9e 100644 --- a/src/service_inspectors/gtp/ips_gtp_version.cc +++ b/src/service_inspectors/gtp/ips_gtp_version.cc @@ -30,6 +30,7 @@ #include "framework/ips_option.h" #include "framework/module.h" #include "hash/sfhashfcn.h" +#include "protocols/packet.h" #include "time/profiler.h" #include "gtp_inspect.h" diff --git a/src/service_inspectors/imap/imap.cc b/src/service_inspectors/imap/imap.cc index de59b311e..c96509347 100644 --- a/src/service_inspectors/imap/imap.cc +++ b/src/service_inspectors/imap/imap.cc @@ -29,6 +29,7 @@ #include #include +#include "events/event_queue.h" #include "main/snort_types.h" #include "main/snort_debug.h" #include "time/profiler.h" diff --git a/src/service_inspectors/modbus/ips_modbus_data.cc b/src/service_inspectors/modbus/ips_modbus_data.cc index 64c967d90..ef9aacfc5 100644 --- a/src/service_inspectors/modbus/ips_modbus_data.cc +++ b/src/service_inspectors/modbus/ips_modbus_data.cc @@ -26,6 +26,7 @@ #include "framework/ips_option.h" #include "framework/module.h" #include "hash/sfhashfcn.h" +#include "protocols/packet.h" #include "time/profiler.h" #include "modbus.h" diff --git a/src/service_inspectors/modbus/ips_modbus_func.cc b/src/service_inspectors/modbus/ips_modbus_func.cc index 5a48a9ffb..a2e278e8b 100644 --- a/src/service_inspectors/modbus/ips_modbus_func.cc +++ b/src/service_inspectors/modbus/ips_modbus_func.cc @@ -25,6 +25,7 @@ #include "framework/ips_option.h" #include "framework/module.h" #include "hash/sfhashfcn.h" +#include "protocols/packet.h" #include "time/profiler.h" #include "modbus.h" diff --git a/src/service_inspectors/modbus/ips_modbus_unit.cc b/src/service_inspectors/modbus/ips_modbus_unit.cc index 705aa8e5f..f04642be2 100644 --- a/src/service_inspectors/modbus/ips_modbus_unit.cc +++ b/src/service_inspectors/modbus/ips_modbus_unit.cc @@ -25,6 +25,7 @@ #include "framework/ips_option.h" #include "framework/module.h" #include "hash/sfhashfcn.h" +#include "protocols/packet.h" #include "time/profiler.h" #include "modbus.h" diff --git a/src/service_inspectors/modbus/modbus.cc b/src/service_inspectors/modbus/modbus.cc index 1896bd85c..97a87a2d0 100644 --- a/src/service_inspectors/modbus/modbus.cc +++ b/src/service_inspectors/modbus/modbus.cc @@ -24,6 +24,7 @@ #include "config.h" #endif +#include "events/event_queue.h" #include "managers/inspector_manager.h" #include "time/profiler.h" diff --git a/src/service_inspectors/pop/pop.cc b/src/service_inspectors/pop/pop.cc index 29d0eda19..9aa82f1b1 100644 --- a/src/service_inspectors/pop/pop.cc +++ b/src/service_inspectors/pop/pop.cc @@ -27,6 +27,7 @@ #include #include +#include "events/event_queue.h" #include "main/snort_types.h" #include "main/snort_debug.h" #include "time/profiler.h" diff --git a/src/service_inspectors/sip/ips_sip.cc b/src/service_inspectors/sip/ips_sip.cc index ee73b7f01..efb8d13b3 100644 --- a/src/service_inspectors/sip/ips_sip.cc +++ b/src/service_inspectors/sip/ips_sip.cc @@ -97,8 +97,7 @@ private: int SipIpsOption::eval(Cursor& c, Packet* p) { - auto& sip_stats = sip_ps[idx]; - PERF_PROFILE(sip_stats); + PERF_PROFILE_THREAD_LOCAL(sip_ps, idx); SIPData* sd; SIP_Roptions* ropts; diff --git a/src/service_inspectors/ssh/ssh.cc b/src/service_inspectors/ssh/ssh.cc index 35319d7ad..87a6b3a59 100644 --- a/src/service_inspectors/ssh/ssh.cc +++ b/src/service_inspectors/ssh/ssh.cc @@ -31,6 +31,7 @@ #include #include +#include "events/event_queue.h" #include "main/snort_types.h" #include "main/snort_debug.h" #include "time/profiler.h" diff --git a/src/service_inspectors/ssl/ssl_inspector.cc b/src/service_inspectors/ssl/ssl_inspector.cc index a66a1b8c4..5f8e19c76 100644 --- a/src/service_inspectors/ssl/ssl_inspector.cc +++ b/src/service_inspectors/ssl/ssl_inspector.cc @@ -33,6 +33,7 @@ #include #include +#include "events/event_queue.h" #include "main/snort_types.h" #include "main/snort_debug.h" #include "time/profiler.h" diff --git a/src/stream/tcp/ips_stream_reassemble.cc b/src/stream/tcp/ips_stream_reassemble.cc index 68ae36e00..003ca410c 100644 --- a/src/stream/tcp/ips_stream_reassemble.cc +++ b/src/stream/tcp/ips_stream_reassemble.cc @@ -22,6 +22,8 @@ #endif #include "tcp_session.h" + +#include "main/snort_config.h" #include "stream/stream_splitter.h" #include "framework/ips_option.h" #include "framework/module.h" diff --git a/src/stream/tcp/tcp_module.cc b/src/stream/tcp/tcp_module.cc index 66ac85178..84601a05b 100644 --- a/src/stream/tcp/tcp_module.cc +++ b/src/stream/tcp/tcp_module.cc @@ -38,7 +38,6 @@ THREAD_LOCAL ProfileStats s5TcpInsertPerfStats; THREAD_LOCAL ProfileStats s5TcpPAFPerfStats; THREAD_LOCAL ProfileStats s5TcpFlushPerfStats; THREAD_LOCAL ProfileStats s5TcpBuildPacketPerfStats; -THREAD_LOCAL ProfileStats s5TcpProcessRebuiltPerfStats; const PegInfo tcp_pegs[] = { { "sessions", "total sessions" }, @@ -262,11 +261,6 @@ ProfileStats* StreamTcpModule::get_profile( name = "tcpBuildPacket"; parent = "tcpFlush"; return &s5TcpBuildPacketPerfStats; - - case 8: - name = "tcpProcessRebuilt"; - parent = "tcpFlush"; - return &s5TcpProcessRebuiltPerfStats; } return nullptr; } diff --git a/src/stream/tcp/tcp_module.h b/src/stream/tcp/tcp_module.h index 11fec0803..2ca30f951 100644 --- a/src/stream/tcp/tcp_module.h +++ b/src/stream/tcp/tcp_module.h @@ -63,7 +63,6 @@ extern THREAD_LOCAL ProfileStats s5TcpInsertPerfStats; extern THREAD_LOCAL ProfileStats s5TcpPAFPerfStats; extern THREAD_LOCAL ProfileStats s5TcpFlushPerfStats; extern THREAD_LOCAL ProfileStats s5TcpBuildPacketPerfStats; -extern THREAD_LOCAL ProfileStats s5TcpProcessRebuiltPerfStats; extern THREAD_LOCAL ProfileStats streamSizePerfStats; struct TcpStats diff --git a/src/stream/tcp/tcp_reassembler.cc b/src/stream/tcp/tcp_reassembler.cc index fe23ddf56..9e908b551 100644 --- a/src/stream/tcp/tcp_reassembler.cc +++ b/src/stream/tcp/tcp_reassembler.cc @@ -713,7 +713,6 @@ int TcpReassembler::_flush_to_seq( uint32_t bytes, Packet* p, uint32_t pkt_flags UpdateStreamReassStats( &sfBase, flushed_bytes ); PERF_PAUSE_BLOCK(s5TcpFlushPerfStats) - PERF_PROFILE_BLOCK(s5TcpProcessRebuiltPerfStats) { Snort::detect_rebuilt_packet(s5_pkt); } diff --git a/src/time/CMakeLists.txt b/src/time/CMakeLists.txt index 3e1a7c432..45bc1d5b5 100644 --- a/src/time/CMakeLists.txt +++ b/src/time/CMakeLists.txt @@ -1,28 +1,32 @@ - if ( ENABLE_PERFPROFILING ) - set ( PERFPROFILING_SOURCES profiler.cc ) + set ( PROFILER_SOURCES profiler.cc ) endif ( ENABLE_PERFPROFILING ) +set ( PROFILER_INCLUDES profiler.h ) -set (TIME_INCLUDES - cpuclock.h - profiler.h - ppm.h -) +# FIXIT-L Should this be conditionally compiled as well? +set ( PPM_SOURCES ppm.cc ppm_module.cc ppm_module.h ) +set ( PPM_INCLUDES ppm.h ) -add_library( time STATIC - packet_time.cc - packet_time.h - ppm.cc - ppm.h - ppm_module.cc - ppm_module.h - ${PERFPROFILING_SOURCES} - periodic.cc - periodic.h +set ( TIME_INTERNAL_SOURCES + packet_time.cc + packet_time.h + periodic.cc + periodic.h timersub.h - ${TIME_INCLUDES} -) + ) + +set ( TIME_INCLUDES + ${PROFILER_INCLUDES} + ${PPM_INCLUDES} + cpuclock.h + ) +add_library ( time STATIC + ${PROFILER_SOURCES} + ${PPM_SOURCES} + ${TIME_INTERNAL_SOURCES} + ${TIME_INCLUDES} + ) install (FILES ${TIME_INCLUDES} DESTINATION "${INCLUDE_INSTALL_PATH}/time" diff --git a/src/time/ppm.cc b/src/time/ppm.cc index e98a4040e..654316d0c 100644 --- a/src/time/ppm.cc +++ b/src/time/ppm.cc @@ -60,6 +60,7 @@ #include "detection/fp_create.h" #include "detection/detect.h" #include "parser/parser.h" +#include "events/event_queue.h" #include "events/event_wrapper.h" #include "actions/actions.h" #include "protocols/packet.h" diff --git a/src/time/profiler.cc b/src/time/profiler.cc index e47b11f92..9a5ed2b9a 100644 --- a/src/time/profiler.cc +++ b/src/time/profiler.cc @@ -4,7 +4,6 @@ // // This program is free software; you can redistribute it and/or modify it // under the terms of the GNU General Public License Version 2 as published -// by the Free Software Foundation. You may not use, modify or distribute // this program under any other version of the GNU General Public License. // // This program is distributed in the hope that it will be useful, but @@ -16,7 +15,8 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //-------------------------------------------------------------------------- -// Steven Sturges +// profiler.cc author Joel Cornett +// based on work by Steven Sturges #include "profiler.h" @@ -25,212 +25,393 @@ #endif #include -#include -#include #include -#include +#include #include +#include +#include -#include "time/cpuclock.h" #include "detection/fp_detect.h" #include "detection/treenodes.h" -#include "detection/rules.h" #include "detection/detection_options.h" -#include "parser/parser.h" -#include "utils/util.h" -#include "main/snort_types.h" #include "framework/module.h" #include "hash/sfghash.h" +#include "main/snort_config.h" +#include "parser/parser.h" #ifdef UNIT_TEST #include "catch/catch.hpp" #endif -using namespace std; - #define TOTAL "total" // ----------------------------------------------------------------------------- // types // ----------------------------------------------------------------------------- -// ----------------------------------------------------------------------------- -// static variables -// ----------------------------------------------------------------------------- +class ModStatsFunctor +{ +public: + const ProfileStats* operator()(const std::string&); -// ----------------------------------------------------------------------------- -// static functions -// ----------------------------------------------------------------------------- + bool is_set() const + { return type != NONE; } -// FIXIT-L legacy stuff (to be cleaned up) -typedef struct _ProfileStatsNode -{ - ProfileStats stats; - const char* name; - const char* pname; - Module* owner; - int layer; - struct _ProfileStatsNode* next; - struct _ProfileStatsNode* parent; - get_profile_func get_data; -} ProfileStatsNode; - -typedef struct _OTN_WorstPerformer + void set(Module* m) + { owner = m; type = MODULE; } + + void set(get_profile_func cb) + { callback = cb; type = CALLBACK; } + +private: + enum { NONE, MODULE, CALLBACK } type = NONE; + union + { + Module* owner; + get_profile_func callback; + }; +}; + +class ModStatsNode { - OptTreeNode* otn; - struct _OTN_WorstPerformer* next; - struct _OTN_WorstPerformer* prev; - double ticks_per_check; - double ticks_per_match; - double ticks_per_nomatch; -} OTN_WorstPerformer; +public: + ModStatsNode(const std::string& key) : + name { key } { } + + template + void set(T v) + { getter.set(v); } + + bool is_set() const + { return getter.is_set(); } + + void add_child(ModStatsNode* p) + { children.insert(p); } + + void reset() + { stats.reset(); totalled = false; } + + const ProfileStats& get_total() + { return stats; } -typedef struct _Preproc_WorstPerformer + void accumulate(); + + const std::string name; + std::set children; + +private: + ProfileStats stats { 0, 0 }; + bool totalled = false; + ModStatsFunctor getter; +}; + +struct ModEntry { - ProfileStatsNode* node; - struct _Preproc_WorstPerformer* next; - struct _Preproc_WorstPerformer* prev; - struct _Preproc_WorstPerformer* children; + std::string name; + ModStatsNode* node; + ProfileStats stats; + double ticks_per_check; double pct_of_parent; - double pct_of_total; -} Preproc_WorstPerformer; -static THREAD_LOCAL double ticks_per_microsec = 0.0; + std::vector entries; + + ModEntry(ModStatsNode*, const ProfileStats&); +}; + +using ModEntrySortFunc = std::function; + +// Wraps std::unordered_map with some initialization for operator[] +class ModStatsTree +{ + std::unordered_map nodes; + +public: + ModStatsNode& operator[](std::string key) + { + auto result = nodes.emplace(key, key); + return result.first->second; + } + + auto begin() -> decltype(nodes.begin()) + { return nodes.begin(); } -static OTN_WorstPerformer* worstPerformers = NULL; -static Preproc_WorstPerformer* worstPreprocPerformers = NULL; + auto end() -> decltype(nodes.end()) + { return nodes.end(); } +}; + +struct RuleEntry +{ + SigInfo sig_info; + + OtnState state; + + double ticks_per_check = 0.0; + double ticks_per_match = 0.0; + double ticks_per_nomatch = 0.0; + + RuleEntry(const SigInfo&, const OtnState&); +}; + +using RuleEntrySortFunc = std::function; + +// ----------------------------------------------------------------------------- +// global variables +// ----------------------------------------------------------------------------- THREAD_LOCAL ProfileStats totalPerfStats; THREAD_LOCAL ProfileStats metaPerfStats; -static THREAD_LOCAL ProfileStats* mpsePerfStats; -static ProfileStatsNode* gProfileStatsNodeList = NULL; -static int max_layers = 0; - -static ProfileStatsNode* get_node(const char*); +static ModStatsTree s_module_nodes; +// ----------------------------------------------------------------------------- +// static functions +// ----------------------------------------------------------------------------- -static ProfileStatsNode* get_root(ProfileStatsNode* idx) +static inline OtnState& operator+=(OtnState& lhs, const OtnState& rhs) { - while ( idx->parent ) - idx = idx->parent; - return idx; + lhs.ticks += rhs.ticks; + lhs.ticks_match += rhs.ticks_match; + lhs.ticks_no_match += rhs.ticks_no_match; + lhs.checks += rhs.checks; + lhs.matches += rhs.matches; + lhs.noalerts += rhs.noalerts; + lhs.alerts += rhs.alerts; + return lhs; } -static void set_node(ProfileStatsNode* idx) +static double get_ticks_per_us() { - idx->parent = get_node(idx->pname); + static double ticks_per_us = 0.0; + if ( ticks_per_us == 0.0 ) + ticks_per_us = get_ticks_per_usec(); - if ( idx->pname && strcasecmp(idx->pname, TOTAL) ) - idx->layer = idx->parent->layer + 1; + return ticks_per_us; } -static ProfileStatsNode* get_node(const char* s) +template +static void add_module(std::string name, const char* pname, T v) { - for ( ProfileStatsNode* idx = gProfileStatsNodeList; idx; idx = idx->next ) - { - if ( !strcmp(idx->name, s) ) - { - if ( idx->pname && !idx->parent ) - set_node(idx); + ModStatsNode& node = s_module_nodes[name]; - return idx; - } - } - return nullptr; + assert(!node.is_set()); + node.set(v); + + if ( !pname ) + pname = TOTAL; + + // don't allow parent to be child of self + if ( name == pname ) + return; + + ModStatsNode& parent = s_module_nodes[pname]; + parent.add_child(&node); } -static void link_nodes() +static void get_mod_entries(ModEntry& parent, ModEntrySortFunc* sort_fn, int count) { - for ( ProfileStatsNode* idx = gProfileStatsNodeList; idx; idx = idx->next ) + std::vector entries; + + for ( auto child : parent.node->children ) { - if ( idx->pname && !idx->parent ) - set_node(idx); + if ( bool(child->get_total()) ) + entries.emplace_back(child, parent.stats); + } + + if ( sort_fn ) + std::stable_sort(entries.begin(), entries.end(), *sort_fn); - if ( idx->layer > max_layers) - max_layers = idx->layer; + size_t n = (count < 0) ? entries.size() : static_cast(count); + + for ( size_t i = 0; i < n && i < entries.size(); ++i ) + { + parent.entries.push_back(entries[i]); + get_mod_entries(parent.entries.back(), sort_fn, count); } } -static void getTicksPerMicrosec(void) +static void print_mod_entry(int layer, int num, const ModEntry& root, const ModEntry& cur) { - if (ticks_per_microsec == 0.0) - { - ticks_per_microsec = get_ticks_per_usec(); - } + unsigned indent = 6 - (5 - layer) + 2; + LogMessage("%*d%*s%*d" FMTu64("*") FMTu64("*") "%*.2f%*.2f%*.2f\n", + indent, num, + 28 - indent, cur.name.c_str(), 6, layer, + 11, cur.stats.checks, + 20, uint64_t((double(cur.stats.ticks) / get_ticks_per_us())), + 11, cur.ticks_per_check / get_ticks_per_us(), + 10, cur.pct_of_parent, + 10, double(cur.stats.ticks) / double(root.stats.ticks) * 100.0); + + int num2 = 0; + for ( const auto& entry : cur.entries ) + print_mod_entry(layer + 1, ++num2, root, entry); } -void ResetRuleProfiling(void) +static void print_mod_entries(ModEntry& root, int count) { - /* Cycle through all Rules, print ticks & check count for each */ - RuleTreeNode* rtn; - SFGHASH_NODE* hashNode; - OptTreeNode* otn = NULL; - SnortConfig* sc = snort_conf; + LogMessage("--------------------------------------------------\n"); - if ((sc == NULL) || (sc->profile_rules->num == 0)) - return; + // print table title + if (count != -1) + LogMessage("Module Profile Statistics (worst %d)\n", count); + else + LogMessage("Module Profile Statistics (all)\n"); - for (hashNode = sfghash_findfirst(sc->otn_map); - hashNode; - hashNode = sfghash_findnext(sc->otn_map)) - { - otn = (OptTreeNode*)hashNode->data; - { - rtn = getRtnFromOtn(otn); - if (rtn == NULL) - continue; + // print headers + LogMessage("%*s%*s%*s%*s%*s%*s%*s%*s\n", + 4, "Num", + 24, "Module", + 6, "Layer", + 11, "Checks", + 20, "Microsecs", + 11, "Avg/Check", + 10, "%/Caller", + 10, "%/Total"); - if ( is_network_protocol(rtn->proto) ) - { - //do operation - for ( unsigned i = 0; i < get_instance_max(); ++i ) - { - OtnState* state = otn->state + i; - state->ticks = 0; - state->ticks_match = 0; - state->ticks_no_match = 0; - state->checks = 0; - state->matches = 0; - state->alerts = 0; - state->noalerts = 0; -#ifdef PPM_MGR - state->ppm_disable_cnt = 0; -#endif - } - } - } + LogMessage("%*s%*s%*s%*s%*s%*s%*s%*s\n", + 4, "===", + 24, "======", + 6, "=====", + 11, "======", + 20, "=========", + 11, "=========", + 10, "========", + 10, "========"); + + int num = 0; + for ( const auto& entry : root.entries ) + print_mod_entry(0, ++num, root, entry); + + int indent = root.name.size() + 1; + LogMessage("%*s%*s%*d" FMTu64("*") FMTu64("*") "%*.2f%*.2f%*.2f\n", + indent, root.name.c_str(), + 28 - indent, root.name.c_str(), 6, 0, + 11, root.stats.checks, + 20, uint64_t(double(root.stats.ticks) / get_ticks_per_us()), + 11, root.ticks_per_check / get_ticks_per_us(), + 10, root.pct_of_parent, + 10, root.pct_of_parent); +} + +bool get_mod_sort_function(ProfileSort sort_mode, ModEntrySortFunc& sort_fn) +{ + switch ( sort_mode ) + { + case PROFILE_SORT_CHECKS: + sort_fn = [](const ModEntry& a, const ModEntry& b) -> bool + { return a.stats.checks > b.stats.checks; }; + break; + + case PROFILE_SORT_TOTAL_TICKS: + sort_fn = [](const ModEntry& a, const ModEntry& b) -> bool + { return a.stats.ticks > b.stats.ticks; }; + break; + + case PROFILE_SORT_AVG_TICKS: + sort_fn = [](const ModEntry& a, const ModEntry& b) -> bool + { return a.ticks_per_check > b.ticks_per_check; }; + break; + + default: + return false; + break; } + + return true; } -void PrintWorstRules(int numToPrint) +static void consolidate_otn_states(OtnState* states) { - OptTreeNode* otn; - OTN_WorstPerformer* node, * tmp; - int num = 0; - SnortConfig* sc = snort_conf; + for ( unsigned i = 1; i < get_instance_max(); ++i ) + states[0] += states[i]; +} - if (sc == NULL) +static void sort_rule_stats(std::vector& entries, ProfileSort sort_mode) +{ + RuleEntrySortFunc sort_fn; + switch ( sort_mode ) + { + case PROFILE_SORT_CHECKS: + sort_fn = [](const RuleEntry& a, const RuleEntry& b) + { return a.state.checks >= b.state.checks; }; + break; + + case PROFILE_SORT_MATCHES: + sort_fn = [](const RuleEntry& a, const RuleEntry& b) + { return a.state.matches >= b.state.matches; }; + break; + + case PROFILE_SORT_NOMATCHES: + sort_fn = [](const RuleEntry& a, const RuleEntry& b) + { return (a.state.checks - a.state.matches) > (b.state.checks - b.state.matches); }; + break; + + case PROFILE_SORT_AVG_TICKS_PER_MATCH: + sort_fn = [](const RuleEntry& a, const RuleEntry& b) + { return a.ticks_per_match >= b.ticks_per_match; }; + break; + + case PROFILE_SORT_AVG_TICKS_PER_NOMATCH: + sort_fn = [](const RuleEntry& a, const RuleEntry& b) + { return a.ticks_per_nomatch >= b.ticks_per_nomatch; }; + break; + + case PROFILE_SORT_TOTAL_TICKS: + sort_fn = [](const RuleEntry& a, const RuleEntry& b) + { return a.state.ticks >= b.state.ticks; }; + break; + + case PROFILE_SORT_AVG_TICKS: + sort_fn = [](const RuleEntry& a, const RuleEntry& b) + { return a.ticks_per_check >= b.ticks_per_check; }; + break; + + default: return; + break; + } - getTicksPerMicrosec(); + std::sort(entries.begin(), entries.end(), sort_fn); +} - if ( !worstPerformers ) - return; +static void get_rule_stats_entries(std::vector& entries) +{ + assert(snort_conf); - LogMessage("--------------------------------------------------\n"); + detection_option_tree_update_otn_stats(snort_conf->detection_option_tree_hash_table); - if (numToPrint != -1) + for ( SFGHASH_NODE* h = sfghash_findfirst(snort_conf->otn_map); h; h = sfghash_findnext(snort_conf->otn_map) ) { - LogMessage("Rule Profile Statistics (worst %d rules)\n", numToPrint); + OptTreeNode* otn = static_cast(h->data); + assert(otn); + + OtnState* states = otn->state; + OtnState& state = states[0]; + + consolidate_otn_states(states); + + if ( !state.checks || !state.ticks ) + continue; + + entries.emplace_back(otn->sigInfo, state); } +} + +static void print_rule_stats(std::vector& entries, int num) +{ + // if ( entries.empty ) return; + + LogMessage("--------------------------------------------------\n"); + + // print table title + if ( num != -1 ) + LogMessage("Rule Profile Statistics (worst %d rules)\n", num); + else - { LogMessage("Rule Profile Statistics (all rules)\n"); - } + // print headers LogMessage( #ifdef PPM_MGR "%*s%*s%*s%*s%*s%*s%*s%*s%*s%*s%*s%*s\n", @@ -271,14 +452,11 @@ void PrintWorstRules(int numToPrint) #endif ); - for (node = worstPerformers, num=1; - node && ((numToPrint < 0) ? 1 : (num <= numToPrint)); - node= node->next, num++) + int i = 0; + for ( const auto& entry : entries ) { - //if (!node) - // break; - otn = node->otn; - OtnState* state = otn->state; + if ( (num != -1) && (i >= num) ) + break; LogMessage( #ifdef PPM_MGR @@ -287,680 +465,198 @@ void PrintWorstRules(int numToPrint) #else "%*d%*d%*d%*d" FMTu64("*") FMTu64("*") FMTu64("*") FMTu64("*") "%*.1f%*.1f%*.1f" "\n", #endif - 6, num, 9, otn->sigInfo.id, 4, otn->sigInfo.generator, 4, otn->sigInfo.rev, - 11, state->checks, - 10, state->matches, - 10, state->alerts, - 20, (uint64_t)(state->ticks/ticks_per_microsec), - 11, node->ticks_per_check/ticks_per_microsec, - 11, node->ticks_per_match/ticks_per_microsec, - 13, node->ticks_per_nomatch/ticks_per_microsec + 6, ++i, + 9, entry.sig_info.id, 4, entry.sig_info.generator, 4, entry.sig_info.rev, + 11, entry.state.checks, + 10, entry.state.matches, + 10, entry.state.alerts, + 20, uint64_t(double(entry.state.ticks) / get_ticks_per_us()), + 11, entry.ticks_per_check / get_ticks_per_us(), + 11, entry.ticks_per_match / get_ticks_per_us(), + 13, entry.ticks_per_nomatch / get_ticks_per_us() #ifdef PPM_MGR - , 11, state->ppm_disable_cnt + , 11, entry.state.ppm_disable_cnt #endif ); } - - /* Do some cleanup */ - for (node = worstPerformers; node; ) - { - tmp = node->next; - free(node); - node = tmp; - } - - worstPerformers = NULL; } -static void sum_otn_profile_data(OtnState* state) -{ - for ( unsigned i = 1; i < get_instance_max(); ++i ) - { - state[0].ticks += state[i].ticks; - state[0].ticks_match += state[i].ticks_match; - state[0].ticks_no_match += state[i].ticks_no_match; - state[0].checks += state[i].checks; - state[0].matches += state[i].matches; - state[0].noalerts += state[i].noalerts; - state[0].alerts += state[i].alerts; - } -} +// ----------------------------------------------------------------------------- +// class/struct implementation +// ----------------------------------------------------------------------------- -void CollectRTNProfile(void) -{ - OptTreeNode* otn; - OTN_WorstPerformer* owp, * node, * last = NULL; - char got_position; - SFGHASH_NODE* hashNode; - SnortConfig* sc = snort_conf; +void NodePerfProfiler::update(bool match) +{ stats.update(get_delta(), match); } - if (sc == NULL) - return; +const ProfileStats* ModStatsFunctor::operator()(const std::string& key) +{ + assert(is_set()); - for (hashNode = sfghash_findfirst(sc->otn_map); - hashNode; - hashNode = sfghash_findnext(sc->otn_map)) + if ( type == MODULE ) { - otn = (OptTreeNode*)hashNode->data; - OtnState* state = otn->state; + const auto *ps = owner->get_profile(); + if ( ps ) + return ps; - sum_otn_profile_data(state); + unsigned i = 0; + const char* name, * pname; + while ( (ps = owner->get_profile(i++, name, pname)) && key != name ); - { - /* Only log info if OTN has actually been eval'd */ - if (state->checks > 0 && state->ticks > 0) - { - double ticks_per_check = (double)state->ticks/(double)state->checks; - double ticks_per_nomatch; - double ticks_per_match; - - if (state->matches > state->checks) - state->checks = state->matches; - - if (state->matches) - ticks_per_match = (double)state->ticks_match/(double)state->matches; - else - ticks_per_match = 0.0; - - if (state->checks == state->matches) - ticks_per_nomatch = 0.0; - else - ticks_per_nomatch = (double)state->ticks_no_match/(double)(state->checks - - state->matches); - - /* Find where he goes in the list - * Cycle through the list and add - * this where it goes - */ - owp = (OTN_WorstPerformer*)SnortAlloc(sizeof(OTN_WorstPerformer)); - owp->otn = otn; - owp->ticks_per_check = ticks_per_check; - owp->ticks_per_match = ticks_per_match; - owp->ticks_per_nomatch = ticks_per_nomatch; - - got_position = 0; - - for (node = worstPerformers; node && !got_position; node = node->next) - { - OtnState* worst_otn = node->otn->state; - last = node; - switch (sc->profile_rules->sort) - { - case PROFILE_SORT_CHECKS: - if (state->checks >= worst_otn->checks) - { - got_position = 1; - } - break; - case PROFILE_SORT_MATCHES: - if (state->matches >= worst_otn->matches) - { - got_position = 1; - } - break; - case PROFILE_SORT_NOMATCHES: - if (state->checks - state->matches > - worst_otn->checks - worst_otn->matches) - { - got_position = 1; - } - break; - case PROFILE_SORT_AVG_TICKS_PER_MATCH: - if (ticks_per_match >= node->ticks_per_match) - { - got_position = 1; - } - break; - case PROFILE_SORT_AVG_TICKS_PER_NOMATCH: - if (ticks_per_nomatch >= node->ticks_per_nomatch) - { - got_position = 1; - } - break; - case PROFILE_SORT_TOTAL_TICKS: - if (state->ticks >= worst_otn->ticks) - { - got_position = 1; - } - break; - default: - case PROFILE_SORT_AVG_TICKS: - if (ticks_per_check >= node->ticks_per_check) - { - got_position = 1; - } - break; - } - if (got_position) - break; - } - - if (node) - { - owp->next = node; - owp->prev = node->prev; - node->prev = owp; - if (owp->prev) - owp->prev->next = owp; - /* Reset the head of list */ - if (node == worstPerformers) - worstPerformers = owp; - } - else - { - if (!last) - { - worstPerformers = owp; - } - else - { - owp->prev = last; - last->next = owp; - } - } - } - } + return ps; } -} - -void ShowRuleProfiles(void) -{ - /* Cycle through all Rules, print ticks & check count for each */ - SnortConfig* sc = snort_conf; - - if ((sc == NULL) || (sc->profile_rules->num == 0)) - return; - - detection_option_tree_update_otn_stats(sc->detection_option_tree_hash_table); - CollectRTNProfile(); - link_nodes(); + else if ( type == CALLBACK ) + return callback(key.c_str()); - /* Specifically call out a top xxx or something? */ - PrintWorstRules(sc->profile_rules->num); + return nullptr; } -/* The preprocessor profile list is only accessed for printing stats when - * Snort shuts down. */ -void RegisterProfile( - const char* keyword, const char* parent, get_profile_func get, Module* mod) +void ModStatsNode::accumulate() { - ProfileStatsNode* node; - node = (ProfileStatsNode*)SnortAlloc(sizeof(ProfileStatsNode)); + assert(!name.empty()); - if (gProfileStatsNodeList == NULL) + if ( is_set() ) { - gProfileStatsNodeList = node; - } - else - { - ProfileStatsNode* tmp = gProfileStatsNodeList; - ProfileStatsNode* last; - - do - { - if (strcasecmp(tmp->name, keyword) == 0) - { - //FatalError("Duplicate Preprocessor Stats Name (%s)\n", keyword); - /* Don't fatal error here since during a reload there are - * probably going to be dups - just return */ - //multiple policy support - free(node); - return; - } - - last = tmp; - tmp = tmp->next; - } - while (tmp != NULL); - - last->next = node; + const auto* ps = getter(name); + if ( ps ) + stats += *ps; } - - node->name = keyword; - node->pname = parent; - node->layer = 0; - node->get_data = get; - node->owner = mod; - node->parent = nullptr; - - if ( !node->pname && strcasecmp(node->name, TOTAL) ) - node->pname = TOTAL; - - if ( !strcasecmp(node->name, "mpse") ) - mpsePerfStats = &node->stats; } -void RegisterProfile(Module* m) +ModEntry::ModEntry(ModStatsNode* node, const ProfileStats& caller_stats) : + name(node->name), node(node), stats(node->get_total()) { - ProfileStats* ps = m->get_profile(); - - if ( ps ) - RegisterProfile(m->get_name(), nullptr, nullptr, m); + assert(stats.ticks >= stats.checks); + assert(caller_stats.ticks >= stats.ticks); + if ( stats.checks == 0.0 ) + ticks_per_check = 0.0; else - { - unsigned i = 0; - const char* name, * pname; + ticks_per_check = double(stats.ticks) / double(stats.checks); - while ( (ps = m->get_profile(i++, name, pname)) ) - RegisterProfile(name, pname, nullptr, m); - } + if ( caller_stats.ticks == 0.0 ) + pct_of_parent = 0.0; + else + pct_of_parent = double(stats.ticks) / double(caller_stats.ticks) * 100.0; } -static ProfileStats* get_profile(ProfileStatsNode* node) +RuleEntry::RuleEntry(const SigInfo& si, const OtnState& os) : + sig_info(si), state(os) { - ProfileStats* ps = node->owner->get_profile(); - - if ( ps ) - return ps; + ticks_per_check = double(state.ticks) / double(state.checks); - unsigned i = 0; - const char* name, * pname; + state.checks = std::max(state.checks, state.matches); - while ( (ps = node->owner->get_profile(i++, name, pname)) && - strcasecmp(node->name, name) ) - ; + if ( state.matches ) + ticks_per_match = double(state.ticks_match) / double(state.matches); - return ps; + // can safely replace != with > + if ( state.checks != state.matches ) + ticks_per_nomatch = double(state.ticks_no_match) / double(state.checks - state.matches); } -void FreePreprocPerformance(Preproc_WorstPerformer* idx) -{ - Preproc_WorstPerformer* child, * tmp; - child = idx->children; - while (child) - { - FreePreprocPerformance(child); - tmp = child; - child = child->next; - free(tmp); - } -} +// ----------------------------------------------------------------------------- +// public API +// ----------------------------------------------------------------------------- -void PrintPreprocPerformance(int num, Preproc_WorstPerformer* idx) +void PerfProfilerManager::register_module(Module* m) { - Preproc_WorstPerformer* child; - int i; - /* indent 'Num' based on the layer */ - unsigned int indent = 6 - (5 - idx->node->layer); + if ( m->get_profile() ) + register_module(m->get_name(), nullptr, m); - if (num != 0) - { - indent += 2; - LogMessage("%*d%*s%*d" FMTu64("*") FMTu64("*") "%*.2f%*.2f%*.2f\n", - indent, num, - 28 - indent, idx->node->name, 6, idx->node->layer, - 11, idx->node->stats.checks, - 20, (uint64_t)(idx->node->stats.ticks/ticks_per_microsec), - 11, idx->ticks_per_check/ticks_per_microsec, - 14, idx->pct_of_parent, - 13, idx->pct_of_total); - } else { - /* The totals */ - indent += strlen(idx->node->name); - - LogMessage("%*s%*s%*d" FMTu64("*") FMTu64("*") "%*.2f%*.2f%*.2f\n", - indent, idx->node->name, - 28 - indent, idx->node->name, 6, idx->node->layer, - 11, idx->node->stats.checks, - 20, (uint64_t)(idx->node->stats.ticks/ticks_per_microsec), - 11, idx->ticks_per_check/ticks_per_microsec, - 14, idx->pct_of_parent, - 13, idx->pct_of_parent); - } - - child = idx->children; - - i = 1; - while (child) - { - PrintPreprocPerformance(i++, child); - child = child->next; - } -} + const char* name, * pname; + const ProfileStats* ps; + unsigned i = 0; -// from main thread only -static void CleanupProfileStatsNodeList(ProfileStatsNode* node) -{ - while (node) - { - ProfileStatsNode* nxt = node->next; - free(node); - node = nxt; + while ( (ps = m->get_profile(i++, name, pname)) ) + register_module(name, pname, m); } } -// from main thread only -void CleanupProfileStatsNodeList(void) -{ - CleanupProfileStatsNodeList(gProfileStatsNodeList); - gProfileStatsNodeList = NULL; -} - -// from packet thread only -void ReleaseProfileStats(void) -{ - static mutex stats_mutex; - stats_mutex.lock(); - - ProfileStatsNode* node = gProfileStatsNodeList; - - while (node) - { - const ProfileStats* ps; - assert(node->get_data || node->owner); - - if ( node->owner ) - ps = get_profile(node); - else - ps = node->get_data(node->name); - assert(ps); - - node->stats.ticks += ps->ticks; - node->stats.checks += ps->checks; +void PerfProfilerManager::register_module(const char* name, const char* pname, Module* m) +{ add_module(name, pname, m); } - node = node->next; - } - stats_mutex.unlock(); -} +void PerfProfilerManager::register_module(const char* name, const char* pname, + get_profile_func getter) +{ add_module(name, pname, getter); } -void CleanupPreprocPerformance(Preproc_WorstPerformer* worst) +// thread local +void PerfProfilerManager::consolidate_stats() { - Preproc_WorstPerformer* idx, * child, * tmp; + static std::mutex stats_mutex; + std::lock_guard lock(stats_mutex); - idx = worst; - while (idx) - { - tmp = idx->next; - child = idx->children; - CleanupPreprocPerformance(child); - - free(idx); - idx = tmp; - } + for ( auto it = s_module_nodes.begin(); it != s_module_nodes.end(); ++it ) + it->second.accumulate(); } -void PrintWorstPreprocs(int numToPrint) +void PerfProfilerManager::show_module_stats() { - Preproc_WorstPerformer* idx; - Preproc_WorstPerformer* total = NULL; - int num = 0; - - getTicksPerMicrosec(); + const auto& config = *snort_conf->profile_modules; - if ( !worstPreprocPerformers ) + if ( !config.count ) return; - LogMessage("--------------------------------------------------\n"); + ModStatsNode& root_node = s_module_nodes[TOTAL]; + ModEntry root(&root_node, root_node.get_total()); - if (numToPrint != -1) - LogMessage("Module Profile Statistics (worst %d)\n", numToPrint); - else - LogMessage("Module Profile Statistics (all)\n"); + ModEntrySortFunc sort_fn; + if ( get_mod_sort_function(config.sort, sort_fn) ) + get_mod_entries(root, &sort_fn, config.count); - LogMessage("%*s%*s%*s%*s%*s%*s%*s%*s\n", - 4, "Num", - 24, "Module", - 6, "Layer", - 11, "Checks", - 20, "Microsecs", - 11, "Avg/Check", - 14, "Pct of Caller", - 13, "Pct of Total"); - - LogMessage("%*s%*s%*s%*s%*s%*s%*s%*s\n", - 4, "===", - 24, "======", - 6, "=====", - 11, "======", - 20, "=========", - 11, "=========", - 14, "=============", - 13, "============"); - - for (idx = worstPreprocPerformers, num=1; - idx && ((numToPrint < 0) ? 1 : (num <= numToPrint)); - idx= idx->next, num++) - { - /* Skip the total counter */ - if ( !strcasecmp(idx->node->name, TOTAL) ) - { - num--; - total = idx; - continue; - } - PrintPreprocPerformance(num, idx); - } - if (total) - PrintPreprocPerformance(0, total); + else + get_mod_entries(root, nullptr, config.count); - CleanupPreprocPerformance(worstPreprocPerformers); - worstPreprocPerformers = NULL; + print_mod_entries(root, config.count); } -Preproc_WorstPerformer* findPerfParent(ProfileStatsNode* node, - Preproc_WorstPerformer* top) +void PerfProfilerManager::reset_module_stats() { - Preproc_WorstPerformer* list = top; - Preproc_WorstPerformer* parent; - - if (!list) - return NULL; - - if (list->node->layer > node->layer) - return NULL; - - while (list) - { - if ( !strcmp(list->node->name, node->pname) ) - return list; - - parent = findPerfParent(node, list->children); - - if (parent) - return parent; - - list = list->next; - } - - return NULL; + for ( auto it = s_module_nodes.begin(); it != s_module_nodes.end(); ++it ) + it->second.reset(); } -void ResetPreprocProfiling(void) +void PerfProfilerManager::show_rule_stats() { - ProfileStatsNode* idx = NULL; - SnortConfig* sc = snort_conf; + const auto& config = *snort_conf->profile_rules; - if ((sc == NULL) || (sc->profile_modules->num == 0)) + if ( !config.count ) return; - for (idx = gProfileStatsNodeList; idx != NULL; idx = idx->next) - { - idx->stats.ticks = 0; - idx->stats.checks = 0; - } -} + std::vector entries; + get_rule_stats_entries(entries); -void ShowPreprocProfiles(void) -{ - /* Cycle through all Rules, print ticks & check count for each */ - ProfileStatsNode* idx; - int layer; - Preproc_WorstPerformer* parent, * pwp, * curr = NULL, * last = NULL; - char got_position; - Preproc_WorstPerformer* listhead; - double ticks_per_check; - SnortConfig* sc = snort_conf; - - if ((sc == NULL) || (sc->profile_modules->num == 0)) + if ( entries.empty() ) return; - link_nodes(); - - /* Adjust mpse stats to not include rule evaluation */ - if ( mpsePerfStats ) - mpsePerfStats->ticks -= rulePerfStats.ticks; + sort_rule_stats(entries, config.sort); + print_rule_stats(entries, config.count); +} - /* And adjust the rules to include the NC rules */ - rulePerfStats.ticks += ncrulePerfStats.ticks; +void PerfProfilerManager::reset_rule_stats() +{ + auto* otn_map = snort_conf->otn_map; - for (layer=0; layer<=max_layers; layer++) + for ( SFGHASH_NODE* h = sfghash_findfirst(otn_map); h; h = sfghash_findnext(otn_map) ) { - for (idx = gProfileStatsNodeList; idx; idx = idx->next) - { - if (idx->stats.checks == 0 || idx->stats.ticks == 0) - continue; - - if (idx->layer != layer) - continue; - - last = NULL; - - ticks_per_check = (double)idx->stats.ticks/(double)idx->stats.checks; - - pwp = (Preproc_WorstPerformer*)SnortAlloc(sizeof(Preproc_WorstPerformer)); - pwp->node = idx; - pwp->ticks_per_check = ticks_per_check; + auto* otn = static_cast(h->data); + assert(otn); - if (idx->parent) - { - /* Find this idx's parent in the list */ - parent = findPerfParent(idx, worstPreprocPerformers); - if (parent && strcasecmp(parent->node->name, TOTAL)) - { - listhead = parent->children; - } - else - { - listhead = worstPreprocPerformers; - parent = NULL; - } - pwp->pct_of_parent = (double)idx->stats.ticks/idx->parent->stats.ticks*100.0; - pwp->pct_of_total = (double)idx->stats.ticks/get_root(idx)->stats.ticks*100.0; - } - else - { - parent = NULL; - pwp->pct_of_parent = 0.0; - pwp->pct_of_total = 100.0; - listhead = worstPreprocPerformers; - } + auto* rtn = getRtnFromOtn(otn); - got_position = 0; + if ( !rtn || !is_network_protocol(rtn->proto) ) + continue; - for (curr = listhead; curr && !got_position; curr = curr->next) - { - last = curr; - switch (sc->profile_modules->sort) - { - case PROFILE_SORT_CHECKS: - if (pwp->node->stats.checks >= curr->node->stats.checks) - { - got_position = 1; - } - break; - case PROFILE_SORT_TOTAL_TICKS: - if (pwp->node->stats.ticks >= curr->node->stats.ticks) - { - got_position = 1; - } - break; - default: - case PROFILE_SORT_AVG_TICKS: - if (pwp->ticks_per_check >= curr->ticks_per_check) - { - got_position = 1; - } - break; - } - if (got_position) - break; - } - if (curr) - { - pwp->next = curr; - pwp->prev = curr->prev; - curr->prev = pwp; - if (pwp->prev) - pwp->prev->next = pwp; - /* Reset the head of the list */ - if (curr == listhead) - { - if (parent) - { - parent->children = pwp; - } - else - { - worstPreprocPerformers = pwp; - } - } - } - else - { - if (!last) - { - if (parent) - { - parent->children = pwp; - } - else - { - worstPreprocPerformers = pwp; - } - } - else - { - pwp->prev = last; - last->next = pwp; - } - } + for ( unsigned i = 0; i < get_instance_max(); ++i ) + { + auto& state = otn->state[i]; + memset(&state, 0, sizeof(state)); } } - PrintWorstPreprocs(sc->profile_modules->num); - CleanupProfileStatsNodeList(); } -// ----------------------------------------------------------------------------- -// non-static implementation -// ----------------------------------------------------------------------------- - -void NodePerfProfiler::update(bool match) -{ stats.update(get_delta(), match); } - -// ----------------------------------------------------------------------------- -// public API -// ----------------------------------------------------------------------------- - -void PerfProfilerManager::register_module(Module* m) -{ RegisterProfile(m); } - -void PerfProfilerManager::register_module(const char* name, const char* pname, Module* m) -{ RegisterProfile(name, pname, nullptr, m); } - -void PerfProfilerManager::register_module(const char* name, const char* pname, - get_profile_func getter) -{ RegisterProfile(name, pname, getter, nullptr); } - -// thread local -void PerfProfilerManager::consolidate_stats() -{ ReleaseProfileStats(); } - -void PerfProfilerManager::show_module_stats() -{ ShowPreprocProfiles(); } - -void PerfProfilerManager::reset_module_stats() -{ } - -void PerfProfilerManager::show_rule_stats() -{ ShowRuleProfiles(); } - -void PerfProfilerManager::reset_rule_stats() -{ } - void PerfProfilerManager::show_all_stats() { if ( SnortConfig::get_profile_modules() ) @@ -971,13 +667,13 @@ void PerfProfilerManager::show_all_stats() } void PerfProfilerManager::reset_all_stats() -{ } - -void PerfProfilerManager::init() -{ } +{ + if ( SnortConfig::get_profile_modules() ) + reset_module_stats(); -void PerfProfilerManager::term() -{ CleanupProfileStatsNodeList(); } + if ( SnortConfig::get_profile_rules() ) + reset_rule_stats(); +} // ----------------------------------------------------------------------------- // unit tests @@ -1002,6 +698,41 @@ struct ProfilePauseObserver bool pause_called_before_start = false; }; +TEST_CASE( "profile stats", "[profiler]" ) +{ + ProfileStats stats = { 1, 2 }; + + SECTION( "operator bool()" ) + { + CHECK( stats ); + stats = { 0, 0 }; + CHECK_FALSE( stats ); + } + + SECTION( "operator==" ) + { + ProfileStats compare = { 0, 0 }; + CHECK_FALSE( stats == compare ); + compare = stats; + CHECK( stats == compare ); + } + + SECTION( "reset" ) + { + stats.reset(); + CHECK_FALSE( stats ); + } + + SECTION( "operator+=" ) + { + ProfileStats inc = { 3, 4 }; + ProfileStats expected = { stats.ticks + inc.ticks, stats.checks + inc.checks }; + stats += inc; + + CHECK( stats == expected ); + } +} + TEST_CASE( "stopwatch", "[profiler]" ) { Stopwatch sw; @@ -1186,11 +917,7 @@ TEST_CASE( "perf profiler", "[profiler]" ) TEST_CASE( "node perf profiler", "[profiler]" ) { dot_node_state_t stats; - - stats.ticks = 0; - stats.ticks_match = 0; - stats.ticks_no_match = 0; - stats.checks = 0; + memset(&stats, 0, sizeof(stats)); SECTION( "going out of scope causes profiler to update stats" ) { @@ -1291,4 +1018,282 @@ TEST_CASE( "perf profiler pause", "[profiler]" ) CHECK( observer.pause_called_before_start ); } +// FIXIT-M Add unit tests for internally used types + +class MockProfilerModule : public Module +{ +public: + MockProfilerModule(ProfileStats* ps = nullptr) : + Module(nullptr, nullptr), stats(ps) { } + + ProfileStats* get_profile() const override + { + get_profile_called = true; + return stats; + } + + ProfileStats* stats; + mutable bool get_profile_called = false; +}; + +static ProfileStats mod_stats_test_stats; + +static ProfileStats* mock_get_profile_func(const char*) +{ return &mod_stats_test_stats; } + +TEST_CASE( "mod stats functor", "[profiler]" ) +{ + static_assert(std::is_default_constructible::value, ""); + static_assert(std::is_copy_constructible::value, ""); + + ModStatsFunctor functor; + + SECTION( "is not set by default" ) + { + CHECK_FALSE( functor.is_set() ); + } + + SECTION( "calls Module::get_profile() if module is set" ) + { + MockProfilerModule m; + REQUIRE_FALSE( m.get_profile_called ); + functor.set(&m); + + REQUIRE( functor.is_set() ); + functor(""); + + CHECK( m.get_profile_called ); + } + + SECTION( "calls get_profile_func if callback is set" ) + { + functor.set(mock_get_profile_func); + REQUIRE( functor.is_set() ); + auto ps = functor(""); + REQUIRE( ps ); + CHECK( ps == &mod_stats_test_stats ); + } + + SECTION( "last set() overrides any previous set()" ) + { + MockProfilerModule m; + functor.set(&m); + functor.set(mock_get_profile_func); + + REQUIRE( functor.is_set() ); + + auto ps = functor(""); + CHECK( ps == &mod_stats_test_stats ); + CHECK_FALSE( m.get_profile_called ); + + functor.set(&m); + ps = functor(""); + CHECK_FALSE( ps ); + CHECK( m.get_profile_called ); + } + + SECTION( "copy-constructed functor retains information" ) + { + SECTION( "unset" ) + { + ModStatsFunctor copied(functor); + CHECK_FALSE( copied.is_set() ); + } + + SECTION( "set(Module*)" ) + { + MockProfilerModule m; + functor.set(&m); + + ModStatsFunctor copied(functor); + REQUIRE( functor.is_set() ); + + copied(""); + CHECK( m.get_profile_called ); + } + + SECTION( "set(get_profile_func)" ) + { + functor.set(mock_get_profile_func); + + ModStatsFunctor copied(functor); + REQUIRE( functor.is_set() ); + + auto ps = copied(""); + CHECK( ps == &mod_stats_test_stats ); + } + } +} + +TEST_CASE( "mod stats node", "[profiler]" ) +{ + ModStatsNode node("a"); + + SECTION( "copy-constructed node retains children and name" ) + { + ModStatsNode child_1("1"); + ModStatsNode child_2("2"); + + node.add_child(&child_1); + node.add_child(&child_2); + + ModStatsNode copied(node); + + CHECK( copied.name == node.name ); + CHECK( copied.children == node.children ); + } + + SECTION( "copy-constructed node retains stats" ) + { + mod_stats_test_stats = { 1, 2 }; + node.set(mock_get_profile_func); + node.accumulate(); + auto orig_stats = node.get_total(); + + ModStatsNode copied(node); + auto copied_stats = copied.get_total(); + + CHECK( copied_stats.ticks == orig_stats.ticks ); + CHECK( copied_stats.checks == orig_stats.checks ); + } + + SECTION( "accumulate() and get_total() correctly adds stats" ) + { + mod_stats_test_stats = { 1, 2 }; + ProfileStats expected = mod_stats_test_stats; + expected += mod_stats_test_stats; + + node.set(mock_get_profile_func); + + node.accumulate(); + node.accumulate(); + + auto ps = node.get_total(); + CHECK( ps == expected ); + } + + SECTION( "stats are zero initially" ) + { + auto ps = node.get_total(); + CHECK_FALSE( ps ); + } + + SECTION( "get_total() works correctly" ) + { + ProfileStats a_stats = { 1, 3 }; + ProfileStats b_stats = { 2, 4 }; + + MockProfilerModule a_mod(&a_stats); + MockProfilerModule b_mod(&b_stats); + + ModStatsNode child("b"); + + node.set(&a_mod); + child.set(&b_mod); + + node.add_child(&child); + + node.accumulate(); + child.accumulate(); + + SECTION( "A->get_total() calls B->get_total() and adds results only on the first call" ) + { + ProfileStats expected = a_stats; + expected += b_stats; + + auto ps = node.get_total(); + CHECK( ps == expected ); + + ps = node.get_total(); + CHECK( ps == expected ); + + SECTION( "child is totalled only once via call to parent" ) + { + ProfileStats expected = b_stats; + auto ps = child.get_total(); + + CHECK( ps == expected ); + } + + SECTION( "reset forces totalling again for the current node" ) + { + ProfileStats c_stats = { 7, 13 }; + MockProfilerModule c_mod(&c_stats); + ModStatsNode second_child("c"); + + ProfileStats new_expected = expected; + new_expected += c_stats; + + second_child.set(&c_mod); + second_child.accumulate(); + + node.add_child(&second_child); + REQUIRE( node.get_total() == expected ); + + node.reset(); + node.accumulate(); + + auto ps = node.get_total(); + CHECK( ps == new_expected ); + } + } + } +} + +TEST_CASE( "mod stats tree", "[profiler]" ) +{ + ModStatsTree tree; + SECTION( "correctly sets the name of a node when accessed" ) + { + auto& foo = tree["foo"]; + CHECK( foo.name == "foo" ); + } +} + +TEST_CASE( "mod entry", "[profiler]" ) +{ + ProfileStats stats = { 2, 1 }; + ProfileStats caller_stats = { 20, 10 }; + MockProfilerModule m(&stats); + ModStatsNode node("a"); + + node.set(&m); + node.accumulate(); + + ModEntry entry(&node, caller_stats); + + REQUIRE( entry.stats == stats ); + + SECTION( "ctor calculates percentages" ) + { + CHECK( entry.ticks_per_check == 2.0 ); + CHECK( entry.pct_of_parent == 2.0 / 20.0 * 100.0 ); + } + + SECTION( "zeros" ) + { + stats = { 0, 0 }; + caller_stats = { 0, 0 }; + node.reset(); + node.accumulate(); + + ModEntry entry(&node, caller_stats); + + CHECK( entry.ticks_per_check == 0.0 ); + CHECK( entry.pct_of_parent == 0.0 ); + } +} + +TEST_CASE( "rule entry", "[profiler]" ) +{ +} + +TEST_CASE( "module stats algorithms", "[profiler]" ) +{ +} + +TEST_CASE( "rule stats algorithms", "[profiler]" ) +{ +} + #endif diff --git a/src/time/profiler.h b/src/time/profiler.h index cbfe1101d..b7d512286 100644 --- a/src/time/profiler.h +++ b/src/time/profiler.h @@ -16,8 +16,8 @@ // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. //-------------------------------------------------------------------------- - -// profiler.h author Steven Sturges +// profiler.h author Joel Cornett +// based on work by Steven Sturges #ifndef PROFILER_H #define PROFILER_H @@ -29,9 +29,23 @@ #endif #include "main/snort_types.h" -#include "main/snort_config.h" +#include "main/thread.h" +#include "time/cpuclock.h" + +class Module; + +enum ProfileSort +{ + PROFILE_SORT_NONE = 0, + PROFILE_SORT_CHECKS, + PROFILE_SORT_AVG_TICKS, + PROFILE_SORT_TOTAL_TICKS, + PROFILE_SORT_MATCHES, + PROFILE_SORT_NOMATCHES, + PROFILE_SORT_AVG_TICKS_PER_MATCH, + PROFILE_SORT_AVG_TICKS_PER_NOMATCH +}; -// unconditionally declared struct ProfileStats { uint64_t ticks; @@ -39,10 +53,46 @@ struct ProfileStats void update(uint64_t elapsed) { ++checks; ticks += elapsed; } + + void reset() + { ticks = 0; checks = 0; } + + bool operator==(const ProfileStats& rhs) + { return ticks == rhs.ticks && checks == rhs.checks; } + + operator bool() const + { return ticks || checks; } + + ProfileStats& operator+=(const ProfileStats& rhs) + { + ticks += rhs.ticks; + checks += rhs.checks; + return *this; + } }; -#include "main/thread.h" -#include "time/cpuclock.h" +struct RuleProfileStats : ProfileStats +{ + uint64_t ticks_match; + + void update(uint64_t elapsed, bool match) + { + ProfileStats::update(elapsed); + if ( match ) + ticks_match += elapsed; + } + + void reset() + { ProfileStats::reset(); ticks_match = 0; } + + RuleProfileStats& operator+=(const RuleProfileStats& o) + { + ticks += o.ticks; + checks += o.checks; + ticks_match += o.ticks_match; + return *this; + } +}; // FIXIT-L should go in its own module class Stopwatch @@ -191,6 +241,9 @@ struct ProfilerPause Profiler& profiler; }; +// thread local access method +using get_profile_func = ProfileStats* (*)(const char*); + #ifdef PERF_PROFILING #ifndef PROFILING_MODULES #define PROFILING_MODULES SnortConfig::get_profile_modules() @@ -209,9 +262,15 @@ struct ProfilerPause #define PERF_PROFILE(stats) \ PerfProfiler PERF_PROFILER_NAME(stats) { stats } +#define PERF_PROFILE_THREAD_LOCAL(stats, idx) \ + PerfProfiler PERF_PROFILER_NAME(stats) { stats [ idx ] } + #define PERF_PROFILE_BLOCK(stats) \ if ( PERF_PROFILE(stats) ) +#define PERF_PROFILE_THREAD_LOCAL_BLOCK (stats, idx) \ + if ( PERF_PROFILE_THREAD_LOCAL(stats, idx) ) + #define NODE_PERF_PROFILE(stats) \ NodePerfProfiler PERF_PROFILER_NAME(stats) { stats } @@ -229,9 +288,6 @@ struct ProfilerPause PERF_PAUSE_NAME(stats) { PERF_PROFILER_NAME(stats) } ) -// thread local access method -using get_profile_func = ProfileStats* (*)(const char*); - class PerfProfilerManager { public: @@ -250,28 +306,12 @@ public: static void show_all_stats(); static void reset_all_stats(); - - static void init(); - static void term(); }; -// Sort preferences for rule profiling -#define PROFILE_SORT_CHECKS 1 -#define PROFILE_SORT_AVG_TICKS 2 -#define PROFILE_SORT_TOTAL_TICKS 3 -#define PROFILE_SORT_MATCHES 4 -#define PROFILE_SORT_NOMATCHES 5 -#define PROFILE_SORT_AVG_TICKS_PER_MATCH 6 -#define PROFILE_SORT_AVG_TICKS_PER_NOMATCH 7 - -// ----------------------------------------------------------------------------- -// Profiling API -// ----------------------------------------------------------------------------- - struct ProfileConfig { - int num; - int sort; + int count; + ProfileSort sort; }; extern THREAD_LOCAL ProfileStats totalPerfStats; @@ -281,7 +321,9 @@ extern THREAD_LOCAL ProfileStats metaPerfStats; #define PERF_PROFILER_NAME(stats) #define PERF_PAUSE_NAME(stats) #define PERF_PROFILE(stats) +#define PERF_PROFILE_THREAD_LOCAL(stats, idx) #define PERF_PROFILE_BLOCK(stats) +#define PERF_PROFILE_THREAD_LOCAL_BLOCK(stats, idx) #define NODE_PERF_PROFILE(stats) #define NODE_PERF_PROFILE_BLOCK(stats) #define NODE_PERF_PROFILE_STOP_MATCH(stats)