diff --git a/lib/debugger/cmd-help.c b/lib/debugger/cmd-help.c index 78908515f..8b88e779f 100644 --- a/lib/debugger/cmd-help.c +++ b/lib/debugger/cmd-help.c @@ -36,6 +36,7 @@ _cmd_help(Debugger *self, gint argc, gchar *argv[]) " info Display information about the current execution state\n" " list or l Display source code at the current location\n" " print or p Print the current log message using a template\n" + " printx or px Print the current log message using a filterx expression\n" " drop or d Drop the current message\n" " quit or q Tell syslog-ng to exit\n" ); diff --git a/lib/debugger/cmd-printx.c b/lib/debugger/cmd-printx.c new file mode 100644 index 000000000..8a7df55c4 --- /dev/null +++ b/lib/debugger/cmd-printx.c @@ -0,0 +1,103 @@ +/* + * Copyright (c) 2024 Balázs Scheidler + * Copyright (c) 2024 Axoflow + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * As an additional exemption you are allowed to compile & link against the + * OpenSSL libraries as published by the OpenSSL project. See the file + * COPYING for details. + * + */ + +#include "filterx/filterx-parser.h" + +static void +_eval_filterx_expr(Debugger *self, GList *list_of_exprs, LogMessage *msg) +{ + NVTable *payload = nv_table_ref(msg->payload); + + gint expr_index = 1; + for (GList *l = list_of_exprs; l; l = l->next) + { + FilterXExpr *expr = (FilterXExpr *) l->data; + FilterXObject *res = filterx_expr_eval(expr); + + if (!res) + { + printf("$%d = error(%s)\n", expr_index, filterx_eval_get_last_error()); + filterx_eval_clear_errors(); + goto fail; + } + + GString *buf = scratch_buffers_alloc(); + if (!filterx_object_repr(res, buf)) + { + LogMessageValueType t; + if (!filterx_object_marshal(res, buf, &t)) + g_assert_not_reached(); + } + printf("$%d = %s\n", expr_index, buf->str); + + filterx_object_unref(res); + expr_index++; + } + +fail: + nv_table_unref(payload); +} + +static void +_display_filterx_expr(Debugger *self, gint argc, gchar *argv[]) +{ + GList *list_of_exprs = NULL; + for (gint i = 0; i < argc; i++) + { + gchar *expr_text = g_strdup_printf("%s;", argv[i]); + CfgLexer *lexer = cfg_lexer_new_buffer(self->cfg, expr_text, strlen(expr_text)); + + GList *partial = NULL; + if (!cfg_run_parser(self->cfg, lexer, &filterx_parser, (gpointer *) &partial, NULL) || + !partial) + { + printf("Error parsing filterx expression: %s\n", expr_text); + g_free(expr_text); + goto exit; + } + g_free(expr_text); + list_of_exprs = g_list_concat(list_of_exprs, partial); + } + _eval_filterx_expr(self, + list_of_exprs, + self->breakpoint_site->msg); + +exit: + g_list_free_full(list_of_exprs, (GDestroyNotify) filterx_expr_unref); +} + +static gboolean +_cmd_printx(Debugger *self, gint argc, gchar *argv[]) +{ + if (argc == 1) + { + gchar *vars[] = { "vars();", NULL }; + _display_filterx_expr(self, 1, vars); + } + else + { + _display_filterx_expr(self, argc - 1, &argv[1]); + } + return TRUE; +} diff --git a/lib/debugger/debugger.c b/lib/debugger/debugger.c index 91e842381..bedb4d6fd 100644 --- a/lib/debugger/debugger.c +++ b/lib/debugger/debugger.c @@ -145,6 +145,7 @@ _set_mode(Debugger *self, DebuggerMode new_mode, gboolean trace_message) #include "cmd-help.c" #include "cmd-print.c" +#include "cmd-printx.c" #include "cmd-display.c" #include "cmd-drop.c" #include "cmd-info.c" @@ -175,6 +176,8 @@ struct { "f", _cmd_follow, .requires_breakpoint_site = TRUE }, { "print", _cmd_print, .requires_breakpoint_site = TRUE }, { "p", _cmd_print, .requires_breakpoint_site = TRUE }, + { "printx", _cmd_printx, .requires_breakpoint_site = TRUE }, + { "px", _cmd_printx, .requires_breakpoint_site = TRUE }, { "list", _cmd_list, }, { "l", _cmd_list, }, { "display", _cmd_display }, @@ -230,6 +233,43 @@ _fetch_command(Debugger *self) g_free(command); } +static void +_setup_filterx_context(Debugger *self, FilterXEvalContext *context) +{ + const LogPathOptions *path_options = self->breakpoint_site->path_options; + if (!path_options->filterx_context) + { + /* no parent context, let's use our own, changes to variables will be + * lost by the time we reach the very first filterx block, but we do + * allow setting variables in the context of the debugger */ + + filterx_eval_init_context(context, path_options->filterx_context); + context->msgs = &self->breakpoint_site->msg; + context->num_msg = 1; + } + else + { + filterx_eval_set_context(path_options->filterx_context); + } +} + +static void +_clear_filterx_context(Debugger *self, FilterXEvalContext *context) +{ + const LogPathOptions *path_options = self->breakpoint_site->path_options; + if (!path_options->filterx_context) + { + if (filterx_scope_is_dirty(context->scope)) + { + printf("Dropping variables set from the debugger, as debugger was invoked before the filterx block\n"); + } + filterx_eval_deinit_context(context); + } + else + filterx_eval_set_context(NULL); +} + + static gboolean _handle_command(Debugger *self) { @@ -289,6 +329,11 @@ _handle_interactive_prompt(Debugger *self) _set_current_location(self, NULL); printf(" Stopping on Interrupt...\n"); } + + FilterXEvalContext temporary_context; + if (self->breakpoint_site) + _setup_filterx_context(self, &temporary_context); + while (1) { _fetch_command(self); @@ -297,6 +342,8 @@ _handle_interactive_prompt(Debugger *self) break; } + if (self->breakpoint_site) + _clear_filterx_context(self, &temporary_context); printf("(continuing)\n"); }