diff --git a/userspace/falco/CMakeLists.txt b/userspace/falco/CMakeLists.txt
index 41988076..9314d051 100644
--- a/userspace/falco/CMakeLists.txt
+++ b/userspace/falco/CMakeLists.txt
@@ -9,7 +9,7 @@ include_directories("${CURL_INCLUDE_DIR}")
include_directories("${YAMLCPP_INCLUDE_DIR}")
include_directories("${DRAIOS_DEPENDENCIES_DIR}/yaml-${DRAIOS_YAML_VERSION}/target/include")
-add_executable(falco configuration.cpp logger.cpp falco_outputs.cpp falco.cpp)
+add_executable(falco configuration.cpp logger.cpp falco_outputs.cpp statsfilewriter.cpp falco.cpp)
target_link_libraries(falco falco_engine sinsp)
target_link_libraries(falco
diff --git a/userspace/falco/falco.cpp b/userspace/falco/falco.cpp
index e15bf2b2..b55b2c6a 100644
--- a/userspace/falco/falco.cpp
+++ b/userspace/falco/falco.cpp
@@ -36,6 +36,7 @@ along with falco. If not, see .
#include "configuration.h"
#include "falco_engine.h"
#include "config_falco.h"
+#include "statsfilewriter.h"
bool g_terminate = false;
//
@@ -97,6 +98,8 @@ static void usage()
" -P, --pidfile When run as a daemon, write pid to specified file\n"
" -r Rules file (defaults to value set in configuration file, or /etc/falco_rules.yaml).\n"
" Can be specified multiple times to read from multiple files.\n"
+ " -s If specified, write statistics related to falco's reading/processing of events\n"
+ " to this file. (Only useful in live mode).\n"
" -v Verbose output.\n"
"\n"
);
@@ -124,11 +127,23 @@ std::list cmdline_options;
//
uint64_t do_inspect(falco_engine *engine,
falco_outputs *outputs,
- sinsp* inspector)
+ sinsp* inspector,
+ string &stats_filename)
{
uint64_t num_evts = 0;
int32_t res;
sinsp_evt* ev;
+ StatsFileWriter writer;
+
+ if (stats_filename != "")
+ {
+ string errstr;
+
+ if (!writer.init(inspector, stats_filename, 5, errstr))
+ {
+ throw falco_exception(errstr);
+ }
+ }
//
// Loop through the events
@@ -138,6 +153,8 @@ uint64_t do_inspect(falco_engine *engine,
res = inspector->next(&ev);
+ writer.handle();
+
if (g_terminate)
{
break;
@@ -202,6 +219,7 @@ int falco_init(int argc, char **argv)
string pidfilename = "/var/run/falco.pid";
bool describe_all_rules = false;
string describe_rule = "";
+ string stats_filename = "";
bool verbose = false;
bool all_events = false;
string* k8s_api = 0;
@@ -246,7 +264,7 @@ int falco_init(int argc, char **argv)
// Parse the args
//
while((op = getopt_long(argc, argv,
- "hc:AdD:e:k:K:Ll:m:o:P:p:r:vw:",
+ "hc:AdD:e:k:K:Ll:m:o:P:p:r:s:vw:",
long_options, &long_index)) != -1)
{
switch(op)
@@ -318,6 +336,9 @@ int falco_init(int argc, char **argv)
case 'r':
rules_filenames.push_back(optarg);
break;
+ case 's':
+ stats_filename = optarg;
+ break;
case 'v':
verbose = true;
break;
@@ -588,7 +609,8 @@ int falco_init(int argc, char **argv)
num_evts = do_inspect(engine,
outputs,
- inspector);
+ inspector,
+ stats_filename);
duration = ((double)clock()) / CLOCKS_PER_SEC - duration;
diff --git a/userspace/falco/statsfilewriter.cpp b/userspace/falco/statsfilewriter.cpp
new file mode 100644
index 00000000..e50032b5
--- /dev/null
+++ b/userspace/falco/statsfilewriter.cpp
@@ -0,0 +1,90 @@
+#include
+#include
+
+#include "statsfilewriter.h"
+
+using namespace std;
+
+static bool g_save_stats = false;
+static void timer_handler (int signum)
+{
+ g_save_stats = true;
+}
+
+StatsFileWriter::StatsFileWriter()
+ : m_num_stats(0), m_inspector(NULL)
+{
+}
+
+StatsFileWriter::~StatsFileWriter()
+{
+ m_output.close();
+}
+
+bool StatsFileWriter::init(sinsp *inspector, string &filename, uint32_t interval_sec, string &errstr)
+{
+ struct itimerval timer;
+ struct sigaction handler;
+
+ m_inspector = inspector;
+
+ m_output.exceptions ( ofstream::failbit | ofstream::badbit );
+ m_output.open(filename, ios_base::app);
+
+ memset (&handler, 0, sizeof (handler));
+ handler.sa_handler = &timer_handler;
+ if (sigaction(SIGALRM, &handler, NULL) == -1)
+ {
+ errstr = string("Could not set up signal handler for periodic timer: ") + strerror(errno);
+ return false;
+ }
+
+ timer.it_value.tv_sec = interval_sec;
+ timer.it_value.tv_usec = 0;
+ timer.it_interval = timer.it_value;
+ if (setitimer(ITIMER_REAL, &timer, NULL) == -1)
+ {
+ errstr = string("Could not set up periodic timer: ") + strerror(errno);
+ return false;
+ }
+
+ return true;
+}
+
+void StatsFileWriter::handle()
+{
+ if (g_save_stats)
+ {
+ scap_stats cstats;
+ scap_stats delta;
+
+ g_save_stats = false;
+ m_num_stats++;
+ m_inspector->get_capture_stats(&cstats);
+
+ if(m_num_stats == 1)
+ {
+ delta = cstats;
+ }
+ else
+ {
+ delta.n_evts = cstats.n_evts - m_last_stats.n_evts;
+ delta.n_drops = cstats.n_drops - m_last_stats.n_drops;
+ delta.n_preemptions = cstats.n_preemptions - m_last_stats.n_preemptions;
+ }
+
+ m_output << "{\"sample\": " << m_num_stats <<
+ ", \"cur\": {" <<
+ "\"events\": " << cstats.n_evts <<
+ ", \"drops\": " << cstats.n_drops <<
+ ", \"preemptions\": " << cstats.n_preemptions <<
+ "}, \"delta\": {" <<
+ "\"events\": " << delta.n_evts <<
+ ", \"drops\": " << delta.n_drops <<
+ ", \"preemptions\": " << delta.n_preemptions <<
+ "}, \"drop_pct\": " << (delta.n_evts == 0 ? 0 : (100.0*delta.n_drops/delta.n_evts)) <<
+ "}," << endl;
+
+ m_last_stats = cstats;
+ }
+}
diff --git a/userspace/falco/statsfilewriter.h b/userspace/falco/statsfilewriter.h
new file mode 100644
index 00000000..8c95a0aa
--- /dev/null
+++ b/userspace/falco/statsfilewriter.h
@@ -0,0 +1,30 @@
+#pragma once
+
+#include
+
+#include
+
+// Periodically collects scap stats files and writes them to a file as
+// json.
+
+class StatsFileWriter {
+public:
+ StatsFileWriter();
+ virtual ~StatsFileWriter();
+
+ // Returns success as bool. On false fills in errstr.
+ bool init(sinsp *inspector, std::string &filename,
+ uint32_t interval_sec,
+ string &errstr);
+
+ // Should be called often (like for each event in a sinsp
+ // loop).
+ void handle();
+
+protected:
+ uint32_t m_num_stats;
+ sinsp *m_inspector;
+ std::ofstream m_output;
+
+ scap_stats m_last_stats;
+};