cleanup(userspace/falco): address reviewers comments

* renaming to `metrics` for technical clarity
* adopt Prometheus like metrics interval settings

Co-authored-by: Jason Dellaluce <jasondellaluce@gmail.com>
Signed-off-by: Melissa Kilby <melissa.kilby.oss@gmail.com>
This commit is contained in:
Melissa Kilby
2023-05-16 11:52:47 +00:00
committed by poiana
parent 134d2630e9
commit e37027a1d0
12 changed files with 308 additions and 113 deletions

View File

@@ -15,6 +15,7 @@ set(FALCO_ENGINE_SOURCE_FILES
falco_engine.cpp
falco_load_result.cpp
falco_utils.cpp
falco_metrics.cpp
json_evt.cpp
evttype_index_ruleset.cpp
formats.cpp

View File

@@ -0,0 +1,145 @@
/*
Copyright (C) 2023 The Falco Authors.
This file is part of falco.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include <cstring>
#include <iomanip>
#include "falco_utils.h"
#include "utils.h"
#include "banned.h" // This raises a compilation error when certain functions are used
#include <re2/re2.h>
// these follow the POSIX standard
#define RGX_PROMETHEUS_TIME_DURATION_PATTERN "([0-9]+[a-z]+)"
#define RGX_PROMETHEUS_NUMBER_PATTERN "([0-9]+)"
#define RGX_PROMETHEUS_UNIT_PATTERN "([a-z]+)"
// using pre-compiled regex for better performance
static re2::RE2 s_rgx_prometheus_time_duration(RGX_PROMETHEUS_TIME_DURATION_PATTERN, re2::RE2::POSIX);
static re2::RE2 s_rgx_prometheus_number(RGX_PROMETHEUS_NUMBER_PATTERN, re2::RE2::POSIX);
static re2::RE2 s_rgx_prometheus_unit(RGX_PROMETHEUS_UNIT_PATTERN, re2::RE2::POSIX);
// Prometheus time durations: https://prometheus.io/docs/prometheus/latest/querying/basics/#time-durations
#define PROMETHEUS_UNIT_Y "y" ///> assuming a year has always 365d
#define PROMETHEUS_UNIT_W "w" ///> assuming a week has always 7d
#define PROMETHEUS_UNIT_D "d" ///> assuming a day has always 24h
#define PROMETHEUS_UNIT_H "h" ///> hour
#define PROMETHEUS_UNIT_M "m" ///> minute
#define PROMETHEUS_UNIT_S "s" ///> second
#define PROMETHEUS_UNIT_MS "ms" ///> millisecond
// standard time unit conversions to milliseconds
#define SECOND_TO_MS 1000
#define MINUTE_TO_MS SECOND_TO_MS * 60
#define HOUR_TO_MS MINUTE_TO_MS * 60
#define DAY_TO_MS HOUR_TO_MS * 24
#define WEEK_TO_MS DAY_TO_MS * 7
#define YEAR_TO_MS WEEK_TO_MS * 365
namespace falco
{
namespace metrics
{
uint64_t parse_metrics_interval(std::string interval_str)
{
uint64_t interval = 0;
/* Sanitize user input, remove possible whitespaces. */
interval_str.erase(remove_if(interval_str.begin(), interval_str.end(), isspace), interval_str.end());
if(!interval_str.empty())
{
/* Option 1: Passing interval directly in ms. Will be deprecated in the future. */
if(std::all_of(interval_str.begin(), interval_str.end(), ::isdigit))
{
/* todo: deprecate for Falco 0.36. */
interval = std::stoull(interval_str, nullptr, 0);
std::cerr << "Metrics interval was passed as numeric value without Prometheus time unit, this option will no longer be supported starting Falco 0.36" << std::endl;
}
/* Option 2: Passing a Prometheus time duration.
* https://prometheus.io/docs/prometheus/latest/querying/basics/#time-durations
*/
else
{
re2::StringPiece input(interval_str);
std::string r;
std::string cur_interval_str;
uint64_t cur_interval;
std::string cur_unit;
bool valid = true;
/* Note that parsing is done at start up only. */
while(re2::RE2::FindAndConsume(&input, s_rgx_prometheus_time_duration, &r))
{
RE2::Extract(r, s_rgx_prometheus_number, "\\1", &cur_interval_str);
cur_interval = std::stoull(cur_interval_str, nullptr, 0);
if(cur_interval > 0)
{
RE2::Extract(r, s_rgx_prometheus_unit, "\\1", &cur_unit);
if(cur_unit == PROMETHEUS_UNIT_MS)
{
interval += cur_interval;
}
else if(cur_unit == PROMETHEUS_UNIT_S)
{
interval += cur_interval * SECOND_TO_MS;
}
else if(cur_unit == PROMETHEUS_UNIT_M)
{
interval += cur_interval * MINUTE_TO_MS;
}
else if(cur_unit == PROMETHEUS_UNIT_H)
{
interval += cur_interval * HOUR_TO_MS;
}
else if(cur_unit == PROMETHEUS_UNIT_D)
{
interval += cur_interval * DAY_TO_MS;
}
else if(cur_unit == PROMETHEUS_UNIT_W)
{
interval += cur_interval * WEEK_TO_MS;
}
else if(cur_unit == PROMETHEUS_UNIT_Y)
{
interval += cur_interval * YEAR_TO_MS;
}
else
{
valid = false;
}
}
else
{
valid = false;
}
}
if (!valid)
{
// invalidate if any invalid unit or no corresponding numeric value was found
interval = 0;
}
}
}
return interval;
}
} // namespace metrics
} // namespace falco

View File

@@ -0,0 +1,49 @@
/*
Copyright (C) 2023The Falco Authors.
This file is part of falco.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#pragma once
#include <sstream>
#include <fstream>
#include <iostream>
#include <string>
#include <thread>
#include <unordered_set>
#include <set>
#include <vector>
#include <string>
#ifdef __GNUC__
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
#else
#define likely(x) (x)
#define unlikely(x) (x)
#endif
namespace falco
{
namespace metrics
{
uint64_t parse_metrics_interval(std::string interval_str);
} // namespace metrics
} // namespace falco

View File

@@ -401,50 +401,41 @@ static std::shared_ptr<stats_writer> init_stats_writer(const options& opts, std:
{
auto statsw = std::make_shared<stats_writer>(outputs, config);
std::string err;
uint64_t stats_interval_ms = 0;
if (config->m_stats_v2_enabled && config->m_stats_v2_stats_interval_preset > 0)
{
uint16_t index = config->m_stats_v2_stats_interval_preset;
if(index <= MAX_STATS_PRESET_INDEX)
{
/* Index 0 reserved, milliseconds representation for 15min, 30min, 1hr, 4hrs, 6hrs, 12hrs. */
std::vector<uint64_t> vect{0LLU, 900000LU, 1800000LU, 3600000LU, 14400000LU, 21600000LU, 43200000LU};
stats_interval_ms = vect[index];
}
else
{
// todo: warning message
stats_interval_ms = 0;
}
}
uint64_t interval = 0;
/* Continue cmd args support and old defaults for backward compatibility, scheduled for deprecation. */
if (stats_interval_ms == 0 && opts.stats_interval > 0)
if(!config->m_metrics_enabled && opts.stats_interval > 0)
{
stats_interval_ms = opts.stats_interval;
interval = opts.stats_interval;
}
/* New config. Exact stats_interval_ms in falco.yaml overrides presets. */
if (config->m_stats_v2_enabled && config->m_stats_v2_stats_interval_ms > 0)
/* New metrics and configs over falco.yaml. */
else if(config->m_metrics_enabled && config->m_metrics_interval > 0)
{
stats_interval_ms = config->m_stats_v2_stats_interval_ms;
interval = config->m_metrics_interval;
}
if (stats_interval_ms > 0)
/* Enforce minimum bound of 100ms. */
if(interval > 100)
{
if (!stats_writer::init_ticker(stats_interval_ms, err))
if(!stats_writer::init_ticker(interval, err))
{
throw falco_exception(err);
}
/* Only support new info message for new metrics and configs over falco.yaml. */
if(config->m_metrics_enabled)
{
falco_logger::log(LOG_INFO, "Setting metrics interval to " + config->m_metrics_interval_str + ", equivalent to " + std::to_string(interval) + " (ms)\n");
}
}
/* Continue cmd args support for backward compatibility, scheduled for deprecation. */
if (!config->m_stats_v2_enabled && !opts.stats_filename.empty())
if(!config->m_metrics_enabled && !opts.output_file.empty())
{
statsw.reset(new stats_writer(opts.stats_filename, outputs, config));
statsw.reset(new stats_writer(opts.output_file, outputs, config));
}
/* New config. */
else if (config->m_stats_v2_enabled && !config->m_stats_v2_stats_filename.empty())
/* New metrics and configs over falco.yaml. */
else if(config->m_metrics_enabled && !config->m_metrics_output_file.empty())
{
statsw.reset(new stats_writer(config->m_stats_v2_stats_filename, outputs, config));
statsw.reset(new stats_writer(config->m_metrics_output_file, outputs, config));
}
return statsw;
}

View File

@@ -219,7 +219,7 @@ void options::define(cxxopts::Options& opts)
("p,print", "Add additional information to each falco notification's output.\nWith -pc or -pcontainer will use a container-friendly format.\nWith -pk or -pkubernetes will use a kubernetes-friendly format.\nAdditionally, specifying -pc/-pk will change the interpretation of %container.info in rule output fields.", cxxopts::value(print_additional), "<output_format>")
("P,pidfile", "When run as a daemon, write pid to specified file", cxxopts::value(pidfilename)->default_value("/var/run/falco.pid"), "<pid_file>")
("r", "Rules file/directory (defaults to value set in configuration file, or /etc/falco_rules.yaml). This option can be passed multiple times to read from multiple files/directories.", cxxopts::value<std::vector<std::string>>(), "<rules_file>")
("s", "If specified, append statistics related to Falco's reading/processing of events to this file (only useful in live mode).", cxxopts::value(stats_filename), "<stats_file>")
("s", "If specified, append statistics related to Falco's reading/processing of events to this file (only useful in live mode).", cxxopts::value(output_file), "<stats_file>")
("stats-interval", "When using -s <stats_file>, write statistics every <msec> ms. This uses signals, so don't recommend intervals below 200 ms. Defaults to 5000 (5 seconds).", cxxopts::value(stats_interval)->default_value("5000"), "<msec>")
("S,snaplen", "Capture the first <len> bytes of each I/O buffer. By default, the first 80 bytes are captured. Use this option with caution, it can generate huge trace files.", cxxopts::value(snaplen)->default_value("0"), "<len>")
("support", "Print support information including version, rules files used, etc. and exit.", cxxopts::value(print_support)->default_value("false"))

View File

@@ -70,7 +70,7 @@ public:
std::string pidfilename;
// Rules list as passed by the user, via cmdline option '-r'
std::list<std::string> rules_filenames;
std::string stats_filename;
std::string output_file;
uint64_t stats_interval;
uint64_t snaplen;
bool print_support;

View File

@@ -26,6 +26,7 @@ limitations under the License.
#include <sys/stat.h>
#include <unistd.h>
#include "falco_utils.h"
#include "falco_metrics.h"
#include "configuration.h"
#include "logger.h"
@@ -60,7 +61,7 @@ falco_configuration::falco_configuration():
m_cpus_for_each_syscall_buffer(2),
m_syscall_drop_failed_exit(false),
m_base_syscalls_repair(false),
m_stats_v2_enabled(false)
m_metrics_enabled(false)
{
init({});
}
@@ -339,15 +340,15 @@ void falco_configuration::load_yaml(const std::string& config_name, const yaml_h
config.get_sequence<std::unordered_set<std::string>>(m_base_syscalls_custom_set, std::string("base_syscalls.custom_set"));
m_base_syscalls_repair = config.get_scalar<bool>("base_syscalls.repair", false);
m_stats_v2_enabled = config.get_scalar<bool>("stats_v2.enabled", false);
m_stats_v2_stats_interval_preset = config.get_scalar<uint16_t>("stats_v2.stats_interval_preset", 0);
m_stats_v2_stats_interval_ms = config.get_scalar<uint64_t>("stats_v2.stats_interval_ms", 0);
m_stats_v2_stats_internal_rule = config.get_scalar<bool>("stats_v2.stats_internal_rule", true);
m_stats_v2_stats_filename = config.get_scalar<std::string>("stats_v2.stats_filename", "");
m_stats_v2_include_resource_utilization = config.get_scalar<bool>("stats_v2.include_resource_utilization", true);
m_stats_v2_include_kernel_evts_counters = config.get_scalar<bool>("stats_v2.include_kernel_evts_counters", true);
m_stats_v2_include_libbpf_stats = config.get_scalar<bool>("stats_v2.include_libbpf_stats", true);
m_stats_v2_convert_memory_to_mb = config.get_scalar<bool>("stats_v2.convert_memory_to_mb", true);
m_metrics_enabled = config.get_scalar<bool>("metrics.enabled", false);
m_metrics_interval_str = config.get_scalar<std::string>("metrics.interval", "0");
m_metrics_interval = falco::metrics::parse_metrics_interval(m_metrics_interval_str);
m_metrics_stats_rule_enabled = config.get_scalar<bool>("metrics.stats_rule_enabled", true);
m_metrics_output_file = config.get_scalar<std::string>("metrics.output_file", "");
m_metrics_resource_utilization_enabled = config.get_scalar<bool>("metrics.resource_utilization_enabled", true);
m_metrics_kernel_event_counters_enabled = config.get_scalar<bool>("metrics.kernel_event_counters_enabled", true);
m_metrics_libbpf_stats_enabled = config.get_scalar<bool>("metrics.libbpf_stats_enabled", true);
m_metrics_convert_memory_to_mb = config.get_scalar<bool>("metrics.convert_memory_to_mb", true);
std::vector<std::string> load_plugins;

View File

@@ -112,16 +112,16 @@ public:
std::unordered_set<std::string> m_base_syscalls_custom_set;
bool m_base_syscalls_repair;
// stats_v2 configs
bool m_stats_v2_enabled;
uint16_t m_stats_v2_stats_interval_preset;
uint64_t m_stats_v2_stats_interval_ms;
bool m_stats_v2_stats_internal_rule;
std::string m_stats_v2_stats_filename;
bool m_stats_v2_include_resource_utilization;
bool m_stats_v2_include_kernel_evts_counters;
bool m_stats_v2_include_libbpf_stats;
bool m_stats_v2_convert_memory_to_mb;
// metrics configs
bool m_metrics_enabled;
std::string m_metrics_interval_str;
uint64_t m_metrics_interval;
bool m_metrics_stats_rule_enabled;
std::string m_metrics_output_file;
bool m_metrics_resource_utilization_enabled;
bool m_metrics_kernel_event_counters_enabled;
bool m_metrics_libbpf_stats_enabled;
bool m_metrics_convert_memory_to_mb;
std::vector<plugin_config> m_plugins;

View File

@@ -26,6 +26,7 @@ limitations under the License.
#include "logger.h"
#include "banned.h" // This raises a compilation error when certain functions are used
#include "config_falco.h"
#include <re2/re2.h>
// note: ticker_t is an uint16_t, which is enough because we don't care about
// overflows here. Threads calling stats_writer::handle() will just
@@ -157,7 +158,7 @@ stats_writer::collector::collector(std::shared_ptr<stats_writer> writer)
{
}
std::map<std::string, std::string> stats_writer::collector::get_stats_v2_output_fields_wrapper(std::shared_ptr<sinsp> inspector, uint64_t now, std::string src, uint64_t num_evts, double stats_snapshot_time_delta_sec)
std::map<std::string, std::string> stats_writer::collector::get_metrics_output_fields_wrapper(std::shared_ptr<sinsp> inspector, uint64_t now, std::string src, uint64_t num_evts, double stats_snapshot_time_delta_sec)
{
std::map<std::string, std::string> output_fields;
const scap_agent_info* agent_info = inspector->get_agent_info();
@@ -203,7 +204,7 @@ std::map<std::string, std::string> stats_writer::collector::get_stats_v2_output_
}
std::map<std::string, std::string> stats_writer::collector::get_stats_v2_output_fields_additional(std::shared_ptr<sinsp> inspector, std::map<std::string, std::string> output_fields, double stats_snapshot_time_delta_sec, std::string src)
std::map<std::string, std::string> stats_writer::collector::get_metrics_output_fields_additional(std::shared_ptr<sinsp> inspector, std::map<std::string, std::string> output_fields, double stats_snapshot_time_delta_sec, std::string src)
{
const scap_agent_info* agent_info = inspector->get_agent_info();
const scap_machine_info* machine_info = inspector->get_machine_info();
@@ -212,7 +213,7 @@ std::map<std::string, std::string> stats_writer::collector::get_stats_v2_output_
/* Resource utilization, CPU and memory usage etc. */
uint32_t nstats = 0;
int32_t rc = 0;
if (m_writer->m_config->m_stats_v2_include_resource_utilization)
if (m_writer->m_config->m_metrics_resource_utilization_enabled)
{
const scap_stats_v2* utilization;
auto buffer = inspector->get_sinsp_stats_v2_buffer();
@@ -225,7 +226,7 @@ std::map<std::string, std::string> stats_writer::collector::get_stats_v2_output_
switch(utilization[stat].type)
{
case STATS_VALUE_TYPE_U64:
if (m_writer->m_config->m_stats_v2_convert_memory_to_mb && strncmp(utilization[stat].name, "container_memory_used", 21) == 0)
if (m_writer->m_config->m_metrics_convert_memory_to_mb && strncmp(utilization[stat].name, "container_memory_used", 21) == 0)
{
output_fields[utilization[stat].name] = std::to_string(utilization[stat].value.u64 / (double)1024 / (double)1024);
}
@@ -235,7 +236,7 @@ std::map<std::string, std::string> stats_writer::collector::get_stats_v2_output_
}
break;
case STATS_VALUE_TYPE_U32:
if (m_writer->m_config->m_stats_v2_convert_memory_to_mb && strncmp(utilization[stat].name, "memory_", 7) == 0)
if (m_writer->m_config->m_metrics_convert_memory_to_mb && strncmp(utilization[stat].name, "memory_", 7) == 0)
{
output_fields[utilization[stat].name] = std::to_string(utilization[stat].value.u32 / (double)1024);
}
@@ -264,11 +265,11 @@ std::map<std::string, std::string> stats_writer::collector::get_stats_v2_output_
rc = 0;
uint32_t flags = 0;
if (m_writer->m_config->m_stats_v2_include_kernel_evts_counters)
if (m_writer->m_config->m_metrics_kernel_event_counters_enabled)
{
flags |= PPM_SCAP_STATS_KERNEL_COUNTERS;
}
if (m_writer->m_config->m_stats_v2_include_libbpf_stats && !inspector->check_current_engine(KMOD_ENGINE) && (machine_info->flags & PPM_BPF_STATS_ENABLED))
if (m_writer->m_config->m_metrics_libbpf_stats_enabled && !inspector->check_current_engine(KMOD_ENGINE) && (machine_info->flags & PPM_BPF_STATS_ENABLED))
{
flags |= PPM_SCAP_STATS_LIBBPF_STATS;
}
@@ -316,7 +317,7 @@ std::map<std::string, std::string> stats_writer::collector::get_stats_v2_output_
void stats_writer::collector::collect(std::shared_ptr<sinsp> inspector, const std::string &src, uint64_t num_evts)
{
if (m_writer->m_config->m_stats_v2_enabled || m_writer->has_output())
if (m_writer->m_config->m_metrics_enabled || m_writer->has_output())
{
/* Collect stats / metrics once per ticker period. */
auto tick = stats_writer::get_ticker();
@@ -332,15 +333,15 @@ void stats_writer::collector::collect(std::shared_ptr<sinsp> inspector, const st
double stats_snapshot_time_delta_sec = (stats_snapshot_time_delta / (double)ONE_SECOND_IN_NS);
/* Get respective metrics output_fields. */
std::map<std::string, std::string> output_fields = stats_writer::collector::get_stats_v2_output_fields_wrapper(inspector, now, src, num_evts, stats_snapshot_time_delta_sec);
output_fields = stats_writer::collector::get_stats_v2_output_fields_additional(inspector, output_fields, stats_snapshot_time_delta_sec, src);
std::map<std::string, std::string> output_fields = stats_writer::collector::get_metrics_output_fields_wrapper(inspector, now, src, num_evts, stats_snapshot_time_delta_sec);
output_fields = stats_writer::collector::get_metrics_output_fields_additional(inspector, output_fields, stats_snapshot_time_delta_sec, src);
/* Pipe to respective output. */
if (m_writer->m_config->m_stats_v2_enabled && m_writer->m_config->m_stats_v2_stats_internal_rule && m_writer->m_outputs)
if (m_writer->m_config->m_metrics_enabled && m_writer->m_config->m_metrics_stats_rule_enabled && m_writer->m_outputs)
{
std::string rule = "Falco internal: resource utilization stats metrics";
std::string msg = "";
m_writer->m_outputs->handle_msg(now, falco_common::PRIORITY_DEBUG, msg, rule, output_fields);
m_writer->m_outputs->handle_msg(now, falco_common::PRIORITY_INFORMATIONAL, msg, rule, output_fields);
}
if (m_writer->has_output())
{

View File

@@ -63,14 +63,14 @@ public:
void collect(std::shared_ptr<sinsp> inspector, const std::string& src, uint64_t num_evts);
/*!
\brief Collect snapshot stats v2 wrapper fields as internal rule formatted output fields.
\brief Collect snapshot metrics wrapper fields as internal rule formatted output fields.
*/
std::map<std::string, std::string> get_stats_v2_output_fields_wrapper(std::shared_ptr<sinsp> inspector, uint64_t now, std::string src, uint64_t num_evts, double stats_snapshot_time_delta_sec);
std::map<std::string, std::string> get_metrics_output_fields_wrapper(std::shared_ptr<sinsp> inspector, uint64_t now, std::string src, uint64_t num_evts, double stats_snapshot_time_delta_sec);
/*!
\brief Collect snapshot stats v2 syscalls related metrics as internal rule formatted output fields.
\brief Collect snapshot metrics syscalls related metrics as internal rule formatted output fields.
*/
std::map<std::string, std::string> get_stats_v2_output_fields_additional(std::shared_ptr<sinsp> inspector, std::map<std::string, std::string> output_fields, double stats_snapshot_time_delta_sec, std::string src);
std::map<std::string, std::string> get_metrics_output_fields_additional(std::shared_ptr<sinsp> inspector, std::map<std::string, std::string> output_fields, double stats_snapshot_time_delta_sec, std::string src);
private:
std::shared_ptr<stats_writer> m_writer;