Skip to content

Commit

Permalink
new(userspace): moved dump API under async capability.
Browse files Browse the repository at this point in the history
We now expect `PPME_ASYNCEVENT_E` whose `name` matches one of the
plugin supported ones (get_async_events() API).

The new API is not required for async capability.

Added also a test.

Signed-off-by: Federico Di Pierro <[email protected]>
  • Loading branch information
FedeDP committed Nov 7, 2024
1 parent 5c5e9eb commit 56f40b5
Show file tree
Hide file tree
Showing 9 changed files with 140 additions and 53 deletions.
2 changes: 1 addition & 1 deletion userspace/libsinsp/dumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ void sinsp_dumper::open(sinsp* inspector, const std::string& filename, bool comp

// notify registered plugins of capture open
for(auto& p : inspector->m_plugin_manager->plugins()) {
if(p->caps() & CAP_DUMPING) {
if(p->caps() & CAP_ASYNC) {
if(!p->dump(*this)) {
throw sinsp_exception("dump error for plugin '" + p->name() +
"' : " + p->get_last_error());
Expand Down
79 changes: 49 additions & 30 deletions userspace/libsinsp/plugin.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -856,36 +856,6 @@ bool sinsp_plugin::capture_close() {
return m_handle->api.capture_close(m_state, &in) == SS_PLUGIN_SUCCESS;
}

bool sinsp_plugin::dump(sinsp_dumper& dumper) {
if(!m_inited) {
throw sinsp_exception(std::string(s_not_init_err) + ": " + m_name);
}

if(!m_handle->api.dump) {
return false;
}

uint32_t num_evts;
ss_plugin_event** evts;

if(m_handle->api.dump(m_state, &num_evts, &evts) != SS_PLUGIN_SUCCESS) {
return false;
}
for(int i = 0; i < num_evts; i++) {
auto e = evts[i];
auto evt = sinsp_evt();
ASSERT(evt.get_scap_evt_storage() == nullptr);
evt.set_scap_evt_storage(new char[e->len]);
memcpy(evt.get_scap_evt_storage(), e, e->len);
evt.set_cpuid(0);
evt.set_num(0);
evt.set_scap_evt((scap_evt*)evt.get_scap_evt_storage());
evt.init();
dumper.dump(&evt);
}
return true;
}

/** Event Source CAP **/

scap_source_plugin& sinsp_plugin::as_scap_source() {
Expand Down Expand Up @@ -1205,3 +1175,52 @@ bool sinsp_plugin::set_async_event_handler(async_event_handler_t handler) {

return rc == SS_PLUGIN_SUCCESS;
}

bool sinsp_plugin::dump(sinsp_dumper& dumper) {
if(!m_inited) {
throw sinsp_exception(std::string(s_not_init_err) + ": " + m_name);
}

if(!(m_caps & CAP_ASYNC)) {
throw sinsp_exception("plugin " + m_name + "without async events cap used as dumper");
}

if(!m_handle->api.dump) {
// Not required.
return true;
}

uint32_t num_evts;
ss_plugin_event** evts;

if(m_handle->api.dump(m_state, &num_evts, &evts) != SS_PLUGIN_SUCCESS) {
return false;
}

for(uint32_t i = 0; i < num_evts; i++) {
auto e = evts[i];
// We only support dumping of PPME_ASYNCEVENT_E events
if(e->type != PPME_ASYNCEVENT_E || e->nparams != 3) {
throw sinsp_exception("malformed async event dumped by plugin: " + m_name);
}

// Event name must be one of the async event names
auto name = (const char*)((uint8_t*)e + sizeof(ss_plugin_event) + 4 + 4 + 4 + 4);
if(async_event_names().find(name) == async_event_names().end()) {
throw sinsp_exception("incompatible async event '" + std::string(name) +
"' produced by plugin: " + m_name);
}

// Build the sinsp_evt
auto evt = sinsp_evt();
ASSERT(evt.get_scap_evt_storage() == nullptr);
evt.set_scap_evt_storage(new char[e->len]);
memcpy(evt.get_scap_evt_storage(), e, e->len);
evt.set_cpuid(0);
evt.set_num(0);
evt.set_scap_evt((scap_evt*)evt.get_scap_evt_storage());
evt.init();
dumper.dump(&evt);
}
return true;
}
1 change: 1 addition & 0 deletions userspace/libsinsp/plugin.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ limitations under the License.
#include <atomic>
#include <libscap/engine/source_plugin/source_plugin_public.h>
#include <libsinsp/event.h>
#include <libsinsp/dumper.h>
#include <libsinsp/sinsp_filtercheck.h>
#include <libsinsp/version.h>
#include <libsinsp/events/sinsp_events.h>
Expand Down
2 changes: 1 addition & 1 deletion userspace/libsinsp/sinsp_cycledumper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ void sinsp_cycledumper::autodump_start(const std::string& dump_filename) {
std::for_each(m_open_file_callbacks.begin(), m_open_file_callbacks.end(), std::ref(*this));

m_dumper->open(m_inspector,
dump_filename.c_str(),
dump_filename,
m_compress ? SCAP_COMPRESSION_GZIP : SCAP_COMPRESSION_NONE);

m_inspector->set_dumping(true);
Expand Down
45 changes: 45 additions & 0 deletions userspace/libsinsp/test/plugins.ut.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -378,6 +378,51 @@ TEST_F(sinsp_with_test_input, plugin_custom_source) {
ASSERT_EQ(next_event(), nullptr); // EOF is expected
}

class plugin_test_event_processor : public libsinsp::event_processor {
public:
plugin_test_event_processor() { num_async_evts = 0; }

void on_capture_start() {}

virtual void process_event(sinsp_evt* evt, libsinsp::event_return rc) {
if(evt->get_type() == PPME_ASYNCEVENT_E) {
num_async_evts++;
}
}

int num_async_evts;
};

// scenario: a plugin with dump capability is requested a dump and then the capture file is read.
TEST_F(sinsp_with_test_input, plugin_dump) {
uint64_t max_count = 1;
uint64_t period_ns = 1000000; // 1ms
std::string async_pl_cfg = std::to_string(max_count) + ":" + std::to_string(period_ns);
register_plugin(&m_inspector, get_plugin_api_sample_syscall_async);

// we will not use the test scap engine here, but open the src plugin instead
// note: we configure the plugin to just emit 1 event through its open params
m_inspector.open_nodriver();

auto evt = next_event();
ASSERT_NE(evt, nullptr);
ASSERT_EQ(evt->get_type(), PPME_ASYNCEVENT_E);

auto sinspdumper = sinsp_dumper();
sinspdumper.open(&m_inspector, "test.txt", false);
sinspdumper.close();

// Here we open a replay inspector just to trigger the initstate events parsing
auto replay_inspector = sinsp();
auto processor = plugin_test_event_processor();
replay_inspector.register_external_event_processor(processor);
ASSERT_NO_THROW(replay_inspector.open_savefile("test.txt"));

ASSERT_EQ(processor.num_async_evts, 10);

remove("test.txt");
}

TEST(sinsp_plugin, plugin_extract_compatibility) {
std::string tmp;
sinsp i;
Expand Down
30 changes: 30 additions & 0 deletions userspace/libsinsp/test/plugins/syscall_async.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,35 @@ ss_plugin_rc plugin_set_async_event_handler(ss_plugin_t* s,
return SS_PLUGIN_SUCCESS;
}

ss_plugin_rc plugin_dump(ss_plugin_t* s, uint32_t* nevts, ss_plugin_event*** evts) {
auto ps = reinterpret_cast<plugin_state*>(s);

static uint8_t evt_buf[10][64];
static ss_plugin_event* evt[10];
const char* name = "sampleticker";
const char* data = "hello world";
for(int i = 0; i < 10; i++) {
evt[i] = (ss_plugin_event*)evt_buf[i];
char error[SCAP_LASTERR_SIZE];
int32_t encode_res =
scap_event_encode_params(scap_sized_buffer{evt[i], sizeof(evt_buf[i])},
nullptr,
error,
PPME_ASYNCEVENT_E,
3,
0,
name,
scap_sized_buffer{(void*)data, strlen(data) + 1});

if(encode_res == SCAP_FAILURE) {
return SS_PLUGIN_FAILURE;
}
}
*evts = evt;
*nevts = 10;
return SS_PLUGIN_SUCCESS;
}

} // anonymous namespace

void get_plugin_api_sample_syscall_async(plugin_api& out) {
Expand All @@ -203,4 +232,5 @@ void get_plugin_api_sample_syscall_async(plugin_api& out) {
out.get_async_event_sources = plugin_get_async_event_sources;
out.get_async_events = plugin_get_async_events;
out.set_async_event_handler = plugin_set_async_event_handler;
out.dump = plugin_dump;
}
27 changes: 12 additions & 15 deletions userspace/plugin/plugin_api.h
Original file line number Diff line number Diff line change
Expand Up @@ -1053,6 +1053,18 @@ typedef struct {
ss_plugin_rc (*set_async_event_handler)(ss_plugin_t* s,
ss_plugin_owner_t* owner,
const ss_plugin_async_event_handler_t handler);

//
// Called by the framework when a capture file dump is requested.
//
// Required: no
// Arguments:
// - s: the plugin state, returned by init(). Can be NULL.
// - nevts: number of events retrieved.
// - evts: events retrieved. Events MUST be of type PPME_ASYNCEVENT_E.
//
// Return value: A ss_plugin_rc with values SS_PLUGIN_SUCCESS or SS_PLUGIN_FAILURE.
ss_plugin_rc (*dump)(ss_plugin_t* s, uint32_t* nevts, ss_plugin_event*** evts);
};

// Sets a new plugin configuration when provided by the framework.
Expand Down Expand Up @@ -1104,21 +1116,6 @@ typedef struct {
// Return value: A ss_plugin_rc with values SS_PLUGIN_SUCCESS or SS_PLUGIN_FAILURE.
ss_plugin_rc (*capture_close)(ss_plugin_t* s, const ss_plugin_capture_listen_input* i);
};

// Events dumping capability API
struct {
//
// Called by the framework when a capture file dump is requested.
//
// Required: yes
// Arguments:
// - s: the plugin state, returned by init(). Can be NULL.
// - evts: input containing vtables for performing table operations and
// subscribe/unsubscribe async routines
//
// Return value: A ss_plugin_rc with values SS_PLUGIN_SUCCESS or SS_PLUGIN_FAILURE.
ss_plugin_rc (*dump)(ss_plugin_t* s, uint32_t* nevts, ss_plugin_event*** evts);
};
} plugin_api;

#ifdef __cplusplus
Expand Down
6 changes: 1 addition & 5 deletions userspace/plugin/plugin_loader.c
Original file line number Diff line number Diff line change
Expand Up @@ -122,11 +122,11 @@ plugin_handle_t* plugin_load(const char* path, char* err) {
SYM_RESOLVE(ret, get_async_event_sources);
SYM_RESOLVE(ret, get_async_events);
SYM_RESOLVE(ret, set_async_event_handler);
SYM_RESOLVE(ret, dump);
SYM_RESOLVE(ret, set_config);
SYM_RESOLVE(ret, get_metrics);
SYM_RESOLVE(ret, capture_open);
SYM_RESOLVE(ret, capture_close);
SYM_RESOLVE(ret, dump);
return ret;
}

Expand Down Expand Up @@ -282,10 +282,6 @@ plugin_caps_t plugin_get_capabilities(const plugin_handle_t* h, char* err) {
", ");
}

if(h->api.dump != NULL) {
caps = (plugin_caps_t)((uint32_t)caps | (uint32_t)CAP_DUMPING);
}

return caps;
}

Expand Down
1 change: 0 additions & 1 deletion userspace/plugin/plugin_loader.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ typedef enum {
CAP_PARSING = 1 << 2,
CAP_ASYNC = 1 << 3,
CAP_CAPTURE_LISTENING = 1 << 4,
CAP_DUMPING = 1 << 5,
CAP_BROKEN = 1 << 31, // used to report inconsistencies
} plugin_caps_t;

Expand Down

0 comments on commit 56f40b5

Please sign in to comment.