Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

new(plugin_api): add plugin metrics support #1828

Merged
merged 3 commits into from
May 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions userspace/libscap/metrics_v2.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ extern "C" {
#define METRICS_V2_STATE_COUNTERS (1 << 3)
#define METRICS_V2_RULE_COUNTERS (1 << 4)
#define METRICS_V2_MISC (1 << 5)
#define METRICS_V2_PLUGINS (1 << 6)

typedef union metrics_v2_value {
uint32_t u32;
Expand Down
14 changes: 14 additions & 0 deletions userspace/libsinsp/metrics_collector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ limitations under the License.

#include <libsinsp/sinsp_int.h>
#include <libsinsp/metrics_collector.h>
#include <libsinsp/plugin_manager.h>
#include <cmath>
#include <sys/times.h>
#include <sys/stat.h>
Expand Down Expand Up @@ -456,6 +457,19 @@ void libs_metrics_collector::snapshot()
return;
}

/*
* plugins metrics
*/

if(m_metrics_flags & METRICS_V2_PLUGINS)
{
for (auto& p : m_inspector->get_plugin_manager()->plugins())
{
std::vector<metrics_v2> plugin_metrics = p->get_metrics();
m_metrics.insert(m_metrics.end(), plugin_metrics.begin(), plugin_metrics.end());
}
}

/*
* libscap metrics
*/
Expand Down
2 changes: 1 addition & 1 deletion userspace/libsinsp/metrics_collector.h
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ class libs_metrics_collector

private:
sinsp* m_inspector;
uint32_t m_metrics_flags = METRICS_V2_KERNEL_COUNTERS | METRICS_V2_LIBBPF_STATS | METRICS_V2_RESOURCE_UTILIZATION | METRICS_V2_STATE_COUNTERS;
uint32_t m_metrics_flags = METRICS_V2_KERNEL_COUNTERS | METRICS_V2_LIBBPF_STATS | METRICS_V2_RESOURCE_UTILIZATION | METRICS_V2_STATE_COUNTERS | METRICS_V2_PLUGINS;
std::vector<metrics_v2> m_metrics;

void get_rss_vsz_pss_total_memory_and_open_fds(uint32_t &rss, uint32_t &vsz, uint32_t &pss, uint64_t &host_memory_used, uint64_t &host_open_fds);
Expand Down
71 changes: 71 additions & 0 deletions userspace/libsinsp/plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -886,6 +886,77 @@ std::vector<sinsp_plugin::open_param> sinsp_plugin::list_open_params() const
return list;
}

static void set_plugin_metric_value(metrics_v2& metric, metrics_v2_value_type type, ss_plugin_metric_value val)
{
switch (type)
{
case METRIC_VALUE_TYPE_U32:
metric.value.u32 = val.u32;
break;
case METRIC_VALUE_TYPE_S32:
metric.value.s32 = val.s32;
break;
case METRIC_VALUE_TYPE_U64:
metric.value.u64 = val.u64;
break;
case METRIC_VALUE_TYPE_S64:
metric.value.s64 = val.s64;
break;
case METRIC_VALUE_TYPE_D:
metric.value.d = val.d;
break;
case METRIC_VALUE_TYPE_F:
metric.value.f = val.f;
break;
case METRIC_VALUE_TYPE_I:
metric.value.i = val.i;
break;
default:
break;
}
}

std::vector<metrics_v2> sinsp_plugin::get_metrics() const
{
if(!m_inited)
{
throw sinsp_exception(std::string(s_not_init_err) + ": " + m_name);
}

std::vector<metrics_v2> metrics;
uint32_t num_metrics = 0;

if(!m_handle->api.get_metrics)
{
return metrics;
}

ss_plugin_metric *plugin_metrics = m_handle->api.get_metrics(m_state, &num_metrics);
for (uint32_t i = 0; i < num_metrics; i++)
{
ss_plugin_metric *plugin_metric = plugin_metrics + i;

metrics_v2 metric;

//copy plugin name
int s = strlcpy(metric.name, m_name.c_str(), METRIC_NAME_MAX);
//copy dot
strlcpy(metric.name + s, ".", METRIC_NAME_MAX);
//copy metric name
strlcpy(metric.name + s + 1, plugin_metric->name, METRIC_NAME_MAX);

metric.flags = METRICS_V2_PLUGINS;
mrgian marked this conversation as resolved.
Show resolved Hide resolved
metric.unit = METRIC_VALUE_UNIT_COUNT;
metric.type = static_cast<metrics_v2_value_type>(plugin_metric->value_type);
metric.metric_type = static_cast<metrics_v2_metric_type>(plugin_metric->type);
set_plugin_metric_value(metric, metric.type, plugin_metric->value);

metrics.emplace_back(metric);
}

return metrics;
}

/** End of Event Source CAP **/

/** Field Extraction CAP **/
Expand Down
1 change: 1 addition & 0 deletions userspace/libsinsp/plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ class sinsp_plugin
std::string get_last_error() const;
std::string get_init_schema(ss_plugin_schema_type& schema_type) const;
bool set_config(const std::string& config);
std::vector<metrics_v2> get_metrics() const;

/** Event Sourcing **/
inline uint32_t id() const
Expand Down
37 changes: 37 additions & 0 deletions userspace/libsinsp/test/plugins.ut.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ limitations under the License.

#include <gtest/gtest.h>
#include <libsinsp/plugin.h>
#include <libsinsp/test/helpers/threads_helpers.h>

#include <sinsp_with_test_input.h>
#include "test_utils.h"
Expand Down Expand Up @@ -890,3 +891,39 @@ TEST(sinsp_plugin, plugin_set_config)

libsinsp_logger()->remove_callback_log();
}

#ifdef __linux__
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this linux only?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

libs_metrics_collector is currently compiled on linux only (see https://github.com/falcosecurity/libs/blob/master/userspace/libsinsp/metrics_collector.h#L82)

I think the reason behind is that we had linux-only metrics.
However, with this PR that's not the case anymore, so we should definitely enable metrics in other platforms too.
I'm working on that, but maybe it's better to open another PR when I'm done :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of simplicity as macOS and Windows were / are not a primary metrics use case. In more detail it's because of how we compute CPU usage etc, which is Linux specific.

I believe there is no harm to refine what actually only needs to be linux. @mrgian does this help?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @incertum Thank you!
I believe we just need to rearrange #ifdef __linux__ directives.
The only hiccup I had is in https://github.com/falcosecurity/libs/blob/master/userspace/libsinsp/metrics_collector.cpp#L35-L56
That uses designated initialization (explicit array indexes) which is not supported by MSVC.
However I don't think removing these explicit indexes is a big problem, the comment on top should be enough for contributors to understand that metrics_unit_name_mappings_prometheus and metrics_metric_type_name_mappings_prometheus need to stay in sync with the enums in metrics_v2.h.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Out of scope, but have we ever thought about using a client library for prometheus, like: https://github.com/jupp0r/prometheus-cpp ? @incertum

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@FedeDP see the previous discussions; that was the whole point of not adding another lib as we have a more unique use case with all of our different metrics spanning multiple areas of the code base, plus that lib seems not very suitable and supported enough IMO.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks Melissa! I probably lost that part :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mrgian mind elaborating However I don't think removing these explicit indexes is a big problem,?

Copy link
Contributor Author

@mrgian mrgian May 16, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

mrgian@cddf7b7
👆 @incertum This is what I've done to make libs_metrics_collector compile on Windows.
Notice how I had to remove designation initializers from metrics_unit_name_mappings_prometheus, it allows to compile with MSVC and the code behavior stays the same.
However we now have to make sure metrics_unit_name_mappings_prometheus has the same ordering as the values in metrics_v2_value_unit in metrics_v2.h. This comment should be enough to make contributors aware of this.
The same applies for metrics_metric_type_name_mappings_prometheus.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe ok for now since we don't have much time left for the next release.


TEST_F(sinsp_with_test_input, plugin_metrics)
{
uint32_t test_metrics_flags = (METRICS_V2_PLUGINS);
libs::metrics::libs_metrics_collector libs_metrics_collector(&m_inspector, test_metrics_flags);

libs_metrics_collector.snapshot();
auto metrics_snapshot = libs_metrics_collector.get_metrics();
ASSERT_EQ(metrics_snapshot.size(), 0);

register_plugin(&m_inspector, get_plugin_api_sample_metrics);
open_inspector();

libs_metrics_collector.snapshot();
metrics_snapshot = libs_metrics_collector.get_metrics();
ASSERT_EQ(metrics_snapshot.size(), 2);

int events = 256;
for (int i = 0; i < events; i++)
{
add_event_advance_ts(increasing_ts(), 0, PPME_SYSCALL_OPEN_E, 3, "/tmp/the_file", PPM_O_RDWR, 0);
}

libs_metrics_collector.snapshot();
metrics_snapshot = libs_metrics_collector.get_metrics();
ASSERT_EQ(metrics_snapshot.size(), 2);

ASSERT_EQ(std::string(metrics_snapshot.at(0).name), "sample_metrics.dummy_metric");
ASSERT_EQ(std::string(metrics_snapshot.at(1).name), "sample_metrics.evt_count");

ASSERT_EQ(metrics_snapshot.back().value.u64, events);
}

#endif
135 changes: 135 additions & 0 deletions userspace/libsinsp/test/plugins/metrics.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// SPDX-License-Identifier: Apache-2.0
/*
Copyright (C) 2024 The Falco Authors.

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 <cstdio>
#include <cstdlib>
#include <sstream>
#include <iostream>

#include <driver/ppm_events_public.h>
#include "test_plugins.h"

struct plugin_state
{
std::string lasterr;
struct ss_plugin_metric metrics[2];
uint64_t count = 0;
};

static const char* plugin_get_required_api_version()
{
return PLUGIN_API_VERSION_STR;
}

static const char* plugin_get_version()
{
return "0.1.0";
}

static const char* plugin_get_name()
{
return "sample_metrics";
}

static const char* plugin_get_description()
{
return "some desc";
}

static const char* plugin_get_contact()
{
return "some contact";
}

static const char* plugin_get_parse_event_sources()
{
return "[\"syscall\"]";
}

static uint16_t* plugin_get_parse_event_types(uint32_t* num_types, ss_plugin_t* s)
{
static uint16_t types[] = {
PPME_SYSCALL_OPEN_E,
//PPME_SYSCALL_OPEN_X,
};
*num_types = sizeof(types) / sizeof(uint16_t);
return &types[0];
}

static ss_plugin_t* plugin_init(const ss_plugin_init_input* in, ss_plugin_rc* rc)
{
*rc = SS_PLUGIN_SUCCESS;
plugin_state *ret = new plugin_state();

ret->metrics[0].type = SS_PLUGIN_METRIC_TYPE_NON_MONOTONIC;
ret->metrics[0].value_type = SS_PLUGIN_METRIC_VALUE_TYPE_U64;
ret->metrics[0].value.u64 = 1234;
ret->metrics[0].name = "dummy_metric";

ret->metrics[1].type = SS_PLUGIN_METRIC_TYPE_MONOTONIC;
ret->metrics[1].value_type = SS_PLUGIN_METRIC_VALUE_TYPE_U64;
ret->metrics[1].value.u64 = 0;
ret->metrics[1].name = "evt_count";

return ret;
}

static void plugin_destroy(ss_plugin_t* s)
{
delete ((plugin_state *) s);
}

static const char* plugin_get_last_error(ss_plugin_t* s)
{
return ((plugin_state *) s)->lasterr.c_str();
}

static ss_plugin_rc plugin_parse_event(ss_plugin_t *s, const ss_plugin_event_input *ev, const ss_plugin_event_parse_input* in)
{
plugin_state *ps = (plugin_state *) s;
ps->count++;
ps->metrics[1].value.u64 = ps->count;

return SS_PLUGIN_SUCCESS;
}

static ss_plugin_metric* plugin_get_metrics(ss_plugin_t *s, uint32_t *num_metrics)
{
plugin_state *ps = (plugin_state *) s;

*num_metrics = sizeof(ps->metrics) / sizeof(ss_plugin_metric);

return ps->metrics;
}

void get_plugin_api_sample_metrics(plugin_api& out)
{
memset(&out, 0, sizeof(plugin_api));
out.get_required_api_version = plugin_get_required_api_version;
out.get_version = plugin_get_version;
out.get_description = plugin_get_description;
out.get_contact = plugin_get_contact;
out.get_name = plugin_get_name;
out.get_last_error = plugin_get_last_error;
out.init = plugin_init;
out.destroy = plugin_destroy;
out.get_parse_event_sources = plugin_get_parse_event_sources;
out.get_parse_event_types = plugin_get_parse_event_types;
out.parse_event = plugin_parse_event;
out.get_metrics = plugin_get_metrics;
}
1 change: 1 addition & 0 deletions userspace/libsinsp/test/plugins/test_plugins.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,4 @@ void get_plugin_api_sample_plugin_extract(plugin_api& out);
void get_plugin_api_sample_syscall_tables(plugin_api& out);
void get_plugin_api_sample_syscall_subtables(plugin_api& out);
void get_plugin_api_sample_syscall_subtables_array(plugin_api& out);
void get_plugin_api_sample_metrics(plugin_api& out);
11 changes: 11 additions & 0 deletions userspace/plugin/plugin_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -966,6 +966,17 @@ typedef struct
// or SS_PLUGIN_FAILURE if the config is rejected.
// If rejected the plugin should provide context in the string returned by get_last_error().
ss_plugin_rc (*set_config)(ss_plugin_t* s, const ss_plugin_set_config_input* i);

//
// Return an updated set of metrics provided by this plugin.
// Required: no
// - s: the plugin state, returned by init(). Can be NULL.
// - num_metrics: lenght of the returned metrics array.
//
// Return value: Pointer to the first element of the metrics array.
// 'num_metrics' must be set to the lenght of the array before returning
// and it can be set to 0 if no metrics are provided.
ss_plugin_metric* (*get_metrics)(ss_plugin_t* s, uint32_t* num_metrics);
} plugin_api;

#ifdef __cplusplus
Expand Down
49 changes: 49 additions & 0 deletions userspace/plugin/plugin_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,55 @@ typedef enum ss_plugin_log_severity
SS_PLUGIN_LOG_SEV_TRACE = 8,
} ss_plugin_log_severity;

// Types supported by the by the metric values
typedef enum ss_plugin_metric_value_type
mrgian marked this conversation as resolved.
Show resolved Hide resolved
{
SS_PLUGIN_METRIC_VALUE_TYPE_U32 = 1,
SS_PLUGIN_METRIC_VALUE_TYPE_S32 = 2,
SS_PLUGIN_METRIC_VALUE_TYPE_U64 = 3,
SS_PLUGIN_METRIC_VALUE_TYPE_S64 = 4,
SS_PLUGIN_METRIC_VALUE_TYPE_D = 5,
SS_PLUGIN_METRIC_VALUE_TYPE_F = 6,
SS_PLUGIN_METRIC_VALUE_TYPE_I = 7,
} ss_plugin_metric_value_type;

// Data representation of metric values
typedef union ss_plugin_metric_value
{
uint32_t u32;
int32_t s32;
uint64_t u64;
int64_t s64;
double d;
float f;
int i;
} ss_plugin_metric_value;

// Metric types
typedef enum ss_plugin_metric_type
{
SS_PLUGIN_METRIC_TYPE_MONOTONIC = 1,
SS_PLUGIN_METRIC_TYPE_NON_MONOTONIC = 2,
} ss_plugin_metric_type;

//
// Struct representing a metric to be provided to the plugin framework
typedef struct ss_plugin_metric
{
//
// Opaque string representing the metric name
const char* name;
//
// Metric type
ss_plugin_metric_type type;
//
// Metric numeric value
ss_plugin_metric_value value;
//
// Metric value data type
ss_plugin_metric_value_type value_type;
} ss_plugin_metric;

#ifdef __cplusplus
}
#endif
Loading