sep = true;
}
+void JsonStream::put_eol()
+{
+ out << std::endl;
+}
void put_true(const char* key);
void put_false(const char* key);
+ void put_eol();
+
private:
void split();
set ( PROFILER_SOURCES
active_context.h
+ json_view.cc
+ json_view.h
memory_context.cc
memory_profiler.cc
memory_profiler.h
profiler.cc
+ profiler_module.cc
+ profiler_module.h
+ profiler_nodes.cc
+ profiler_nodes.h
profiler_printer.h
profiler_stats_table.cc
profiler_stats_table.h
profiler_tree_builder.h
- profiler_nodes.cc
- profiler_nodes.h
rule_profiler.cc
rule_profiler.h
+ table_view.cc
+ table_view.h
time_profiler.cc
time_profiler.h
- profiler_module.cc
- profiler_module.h
)
add_library ( profiler OBJECT
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2023-2023 Cisco and/or its affiliates. All rights reserved.
+//
+// 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
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// json_view.cc author Anna Norokh <anorokh@cisco.com>
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "json_view.h"
+
+#include <sstream>
+#include <vector>
+
+#include "control/control.h"
+#include "helpers/json_stream.h"
+#include "main/snort_config.h"
+#include "utils/stats.h"
+
+#include "profiler_printer.h"
+#include "rule_profiler.h"
+
+#define PRECISION 5
+
+using namespace snort;
+
+static void print_single_entry(ControlConn* ctrlcon, const rule_stats::View& v, unsigned n,
+ unsigned count, double total_time_usec)
+{
+ using std::chrono::duration_cast;
+ using std::chrono::microseconds;
+
+ std::ostringstream ss;
+ JsonStream json(ss);
+
+ json.open();
+ json.put("gid", v.sig_info.gid);
+ json.put("sid", v.sig_info.sid);
+ json.put("rev", v.sig_info.rev);
+
+ json.put("checks", v.checks());
+ json.put("matches", v.matches());
+ json.put("alerts", v.alerts());
+
+ json.put("timeUs", clock_usecs(TO_USECS(v.elapsed())));
+ json.put("avgCheck", clock_usecs(TO_USECS(v.avg_check())));
+ json.put("avgMatch", clock_usecs(TO_USECS(v.avg_match())));
+ json.put("avgNonMatch", clock_usecs(TO_USECS(v.avg_no_match())));
+
+ json.put("timeouts", v.timeouts());
+ json.put("suspends", v.suspends());
+ json.put("ruleTimePercentage", v.rule_time_per(total_time_usec), PRECISION);
+ json.close();
+
+
+ if ( n < count )
+ ss << ", ";
+
+ LogRespond(ctrlcon, "%s", ss.str().c_str());
+}
+
+void print_json_entries(ControlConn* ctrlcon, std::vector<rule_stats::View>& entries,
+ ProfilerSorter<rule_stats::View>& sort, unsigned count)
+{
+ std::ostringstream ss;
+ JsonStream json(ss);
+
+ RuleContext::set_end_time(get_time_curr());
+ RuleContext::count_total_time();
+
+ double start_time_usec =
+ RuleContext::get_start_time()->tv_sec * 1000000.0 + RuleContext::get_start_time()->tv_usec;
+ double end_time_usec =
+ RuleContext::get_end_time()->tv_sec * 1000000.0 + RuleContext::get_end_time()->tv_usec;
+ double total_time_usec =
+ RuleContext::get_total_time()->tv_sec * 1000000.0 + RuleContext::get_total_time()->tv_usec;
+
+ json.open();
+ json.put("startTime", start_time_usec);
+ json.put("endTime", end_time_usec);
+ json.open_array("rules");
+ json.put_eol();
+
+ LogRespond(ctrlcon, "%s", ss.str().c_str());
+
+ if ( !count || count > entries.size() )
+ count = entries.size();
+
+ if ( sort )
+ std::partial_sort(entries.begin(), entries.begin() + count, entries.end(), sort);
+
+ for ( unsigned i = 0; i < count; ++i )
+ print_single_entry(ctrlcon, entries[i], i + 1, count, total_time_usec);
+
+ //clean the stream from previous data
+ ss.str("");
+ json.close_array();
+ json.close();
+
+ LogRespond(ctrlcon, "%s", ss.str().c_str());
+}
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2023-2023 Cisco and/or its affiliates. All rights reserved.
+//
+// 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
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// json_view.h author Anna Norokh <anorokh@cisco.com>
+
+#ifndef JSON_VIEW_H
+#define JSON_VIEW_H
+
+#include <vector>
+
+#include "main/snort_config.h"
+
+#include "profiler_printer.h"
+#include "rule_profiler.h"
+
+void print_json_entries(ControlConn*, std::vector<rule_stats::View>&, ProfilerSorter<rule_stats::View>&, unsigned);
+
+#endif
#include "control/control.h"
#include "hash/xhash.h"
#include "log/messages.h"
+#include "lua/lua.h"
#include "main/analyzer_command.h"
#include "main/reload_tuner.h"
#include "main/snort.h"
#include "main/snort_config.h"
#include "managers/module_manager.h"
-#include "profiler/rule_profiler.h"
-#include "profiler/rule_profiler_defs.h"
+#include "rule_profiler.h"
+#include "rule_profiler_defs.h"
using namespace snort;
class ProfilerRuleDump : public AnalyzerCommand
{
public:
- ProfilerRuleDump(ControlConn* conn)
- : AnalyzerCommand(conn), nodes(), stats()
+ ProfilerRuleDump(ControlConn* conn, OutType out_type)
+ : AnalyzerCommand(conn), nodes(), stats(), out_type(out_type)
{
const SnortConfig* sc = SnortConfig::get_conf();
assert(sc);
const auto* config = SnortConfig::get_conf()->get_profiler();
assert(config);
- print_rule_profiler_stats(config->rule, stats, ctrlcon);
+ print_rule_profiler_stats(config->rule, stats, ctrlcon, out_type);
}
bool execute(Analyzer&, void**) override
private:
std::vector<HashNode*> nodes;
std::unordered_map<SigInfo*, OtnState> stats;
+ OutType out_type;
};
class ProfilerRuleReset : public AnalyzerCommand
return 0;
}
- main_broadcast_command(new ProfilerRuleDump(ctrlcon), ctrlcon);
+ const int num_of_args = lua_gettop(L);
+
+ if (!L or (num_of_args == 0))
+ {
+ main_broadcast_command(new ProfilerRuleDump(ctrlcon, OutType::OUTPUT_TABLE), ctrlcon);
+ return 0;
+ }
+
+ if (num_of_args > 1)
+ {
+ LogRespond(ctrlcon, "Too many arguments for rule_dump(output) command\n");
+ return 0;
+ }
+
+ const char* arg = lua_tostring(L, 1);
+
+ if (strcmp(arg, "json") == 0)
+ main_broadcast_command(new ProfilerRuleDump(ctrlcon, OutType::OUTPUT_JSON), ctrlcon);
+
+ else if (strcmp(arg, "table") == 0)
+ main_broadcast_command(new ProfilerRuleDump(ctrlcon, OutType::OUTPUT_TABLE), ctrlcon);
+
+ else
+ LogRespond(ctrlcon, "Invalid usage of rule_dump(output), argument can be 'table' or 'json'\n");
return 0;
}
+static const Parameter profiler_dump_params[] =
+{
+ { "output", Parameter::PT_ENUM, "table | json",
+ "table", "print rule statistics in table or json format" },
+
+ { nullptr, Parameter::PT_MAX, nullptr, nullptr, nullptr }
+};
+
static const Command profiler_cmds[] =
{
{ "rule_start", rule_profiling_start,
nullptr, "print rule profiler status" },
{ "rule_dump", rule_profiling_dump,
- nullptr, "print rule statistics" },
+ profiler_dump_params, "print rule statistics" },
{ nullptr, nullptr, nullptr, nullptr }
};
#include "framework/module.h"
-#include "profiler/profiler.h"
+#include "profiler.h"
class ProfilerModule : public snort::Module
{
#include <sstream>
#include <vector>
+#include "control/control.h"
// this include eventually leads to possible issues with std::chrono:
// 1. Undefined or garbage value returned to caller (rep count())
// 2. The left expression of the compound assignment is an uninitialized value.
// The computed value will also be garbage (duration& operator+=(const duration& __d))
#include "detection/detection_options.h" // ... FIXIT-W
-#include "control/control.h"
#include "detection/treenodes.h"
#include "hash/ghash.h"
#include "hash/xhash.h"
#include "main/thread_config.h"
#include "parser/parser.h"
#include "target_based/snort_protocols.h"
-#include "utils/stats.h"
#include "time/timersub.h"
+#include "utils/stats.h"
+#include "json_view.h"
#include "profiler_printer.h"
#include "profiler_stats_table.h"
#include "rule_profiler_defs.h"
+#include "table_view.h"
#ifdef UNIT_TEST
#include "catch/snort_catch.h"
namespace rule_stats
{
-static const StatsTable::Field fields[] =
-{
- { "#", 5, '\0', 0, std::ios_base::left },
- { "gid", 6, '\0', 0, std::ios_base::fmtflags() },
- { "sid", 6, '\0', 0, std::ios_base::fmtflags() },
- { "rev", 4, '\0', 0, std::ios_base::fmtflags() },
- { "checks", 10, '\0', 0, std::ios_base::fmtflags() },
- { "matches", 8, '\0', 0, std::ios_base::fmtflags() },
- { "alerts", 7, '\0', 0, std::ios_base::fmtflags() },
- { "time (us)", 10, '\0', 0, std::ios_base::fmtflags() },
- { "avg/check", 10, '\0', 1, std::ios_base::fmtflags() },
- { "avg/match", 10, '\0', 1, std::ios_base::fmtflags() },
- { "avg/non-match", 14, '\0', 1, std::ios_base::fmtflags() },
- { "timeouts", 9, '\0', 0, std::ios_base::fmtflags() },
- { "suspends", 9, '\0', 0, std::ios_base::fmtflags() },
- { "rule_time (%)", 14, '\0', 5, std::ios_base::fmtflags() },
- { nullptr, 0, '\0', 0, std::ios_base::fmtflags() }
-};
-
-struct View
-{
- OtnState state;
- SigInfo sig_info;
-
- hr_duration elapsed() const
- { return state.elapsed; }
-
- hr_duration elapsed_match() const
- { return state.elapsed_match; }
-
- hr_duration elapsed_no_match() const
- { return elapsed() - elapsed_match(); }
-
- uint64_t checks() const
- { return state.checks; }
-
- uint64_t matches() const
- { return state.matches; }
-
- uint64_t no_matches() const
- { return checks() - matches(); }
-
- uint64_t alerts() const
- { return state.alerts; }
-
- uint64_t timeouts() const
- { return state.latency_timeouts; }
-
- uint64_t suspends() const
- { return state.latency_suspends; }
-
- hr_duration time_per(hr_duration d, uint64_t v) const
- {
- if ( v == 0 )
- return CLOCK_ZERO;
-
- return hr_duration(d / v);
- }
-
- hr_duration avg_match() const
- { return time_per(elapsed_match(), matches()); }
-
- hr_duration avg_no_match() const
- { return time_per(elapsed_no_match(), no_matches()); }
-
- hr_duration avg_check() const
- { return time_per(elapsed(), checks()); }
-
- double rule_time_per(double total_time_usec) const
- {
- if (total_time_usec < 1.)
- return 100.0;
- return clock_usecs(TO_USECS(elapsed())) / total_time_usec * 100;
- }
-
- View(const OtnState& otn_state, const SigInfo* si = nullptr) :
- state(otn_state)
- {
- if ( si )
- // FIXIT-L does sig_info need to be initialized otherwise?
- sig_info = *si;
- }
-};
-
static const ProfilerSorter<View> sorters[] =
{
{ "", nullptr },
return entries;
}
-// FIXIT-L logic duplicated from ProfilerPrinter
-static void print_single_entry(ControlConn* ctrlcon, const View& v, unsigned n,
- double total_time_usec)
-{
- using std::chrono::duration_cast;
- using std::chrono::microseconds;
-
- std::ostringstream ss;
-
- {
- StatsTable table(fields, ss);
-
- table << StatsTable::ROW;
-
- table << n; // #
-
- table << v.sig_info.gid;
- table << v.sig_info.sid;
- table << v.sig_info.rev;
-
- table << v.checks();
- table << v.matches();
- table << v.alerts();
-
- table << clock_usecs(TO_USECS(v.elapsed()));
- table << clock_usecs(TO_USECS(v.avg_check()));
- table << clock_usecs(TO_USECS(v.avg_match()));
- table << clock_usecs(TO_USECS(v.avg_no_match()));
-
- table << v.timeouts();
- table << v.suspends();
- table << v.rule_time_per(total_time_usec);
- }
-
- LogRespond(ctrlcon, "%s", ss.str().c_str());
-}
-
-// FIXIT-L logic duplicated from ProfilerPrinter
-static void print_entries(ControlConn* ctrlcon, std::vector<View>& entries,
- ProfilerSorter<View>& sort, unsigned count)
-{
- std::ostringstream ss;
- RuleContext::set_end_time(get_time_curr());
- RuleContext::count_total_time();
-
- double total_time_usec =
- RuleContext::get_total_time()->tv_sec * 1000000.0 + RuleContext::get_total_time()->tv_usec;
-
- {
- StatsTable table(fields, ss);
-
- table << StatsTable::SEP;
-
- table << s_rule_table_title;
- if ( count )
- table << " (worst " << count;
- else
- table << " (all";
-
- if ( sort )
- table << ", sorted by " << sort.name;
-
- table << ")\n";
-
- table << StatsTable::HEADER;
- }
-
- LogRespond(ctrlcon, "%s", ss.str().c_str());
-
- if ( !count || count > entries.size() )
- count = entries.size();
-
- if ( sort )
- std::partial_sort(entries.begin(), entries.begin() + count, entries.end(), sort);
-
- for ( unsigned i = 0; i < count; ++i )
- print_single_entry(ctrlcon, entries[i], i + 1, total_time_usec);
-}
-
}
void print_rule_profiler_stats(const RuleProfilerConfig& config, const std::unordered_map<SigInfo*, OtnState>& stats,
- ControlConn* ctrlcon)
+ ControlConn* ctrlcon, OutType out_type)
{
auto entries = rule_stats::build_entries(stats);
auto sort = rule_stats::sorters[config.sort];
// FIXIT-L do we eventually want to be able print rule totals, too?
- print_entries(ctrlcon, entries, sort, config.count);
+ if ( out_type == OutType::OUTPUT_TABLE )
+ print_entries(ctrlcon, entries, sort, config.count);
+
+ else if ( out_type == OutType::OUTPUT_JSON )
+ print_json_entries(ctrlcon, entries, sort, config.count);
}
void show_rule_profiler_stats(const RuleProfilerConfig& config)
#ifndef RULE_PROFILER_H
#define RULE_PROFILER_H
-#include "detection/treenodes.h"
#include <unordered_map>
#include <vector>
+#include "detection/treenodes.h"
+#include "main/snort_config.h"
+#include "main/thread_config.h"
+
+#include "rule_profiler_defs.h"
+
struct RuleProfilerConfig;
class ControlConn;
namespace snort
}
void prepare_rule_profiler_stats(std::vector<snort::HashNode*>&, std::unordered_map<SigInfo*, OtnState>&, unsigned);
-void print_rule_profiler_stats(const RuleProfilerConfig&, const std::unordered_map<SigInfo*, OtnState>&, ControlConn* = nullptr);
+void print_rule_profiler_stats(const RuleProfilerConfig&, const std::unordered_map<SigInfo*, OtnState>&,
+ ControlConn* = nullptr, OutType = OutType::OUTPUT_TABLE);
void show_rule_profiler_stats(const RuleProfilerConfig&);
void reset_rule_profiler_stats();
void reset_thread_rule_profiler_stats(std::vector<snort::HashNode*>&, unsigned);
+namespace rule_stats
+{
+
+struct View
+{
+ OtnState state;
+ SigInfo sig_info;
+
+ hr_duration elapsed() const
+ { return state.elapsed; }
+
+ hr_duration elapsed_match() const
+ { return state.elapsed_match; }
+
+ hr_duration elapsed_no_match() const
+ { return elapsed() - elapsed_match(); }
+
+ uint64_t checks() const
+ { return state.checks; }
+
+ uint64_t matches() const
+ { return state.matches; }
+
+ uint64_t no_matches() const
+ { return checks() - matches(); }
+
+ uint64_t alerts() const
+ { return state.alerts; }
+
+ uint64_t timeouts() const
+ { return state.latency_timeouts; }
+
+ uint64_t suspends() const
+ { return state.latency_suspends; }
+
+ hr_duration time_per(hr_duration d, uint64_t v) const
+ {
+ if ( v == 0 )
+ return CLOCK_ZERO;
+
+ return hr_duration(d / v);
+ }
+
+ hr_duration avg_match() const
+ { return time_per(elapsed_match(), matches()); }
+
+ hr_duration avg_no_match() const
+ { return time_per(elapsed_no_match(), no_matches()); }
+
+ hr_duration avg_check() const
+ { return time_per(elapsed(), checks()); }
+
+ double rule_time_per(double total_time_usec) const
+ {
+ if (total_time_usec < 1.)
+ return 100.0;
+ return clock_usecs(TO_USECS(elapsed())) / total_time_usec * 100;
+ }
+
+ View(const OtnState& otn_state, const SigInfo* si = nullptr) :
+ state(otn_state)
+ {
+ if ( si )
+ // FIXIT-L does sig_info need to be initialized otherwise?
+ sig_info = *si;
+ }
+};
+
+}
#endif
struct dot_node_state_t;
+enum OutType
+{
+ OUTPUT_TABLE = 0,
+ OUTPUT_JSON
+};
+
struct RuleProfilerConfig
{
enum Sort
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2015-2023 Cisco and/or its affiliates. All rights reserved.
+//
+// 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
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// table_view.cc author Joel Cornett <jocornet@cisco.com>
+
+#if HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "table_view.h"
+
+#include <sstream>
+#include <vector>
+
+#include "control/control.h"
+#include "detection/treenodes.h"
+#include "utils/stats.h"
+
+#include "profiler_printer.h"
+#include "profiler_stats_table.h"
+#include "rule_profiler.h"
+
+#define s_rule_table_title "rule profile"
+
+using namespace snort;
+
+const StatsTable::Field fields[] =
+{
+ { "#", 5, '\0', 0, std::ios_base::left },
+ { "gid", 6, '\0', 0, std::ios_base::fmtflags() },
+ { "sid", 6, '\0', 0, std::ios_base::fmtflags() },
+ { "rev", 4, '\0', 0, std::ios_base::fmtflags() },
+ { "checks", 10, '\0', 0, std::ios_base::fmtflags() },
+ { "matches", 8, '\0', 0, std::ios_base::fmtflags() },
+ { "alerts", 7, '\0', 0, std::ios_base::fmtflags() },
+ { "time (us)", 10, '\0', 0, std::ios_base::fmtflags() },
+ { "avg/check", 10, '\0', 1, std::ios_base::fmtflags() },
+ { "avg/match", 10, '\0', 1, std::ios_base::fmtflags() },
+ { "avg/non-match", 14, '\0', 1, std::ios_base::fmtflags() },
+ { "timeouts", 9, '\0', 0, std::ios_base::fmtflags() },
+ { "suspends", 9, '\0', 0, std::ios_base::fmtflags() },
+ { "rule_time (%)", 14, '\0', 5, std::ios_base::fmtflags() },
+ { nullptr, 0, '\0', 0, std::ios_base::fmtflags() }
+};
+
+// FIXIT-L logic duplicated from ProfilerPrinter
+static void print_single_entry(ControlConn* ctrlcon, const rule_stats::View& v, unsigned n,
+ double total_time_usec)
+{
+ using std::chrono::duration_cast;
+ using std::chrono::microseconds;
+
+ std::ostringstream ss;
+
+ {
+ StatsTable table(fields, ss);
+
+ table << StatsTable::ROW;
+
+ table << n; // #
+
+ table << v.sig_info.gid;
+ table << v.sig_info.sid;
+ table << v.sig_info.rev;
+
+ table << v.checks();
+ table << v.matches();
+ table << v.alerts();
+
+ table << clock_usecs(TO_USECS(v.elapsed()));
+ table << clock_usecs(TO_USECS(v.avg_check()));
+ table << clock_usecs(TO_USECS(v.avg_match()));
+ table << clock_usecs(TO_USECS(v.avg_no_match()));
+
+ table << v.timeouts();
+ table << v.suspends();
+ table << v.rule_time_per(total_time_usec);
+ }
+
+ LogRespond(ctrlcon, "%s", ss.str().c_str());
+}
+
+// FIXIT-L logic duplicated from ProfilerPrinter
+void print_entries(ControlConn* ctrlcon, std::vector<rule_stats::View>& entries,
+ ProfilerSorter<rule_stats::View>& sort, unsigned count)
+{
+ std::ostringstream ss;
+ RuleContext::set_end_time(get_time_curr());
+ RuleContext::count_total_time();
+
+ double total_time_usec =
+ RuleContext::get_total_time()->tv_sec * 1000000.0 + RuleContext::get_total_time()->tv_usec;
+
+ StatsTable table(fields, ss);
+
+ table << StatsTable::SEP;
+
+ table << s_rule_table_title;
+ if ( count )
+ table << " (worst " << count;
+ else
+ table << " (all";
+
+ if ( sort )
+ table << ", sorted by " << sort.name;
+
+ table << ")\n";
+
+ table << StatsTable::HEADER;
+
+ LogRespond(ctrlcon, "%s", ss.str().c_str());
+
+ if ( !count || count > entries.size() )
+ count = entries.size();
+
+ if ( sort )
+ std::partial_sort(entries.begin(), entries.begin() + count, entries.end(), sort);
+
+ for ( unsigned i = 0; i < count; ++i )
+ print_single_entry(ctrlcon, entries[i], i + 1, total_time_usec);
+
+}
--- /dev/null
+//--------------------------------------------------------------------------
+// Copyright (C) 2015-2023 Cisco and/or its affiliates. All rights reserved.
+//
+// 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
+// WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+// General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License along
+// with this program; if not, write to the Free Software Foundation, Inc.,
+// 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+//--------------------------------------------------------------------------
+
+// table_view.h author Joel Cornett <jocornet@cisco.com>
+
+#ifndef TABLE_VIEW_H
+#define TABLE_VIEW_H
+
+#include <vector>
+
+#include "main/snort_config.h"
+
+#include "profiler_printer.h"
+#include "rule_profiler.h"
+
+void print_entries(ControlConn*, std::vector<rule_stats::View>&, ProfilerSorter<rule_stats::View>&, unsigned);
+
+#endif