]> git.ipfire.org Git - thirdparty/snort3.git/commitdiff
Merge pull request #86 in SNORT/snort3 from oo_perf_profiler to master
authorRuss Combs (rucombs) <rucombs@cisco.com>
Tue, 27 Oct 2015 22:02:52 +0000 (18:02 -0400)
committerRuss Combs (rucombs) <rucombs@cisco.com>
Tue, 27 Oct 2015 22:02:52 +0000 (18:02 -0400)
Squashed commit of the following:

commit 5e0f250e8dd35b005b8d2ae375b0b57500d724fb
Author: Joel Cornett <joel.cornett@gmail.com>
Date:   Tue Oct 27 16:57:35 2015 -0400

    added ncrulePerfStats back as ruleNFPEvalPerfStats

commit 40027f80c681c5cac953aa382cffdd36d9f8fcfa
Author: Joel Cornett <joel.cornett@gmail.com>
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 <joel.cornett@gmail.com>
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 <joel.cornett@gmail.com>
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 <joel.cornett@gmail.com>
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 <joel.cornett@gmail.com>
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 <joel.cornett@gmail.com>
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 <joel.cornett@gmail.com>
Date:   Fri Oct 16 14:40:36 2015 -0400

    rewrote module perf stats code
    removed legacy code
    added underscores in module names

40 files changed:
src/detection/detect.cc
src/detection/detect.h
src/detection/detection_options.cc
src/detection/fp_detect.cc
src/detection/fp_detect.h
src/ips_options/ips_hash.cc
src/ips_options/ips_http.cc
src/ips_options/ips_http_header.cc
src/ips_options/ips_luajit.cc
src/ips_options/ips_replace.cc
src/main/modules.cc
src/main/snort.cc
src/network_inspectors/arp_spoof/arp_spoof.cc
src/network_inspectors/perf_monitor/perf_base.cc
src/network_inspectors/perf_monitor/perf_monitor.cc
src/parser/config_file.cc
src/service_inspectors/back_orifice/back_orifice.cc
src/service_inspectors/dns/dns.cc
src/service_inspectors/gtp/gtp_inspect.cc
src/service_inspectors/gtp/gtp_module.cc
src/service_inspectors/gtp/ips_gtp_info.cc
src/service_inspectors/gtp/ips_gtp_type.cc
src/service_inspectors/gtp/ips_gtp_version.cc
src/service_inspectors/imap/imap.cc
src/service_inspectors/modbus/ips_modbus_data.cc
src/service_inspectors/modbus/ips_modbus_func.cc
src/service_inspectors/modbus/ips_modbus_unit.cc
src/service_inspectors/modbus/modbus.cc
src/service_inspectors/pop/pop.cc
src/service_inspectors/sip/ips_sip.cc
src/service_inspectors/ssh/ssh.cc
src/service_inspectors/ssl/ssl_inspector.cc
src/stream/tcp/ips_stream_reassemble.cc
src/stream/tcp/tcp_module.cc
src/stream/tcp/tcp_module.h
src/stream/tcp/tcp_reassembler.cc
src/time/CMakeLists.txt
src/time/ppm.cc
src/time/profiler.cc
src/time/profiler.h

index 631f4c56818ba61c5b28d10bc5ba44217d04dea7..c079dc4a4a6a6a2739f1766fd0186e2222e293e2 100644 (file)
@@ -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;
index 48d7e0d9b27027ffbe56f902b5c0e6f27ecf1d25..c8056dfdf5deb6a50f8efdd3b6d253813e716a57 100644 (file)
@@ -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
index adac5a67c7e095f90085dfe1d057c54e851543cc..b65dbb493dd70153dd8e7f8d1cd72db8e91d8020 100644 (file)
@@ -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;
index 70a1acaad867f7d2c8a83c78573e1ce63a029fb9..96136aeb4f99a4a38b130d4eff8174052b22374d 100644 (file)
@@ -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)
index cbd23edd6942cef0cbef4ead9431d0237f15f6ea..e92a56b90ed5c6c813d59a1fbe4427be51ced4a3 100644 (file)
@@ -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
 
 /*
index 53052938d87d2445ff72edc3b7949b376b270ea0..302403857a10c229c9218488e5b4f7e4debbc53a 100644 (file)
@@ -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);
 
index afc77b459781805247a0bdaff904a92b459ed4bc..1e6f36edc352dd3defb5a6a4008f9f6ac39ec0ca 100644 (file)
@@ -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;
 
index 3bba43a4a4e47ea5d4c97bcc003c8b01ec786ca0..7a5b8ef0ebe552abe10accd7dd8dadcedd2d3615 100644 (file)
@@ -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"
 
index 8ca2cd850241ea9ded60e7f6cd26040c5935299d..96a34ee8dac7ab74910d968efd6b8024f67dd8ec 100644 (file)
 #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"
 
index 661daa8d52ffb8bc4ebd3867d048bc58bd56b66a..d354519dc339081d7edfaf1f1b14687b1f7a9b4c 100644 (file)
@@ -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"
index d4e2c94358ee4169108be01b9567cebc208535e2..2a493d3a4b6abc2a08f4bd1a6780adf8a66060b8 100644 (file)
@@ -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<ProfileSort>(v.get_long() + 1);
 
     else
         return false;
index 84dff7403d7ad4b9c7e9bfa72d6ca6520ba57e7f..fe7b7689df39fb0d7981d908928e0298b360f646 100644 (file)
@@ -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);
index 1d2c96219a01b5fac3df4b91d94c039f10eb27e6..a797c2321a3222c43f620d96c123c848dcecd312 100644 (file)
@@ -66,6 +66,8 @@
  */
 
 /*  I N C L U D E S  ************************************************/
+#include "arp_module.h"
+
 #ifdef HAVE_CONFIG_H
 #include "config.h"
 #endif
 #include <string.h>
 #include <stdio.h>
 
-#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"
index 58f36a9e18c50c65e648215363ee2970db729055..839a75b4abdf69cd0196180213925824ddbc2bf6 100644 (file)
@@ -55,7 +55,7 @@
 #include <sys/types.h>
 
 #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"
index cd5ab6d7de8580f1354e762fb098e8c53f0c2e7b..ef898ebc42f51bf28c78829a36a11d3313973afe 100644 (file)
@@ -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"
index 22ab4f344764f048c4304122043368ca6bc35ff9..45987ce43c0263db47e7dc80daebb4ff5ef3a3c5 100644 (file)
@@ -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"
index 647702fe8bbe418e25339004635f850386fcc156..17e35ee021435fe10b6efc8522c1e1127dcf87b5 100644 (file)
 #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"
index 68e2a9e6b94bfce7a7aac7d9b21de661c674e7d4..ffbbe5ec4e693108c899912393ad19c5e2bfb7d1 100644 (file)
@@ -32,6 +32,7 @@
 #include <stdio.h>
 #include <sys/types.h>
 
+#include "events/event_queue.h"
 #include "main/snort_types.h"
 #include "main/snort_debug.h"
 #include "time/profiler.h"
index 3c63e05b766f6c30504d256390db912099dfb878..8bc74f45eada758dd1b61e45033371ca590e1b73 100644 (file)
@@ -26,6 +26,7 @@
 #endif
 
 #include "managers/inspector_manager.h"
+#include "protocols/packet.h"
 #include "time/profiler.h"
 
 #include "gtp.h"
index e9608399b909d43392ffc50b3cd61deff1d8af24..024b9a742d7611900c3f3f505c07e177a6a5c2ce 100644 (file)
@@ -20,6 +20,8 @@
 
 #include "gtp_module.h"
 
+#include <cassert>
+
 #include "gtp.h"
 #include "time/profiler.h"
 
index 46f757a8acf87d3d1a8c626048422f37b793b474..5119d6c5a75bec9ba7fee2ca97308f41d1b18230 100644 (file)
@@ -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"
index 987e206fc060a8aba5398d1fb2158da090240fe8..a021608305f770921d28f819a86572c6f226e297 100644 (file)
@@ -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"
index dc6ae6bd97d3d221466417eb9a79e31a76f0833e..ea1547f9e19a78d7dcc99bde0ee949177d948f28 100644 (file)
@@ -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"
index de59b311e38ba5916a79d326c17f9712b5baa019..c9650934765cf864400d4ecff1b6f637833c23df 100644 (file)
@@ -29,6 +29,7 @@
 #include <stdio.h>
 #include <sys/types.h>
 
+#include "events/event_queue.h"
 #include "main/snort_types.h"
 #include "main/snort_debug.h"
 #include "time/profiler.h"
index 64c967d90f443c976fa9c6953ae97ff70d8939e6..ef9aacfc5b6d4f74aa3502486ed9661fc6d92789 100644 (file)
@@ -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"
index 5a48a9ffb8a6e309acfd32ec75b425e9fb8ea237..a2e278e8b201ecda54ea791048244b078270599c 100644 (file)
@@ -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"
index 705aa8e5f6b5892dbc9d2f557ba766fef81d1612..f04642be217b8558f74f7a5e20a04953db0ba024 100644 (file)
@@ -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"
index 1896bd85cc95d524696ebf2eba34bbded8d7299c..97a87a2d02f5f4843d328dbe4cf021414175fea2 100644 (file)
@@ -24,6 +24,7 @@
 #include "config.h"
 #endif
 
+#include "events/event_queue.h"
 #include "managers/inspector_manager.h"
 #include "time/profiler.h"
 
index 29d0eda19ea3d306a1936e4f2274ee00cdcedec3..9aa82f1b1f9d13f095b5d031b4ece6e25b8f923d 100644 (file)
@@ -27,6 +27,7 @@
 #include <stdio.h>
 #include <sys/types.h>
 
+#include "events/event_queue.h"
 #include "main/snort_types.h"
 #include "main/snort_debug.h"
 #include "time/profiler.h"
index ee73b7f013870bd8886d648605de371815a95c91..efb8d13b3c1b519eacf3ec7f9d9af65b1e11dac5 100644 (file)
@@ -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;
index 35319d7ad30e192b13a01ac8297846dc3fe948b8..87a6b3a5969c2c7cddc0d216c429989aef7ec3ff 100644 (file)
@@ -31,6 +31,7 @@
 #include <stdio.h>
 #include <sys/types.h>
 
+#include "events/event_queue.h"
 #include "main/snort_types.h"
 #include "main/snort_debug.h"
 #include "time/profiler.h"
index a66a1b8c4f17240de19894135e230fead7654f43..5f8e19c76b6b9bef961ae07d4c75bddb0ba0ca2d 100644 (file)
@@ -33,6 +33,7 @@
 #include <stdio.h>
 #include <sys/types.h>
 
+#include "events/event_queue.h"
 #include "main/snort_types.h"
 #include "main/snort_debug.h"
 #include "time/profiler.h"
index 68ae36e0077ba69cc520078e4af87008e610aa27..003ca410c0ff32b2347525901738e3769c88c6d1 100644 (file)
@@ -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"
index 66ac851788d35b7cdac8b3b7efe4a55fd5285586..84601a05be27945afde3ab84875d4dc4a66d587b 100644 (file)
@@ -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;
 }
index 11fec0803991257de870e9d7c35f8de6a2770a9a..2ca30f951da6137feb1032e31958a04b7f21f402 100644 (file)
@@ -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
index fe23ddf56228b164151907be599314e201bd9ce5..9e908b5512f3104893f97d7e4bfd2ed15cb4114b 100644 (file)
@@ -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);
             }
index 3e1a7c432e97eba7fc73b1fa9699c65b87cfcd3c..45bc1d5b5cacea4e46501644f84458ce98333b01 100644 (file)
@@ -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"
index e98a4040ed66fbca7668e716399ddcce41a31e8e..654316d0cbc03bef83f8b3ad98b409987e02d174 100644 (file)
@@ -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"
index e47b11f92557e784f90592c51008b5adcc854f77..9a5ed2b9a74d0ec8b08bd827d7d16826f60dfb9d 100644 (file)
@@ -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 <ssturges@sourcefire.com>
+// profiler.cc author Joel Cornett <jocornet@cisco.com>
+// based on work by Steven Sturges <ssturges@sourcefire.com>
 
 #include "profiler.h"
 
 #endif
 
 #include <assert.h>
-#include <stdio.h>
-#include <stdlib.h>
 #include <string.h>
-#include <unistd.h>
 
+#include <functional>
 #include <mutex>
+#include <string>
+#include <unordered_map>
 
-#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<typename T>
+    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<ModStatsNode*> 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<ModEntry> entries;
+
+    ModEntry(ModStatsNode*, const ProfileStats&);
+};
+
+using ModEntrySortFunc = std::function<bool(const ModEntry&, const ModEntry&)>;
+
+// Wraps std::unordered_map with some initialization for operator[]
+class ModStatsTree
+{
+    std::unordered_map<std::string, ModStatsNode> 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<bool(const RuleEntry&, const RuleEntry&)>;
+
+// -----------------------------------------------------------------------------
+// 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<typename T>
+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<ModEntry> 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<size_t>(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<RuleEntry>& 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<RuleEntry>& 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<OptTreeNode*>(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<RuleEntry>& 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<std::mutex> 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<RuleEntry> 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<OptTreeNode*>(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<ModStatsFunctor>::value, "");
+    static_assert(std::is_copy_constructible<ModStatsFunctor>::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
index cbfe1101db585b99e73696bd578c1d5fa4d66154..b7d512286f150b73242ab0c5b343fd93272bcb11 100644 (file)
@@ -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 <ssturges@sourcefire.com>
+// profiler.h author Joel Cornett <jocornet@cisco.com>
+// based on work by Steven Sturges <ssturges@sourcefire.com>
 
 #ifndef PROFILER_H
 #define PROFILER_H
 #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)