123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419 |
- // SPDX-License-Identifier: GPL-3.0-or-later
- #include "ebpf.h"
- #include "ebpf_functions.h"
- /*****************************************************************
- * EBPF SELECT MODULE
- *****************************************************************/
- /**
- * Select Module
- *
- * @param thread_name name of the thread we are looking for.
- *
- * @return it returns a pointer for the module that has thread_name on success or NULL otherwise.
- */
- ebpf_module_t *ebpf_functions_select_module(const char *thread_name) {
- int i;
- for (i = 0; i < EBPF_MODULE_FUNCTION_IDX; i++) {
- if (strcmp(ebpf_modules[i].thread_name, thread_name) == 0) {
- return &ebpf_modules[i];
- }
- }
- return NULL;
- }
- /*****************************************************************
- * EBPF HELP FUNCTIONS
- *****************************************************************/
- /**
- * Thread Help
- *
- * Shows help with all options accepted by thread function.
- *
- * @param transaction the transaction id that Netdata sent for this function execution
- */
- static void ebpf_function_thread_manipulation_help(const char *transaction) {
- pthread_mutex_lock(&lock);
- pluginsd_function_result_begin_to_stdout(transaction, HTTP_RESP_OK, "text/plain", now_realtime_sec() + 3600);
- fprintf(stdout, "%s",
- "ebpf.plugin / thread\n"
- "\n"
- "Function `thread` allows user to control eBPF threads.\n"
- "\n"
- "The following filters are supported:\n"
- "\n"
- " thread:NAME\n"
- " Shows information for the thread NAME. Names are listed inside `ebpf.d.conf`.\n"
- "\n"
- " enable:NAME:PERIOD\n"
- " Enable a specific thread named `NAME` to run a specific PERIOD in seconds. When PERIOD is not\n"
- " specified plugin will use the default 300 seconds\n"
- "\n"
- " disable:NAME\n"
- " Disable a sp.\n"
- "\n"
- "Filters can be combined. Each filter can be given only one time.\n"
- "Process thread is not controlled by functions until we finish the creation of functions per thread..\n"
- );
- pluginsd_function_result_end_to_stdout();
- fflush(stdout);
- pthread_mutex_unlock(&lock);
- }
- /*****************************************************************
- * EBPF ERROR FUNCTIONS
- *****************************************************************/
- /**
- * Function error
- *
- * Show error when a wrong function is given
- *
- * @param transaction the transaction id that Netdata sent for this function execution
- * @param code the error code to show with the message.
- * @param msg the error message
- */
- static void ebpf_function_error(const char *transaction, int code, const char *msg) {
- char buffer[PLUGINSD_LINE_MAX + 1];
- json_escape_string(buffer, msg, PLUGINSD_LINE_MAX);
- pluginsd_function_result_begin_to_stdout(transaction, code, "application/json", now_realtime_sec());
- fprintf(stdout, "{\"status\":%d,\"error_message\":\"%s\"}", code, buffer);
- pluginsd_function_result_end_to_stdout();
- }
- /*****************************************************************
- * EBPF THREAD FUNCTION
- *****************************************************************/
- /**
- * Function enable
- *
- * Enable a specific thread.
- *
- * @param transaction the transaction id that Netdata sent for this function execution
- * @param function function name and arguments given to thread.
- * @param line_buffer buffer used to parse args
- * @param line_max Number of arguments given
- * @param timeout The function timeout
- * @param em The structure with thread information
- */
- static void ebpf_function_thread_manipulation(const char *transaction,
- char *function __maybe_unused,
- char *line_buffer __maybe_unused,
- int line_max __maybe_unused,
- int timeout __maybe_unused,
- ebpf_module_t *em)
- {
- char *words[PLUGINSD_MAX_WORDS] = { NULL };
- char message[512];
- uint32_t show_specific_thread = 0;
- size_t num_words = quoted_strings_splitter_pluginsd(function, words, PLUGINSD_MAX_WORDS);
- for(int i = 1; i < PLUGINSD_MAX_WORDS ;i++) {
- const char *keyword = get_word(words, num_words, i);
- if (!keyword)
- break;
- ebpf_module_t *lem;
- if(strncmp(keyword, EBPF_THREADS_ENABLE_CATEGORY, sizeof(EBPF_THREADS_ENABLE_CATEGORY) -1) == 0) {
- char thread_name[128];
- int period = -1;
- const char *name = &keyword[sizeof(EBPF_THREADS_ENABLE_CATEGORY) - 1];
- char *separator = strchr(name, ':');
- if (separator) {
- strncpyz(thread_name, name, separator - name);
- period = str2i(++separator);
- } else {
- strncpyz(thread_name, name, strlen(name));
- }
- lem = ebpf_functions_select_module(thread_name);
- if (!lem) {
- snprintfz(message, 511, "%s%s", EBPF_PLUGIN_THREAD_FUNCTION_ERROR_THREAD_NOT_FOUND, name);
- ebpf_function_error(transaction, HTTP_RESP_NOT_FOUND, message);
- return;
- }
- pthread_mutex_lock(&ebpf_exit_cleanup);
- if (lem->enabled > NETDATA_THREAD_EBPF_FUNCTION_RUNNING) {
- struct netdata_static_thread *st = lem->thread;
- // Load configuration again
- ebpf_update_module(lem, default_btf, running_on_kernel, isrh);
- // another request for thread that already ran, cleanup and restart
- if (st->thread)
- freez(st->thread);
- if (period <= 0)
- period = EBPF_DEFAULT_LIFETIME;
- st->thread = mallocz(sizeof(netdata_thread_t));
- lem->enabled = NETDATA_THREAD_EBPF_FUNCTION_RUNNING;
- lem->lifetime = period;
- #ifdef NETDATA_INTERNAL_CHECKS
- netdata_log_info("Starting thread %s with lifetime = %d", thread_name, period);
- #endif
- netdata_thread_create(st->thread, st->name, NETDATA_THREAD_OPTION_DEFAULT,
- st->start_routine, lem);
- } else {
- lem->running_time = 0;
- if (period > 0) // user is modifying period to run
- lem->lifetime = period;
- #ifdef NETDATA_INTERNAL_CHECKS
- netdata_log_info("Thread %s had lifetime updated for %d", thread_name, period);
- #endif
- }
- pthread_mutex_unlock(&ebpf_exit_cleanup);
- } else if(strncmp(keyword, EBPF_THREADS_DISABLE_CATEGORY, sizeof(EBPF_THREADS_DISABLE_CATEGORY) -1) == 0) {
- const char *name = &keyword[sizeof(EBPF_THREADS_DISABLE_CATEGORY) - 1];
- lem = ebpf_functions_select_module(name);
- if (!lem) {
- snprintfz(message, 511, "%s%s", EBPF_PLUGIN_THREAD_FUNCTION_ERROR_THREAD_NOT_FOUND, name);
- ebpf_function_error(transaction, HTTP_RESP_NOT_FOUND, message);
- return;
- }
- pthread_mutex_lock(&ebpf_exit_cleanup);
- if (lem->enabled < NETDATA_THREAD_EBPF_STOPPING && lem->thread->thread) {
- lem->lifetime = 0;
- lem->running_time = lem->update_every;
- netdata_thread_cancel(*lem->thread->thread);
- }
- pthread_mutex_unlock(&ebpf_exit_cleanup);
- } else if(strncmp(keyword, EBPF_THREADS_SELECT_THREAD, sizeof(EBPF_THREADS_SELECT_THREAD) -1) == 0) {
- const char *name = &keyword[sizeof(EBPF_THREADS_SELECT_THREAD) - 1];
- lem = ebpf_functions_select_module(name);
- if (!lem) {
- snprintfz(message, 511, "%s%s", EBPF_PLUGIN_THREAD_FUNCTION_ERROR_THREAD_NOT_FOUND, name);
- ebpf_function_error(transaction, HTTP_RESP_NOT_FOUND, message);
- return;
- }
- show_specific_thread |= 1<<lem->thread_id;
- } else if(strncmp(keyword, "help", 4) == 0) {
- ebpf_function_thread_manipulation_help(transaction);
- return;
- }
- }
- time_t expires = now_realtime_sec() + em->update_every;
- BUFFER *wb = buffer_create(PLUGINSD_LINE_MAX, NULL);
- buffer_json_initialize(wb, "\"", "\"", 0, true, false);
- buffer_json_member_add_uint64(wb, "status", HTTP_RESP_OK);
- buffer_json_member_add_string(wb, "type", "table");
- buffer_json_member_add_time_t(wb, "update_every", em->update_every);
- buffer_json_member_add_string(wb, "help", EBPF_PLUGIN_THREAD_FUNCTION_DESCRIPTION);
- // Collect data
- buffer_json_member_add_array(wb, "data");
- int i;
- for (i = 0; i < EBPF_MODULE_FUNCTION_IDX; i++) {
- if (show_specific_thread && !(show_specific_thread & 1<<i))
- continue;
- ebpf_module_t *wem = &ebpf_modules[i];
- buffer_json_add_array_item_array(wb);
- // IMPORTANT!
- // THE ORDER SHOULD BE THE SAME WITH THE FIELDS!
- // thread name
- buffer_json_add_array_item_string(wb, wem->thread_name);
- // description
- buffer_json_add_array_item_string(wb, wem->thread_description);
- // Either it is not running or received a disabled signal and it is stopping.
- if (wem->enabled > NETDATA_THREAD_EBPF_FUNCTION_RUNNING ||
- (!wem->lifetime && (int)wem->running_time == wem->update_every)) {
- // status
- buffer_json_add_array_item_string(wb, EBPF_THREAD_STATUS_STOPPED);
- // Time remaining
- buffer_json_add_array_item_uint64(wb, 0);
- // action
- buffer_json_add_array_item_string(wb, "NULL");
- } else {
- // status
- buffer_json_add_array_item_string(wb, EBPF_THREAD_STATUS_RUNNING);
- // Time remaining
- buffer_json_add_array_item_uint64(wb, (wem->lifetime) ? (wem->lifetime - wem->running_time) : 0);
- // action
- buffer_json_add_array_item_string(wb, "Enabled/Disabled");
- }
- buffer_json_array_close(wb);
- }
- buffer_json_array_close(wb); // data
- buffer_json_member_add_object(wb, "columns");
- {
- int fields_id = 0;
- // IMPORTANT!
- // THE ORDER SHOULD BE THE SAME WITH THE VALUES!
- buffer_rrdf_table_add_field(wb, fields_id++, "Thread", "Thread Name", RRDF_FIELD_TYPE_STRING,
- RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, 0, NULL, NAN,
- RRDF_FIELD_SORT_ASCENDING, NULL, RRDF_FIELD_SUMMARY_COUNT,
- RRDF_FIELD_FILTER_MULTISELECT,
- RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_STICKY, NULL);
- buffer_rrdf_table_add_field(wb, fields_id++, "Description", "Thread Desc", RRDF_FIELD_TYPE_STRING,
- RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, 0, NULL, NAN,
- RRDF_FIELD_SORT_ASCENDING, NULL, RRDF_FIELD_SUMMARY_COUNT,
- RRDF_FIELD_FILTER_MULTISELECT,
- RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_STICKY, NULL);
- buffer_rrdf_table_add_field(wb, fields_id++, "Status", "Thread Status", RRDF_FIELD_TYPE_STRING,
- RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, 0, NULL, NAN,
- RRDF_FIELD_SORT_ASCENDING, NULL, RRDF_FIELD_SUMMARY_COUNT,
- RRDF_FIELD_FILTER_MULTISELECT,
- RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_STICKY, NULL);
- buffer_rrdf_table_add_field(wb, fields_id++, "Time", "Time Remaining", RRDF_FIELD_TYPE_INTEGER,
- RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NUMBER, 0, NULL,
- NAN, RRDF_FIELD_SORT_ASCENDING, NULL, RRDF_FIELD_SUMMARY_COUNT,
- RRDF_FIELD_FILTER_MULTISELECT,
- RRDF_FIELD_OPTS_NONE, NULL);
- buffer_rrdf_table_add_field(wb, fields_id++, "Action", "Thread Action", RRDF_FIELD_TYPE_STRING,
- RRDF_FIELD_VISUAL_VALUE, RRDF_FIELD_TRANSFORM_NONE, 0, NULL, NAN,
- RRDF_FIELD_SORT_ASCENDING, NULL, RRDF_FIELD_SUMMARY_COUNT,
- RRDF_FIELD_FILTER_MULTISELECT,
- RRDF_FIELD_OPTS_VISIBLE | RRDF_FIELD_OPTS_STICKY, NULL);
- }
- buffer_json_object_close(wb); // columns
- buffer_json_member_add_string(wb, "default_sort_column", "Thread");
- buffer_json_member_add_object(wb, "charts");
- {
- // Threads
- buffer_json_member_add_object(wb, "eBPFThreads");
- {
- buffer_json_member_add_string(wb, "name", "Threads");
- buffer_json_member_add_string(wb, "type", "line");
- buffer_json_member_add_array(wb, "columns");
- {
- buffer_json_add_array_item_string(wb, "Threads");
- }
- buffer_json_array_close(wb);
- }
- buffer_json_object_close(wb);
- // Life Time
- buffer_json_member_add_object(wb, "eBPFLifeTime");
- {
- buffer_json_member_add_string(wb, "name", "LifeTime");
- buffer_json_member_add_string(wb, "type", "line");
- buffer_json_member_add_array(wb, "columns");
- {
- buffer_json_add_array_item_string(wb, "Threads");
- buffer_json_add_array_item_string(wb, "Time");
- }
- buffer_json_array_close(wb);
- }
- buffer_json_object_close(wb);
- }
- buffer_json_object_close(wb); // charts
- // Do we use only on fields that can be groupped?
- buffer_json_member_add_object(wb, "group_by");
- {
- // group by Status
- buffer_json_member_add_object(wb, "Status");
- {
- buffer_json_member_add_string(wb, "name", "Thread status");
- buffer_json_member_add_array(wb, "columns");
- {
- buffer_json_add_array_item_string(wb, "Status");
- }
- buffer_json_array_close(wb);
- }
- buffer_json_object_close(wb);
- }
- buffer_json_object_close(wb); // group_by
- buffer_json_member_add_time_t(wb, "expires", expires);
- buffer_json_finalize(wb);
- // Lock necessary to avoid race condition
- pthread_mutex_lock(&lock);
- pluginsd_function_result_begin_to_stdout(transaction, HTTP_RESP_OK, "application/json", expires);
- fwrite(buffer_tostring(wb), buffer_strlen(wb), 1, stdout);
- pluginsd_function_result_end_to_stdout();
- fflush(stdout);
- pthread_mutex_unlock(&lock);
- buffer_free(wb);
- }
- /*****************************************************************
- * EBPF FUNCTION THREAD
- *****************************************************************/
- /**
- * FUNCTION thread.
- *
- * @param ptr a `ebpf_module_t *`.
- *
- * @return always NULL.
- */
- void *ebpf_function_thread(void *ptr)
- {
- ebpf_module_t *em = (ebpf_module_t *)ptr;
- char buffer[PLUGINSD_LINE_MAX + 1];
- char *s = NULL;
- while(!ebpf_exit_plugin && (s = fgets(buffer, PLUGINSD_LINE_MAX, stdin))) {
- char *words[PLUGINSD_MAX_WORDS] = { NULL };
- size_t num_words = quoted_strings_splitter_pluginsd(buffer, words, PLUGINSD_MAX_WORDS);
- const char *keyword = get_word(words, num_words, 0);
- if(keyword && strcmp(keyword, PLUGINSD_KEYWORD_FUNCTION) == 0) {
- char *transaction = get_word(words, num_words, 1);
- char *timeout_s = get_word(words, num_words, 2);
- char *function = get_word(words, num_words, 3);
- if(!transaction || !*transaction || !timeout_s || !*timeout_s || !function || !*function) {
- netdata_log_error("Received incomplete %s (transaction = '%s', timeout = '%s', function = '%s'). Ignoring it.",
- keyword,
- transaction?transaction:"(unset)",
- timeout_s?timeout_s:"(unset)",
- function?function:"(unset)");
- }
- else {
- int timeout = str2i(timeout_s);
- if (!strncmp(function, EBPF_FUNCTION_THREAD, sizeof(EBPF_FUNCTION_THREAD) - 1))
- ebpf_function_thread_manipulation(transaction,
- function,
- buffer,
- PLUGINSD_LINE_MAX + 1,
- timeout,
- em);
- else
- ebpf_function_error(transaction,
- HTTP_RESP_NOT_FOUND,
- "No function with this name found in ebpf.plugin.");
- }
- }
- else
- netdata_log_error("Received unknown command: %s", keyword ? keyword : "(unset)");
- }
- return NULL;
- }
|