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

Debugger interrupt handling #339

Merged
merged 5 commits into from
Nov 13, 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
98 changes: 76 additions & 22 deletions lib/debugger/debugger.c
Original file line number Diff line number Diff line change
Expand Up @@ -31,12 +31,14 @@
#include "compat/time.h"
#include "scratch-buffers.h"

#include <iv_signal.h>
#include <stdio.h>
#include <unistd.h>

struct _Debugger
{
Tracer *tracer;
struct iv_signal sigint;
MainLoop *main_loop;
GlobalConfig *cfg;
gchar *command_buffer;
Expand Down Expand Up @@ -138,15 +140,31 @@ _display_source_line(LogExprNode *expr_node)
static gboolean
_cmd_help(Debugger *self, gint argc, gchar *argv[])
{
printf("syslog-ng interactive console, the following commands are available\n\n"
" help, h, or ? Display this help\n"
" info Display information about the current execution state\n"
" continue or c Continue until the next breakpoint\n"
" trace Display timing information as the message traverses the config\n"
" print, p Print the current log message\n"
" drop, d Drop the current message\n"
" quit, q Tell syslog-ng to exit\n"
);
if (self->breakpoint_site)
{
printf("syslog-ng interactive console\n"
"Stopped on a breakpoint.\n"
"The following commands are available:\n\n"
" help, h, ? Display this help\n"
" info, i Display information about the current execution state\n"
" continue, c Continue until the next breakpoint\n"
" display Set the displayed message template\n"
" trace, t Display timing information as the message traverses the config\n"
" print, p Print the current log message\n"
" drop, d Drop the current message\n"
" quit, q Tell syslog-ng to exit\n"
);
}
else
{
printf("syslog-ng interactive console\n"
"Stopped on an interrupt.\n"
"The following commands are available:\n\n"
" help, h, ? Display this help\n"
" continue, c Continue until the next breakpoint\n"
" quit, q Tell syslog-ng to exit\n"
);
}
return TRUE;
}

Expand Down Expand Up @@ -210,7 +228,8 @@ static gboolean
_cmd_quit(Debugger *self, gint argc, gchar *argv[])
{
main_loop_exit(self->main_loop);
self->breakpoint_site->drop = TRUE;
if (self->breakpoint_site)
self->breakpoint_site->drop = TRUE;
return FALSE;
}

Expand Down Expand Up @@ -245,22 +264,25 @@ struct
{
const gchar *name;
DebuggerCommandFunc command;
gboolean requires_breakpoint_site;
} command_table[] =
{
{ "help", _cmd_help },
{ "h", _cmd_help },
{ "?", _cmd_help },
{ "continue", _cmd_continue },
{ "c", _cmd_continue },
{ "print", _cmd_print },
{ "p", _cmd_print },
{ "print", _cmd_print, .requires_breakpoint_site = TRUE },
{ "p", _cmd_print, .requires_breakpoint_site = TRUE },
{ "display", _cmd_display },
{ "drop", _cmd_drop },
{ "drop", _cmd_drop, .requires_breakpoint_site = TRUE },
{ "d", _cmd_drop, .requires_breakpoint_site = TRUE },
{ "quit", _cmd_quit },
{ "q", _cmd_quit },
{ "trace", _cmd_trace },
{ "info", _cmd_info },
{ "i", _cmd_info },
{ "trace", _cmd_trace, .requires_breakpoint_site = TRUE },
{ "t", _cmd_trace, .requires_breakpoint_site = TRUE },
{ "info", _cmd_info, .requires_breakpoint_site = TRUE },
{ "i", _cmd_info, .requires_breakpoint_site = TRUE },
{ NULL, NULL }
};

Expand Down Expand Up @@ -319,6 +341,7 @@ _handle_command(Debugger *self)
gint argc;
gchar **argv;
GError *error = NULL;
gboolean requires_breakpoint_site = TRUE;
DebuggerCommandFunc command = NULL;

if (!g_shell_parse_argv(self->command_buffer ? : "", &argc, &argv, &error))
Expand All @@ -333,6 +356,7 @@ _handle_command(Debugger *self)
if (strcmp(command_table[i].name, argv[0]) == 0)
{
command = command_table[i].command;
requires_breakpoint_site = command_table[i].requires_breakpoint_site;
break;
}
}
Expand All @@ -341,6 +365,11 @@ _handle_command(Debugger *self)
printf("Undefined command %s, try \"help\"\n", argv[0]);
return TRUE;
}
else if (requires_breakpoint_site && self->breakpoint_site == NULL)
{
printf("Running in interrupt context, command %s requires pipeline context\n", argv[0]);
return TRUE;
}
gboolean result = command(self, argc, argv);
g_strfreev(argv);
return result;
Expand All @@ -350,11 +379,20 @@ static void
_handle_interactive_prompt(Debugger *self)
{
gchar buf[1024];
LogPipe *current_pipe = self->breakpoint_site->pipe;
LogPipe *current_pipe;

if (self->breakpoint_site)
{
current_pipe = self->breakpoint_site->pipe;

printf("Breakpoint hit %s\n", log_expr_node_format_location(current_pipe->expr_node, buf, sizeof(buf)));
_display_source_line(current_pipe->expr_node);
_display_msg_with_template(self, self->breakpoint_site->msg, self->display_template);
printf("Breakpoint hit %s\n", log_expr_node_format_location(current_pipe->expr_node, buf, sizeof(buf)));
_display_source_line(current_pipe->expr_node);
_display_msg_with_template(self, self->breakpoint_site->msg, self->display_template);
}
else
{
printf("Stopping on interrupt, message related commands are unavailable...\n");
}
while (1)
{
_fetch_command(self);
Expand All @@ -374,22 +412,37 @@ _debugger_thread_func(Debugger *self)
while (1)
{
self->breakpoint_site = NULL;
if (!tracer_wait_for_breakpoint(self->tracer, &self->breakpoint_site))
if (!tracer_wait_for_event(self->tracer, &self->breakpoint_site))
break;

_handle_interactive_prompt(self);
tracer_resume_after_breakpoint(self->tracer, self->breakpoint_site);
tracer_resume_after_event(self->tracer, self->breakpoint_site);
}
scratch_buffers_explicit_gc();
app_thread_stop();
return NULL;
}

static void
_interrupt(gpointer user_data)
{
Debugger *self = (Debugger *) user_data;

tracer_stop_on_interrupt(self->tracer);
}

void
debugger_start_console(Debugger *self)
{
main_loop_assert_main_thread();

IV_SIGNAL_INIT(&self->sigint);
self->sigint.signum = SIGINT;
self->sigint.flags = IV_SIGNAL_FLAG_EXCLUSIVE;
self->sigint.cookie = self;
self->sigint.handler = _interrupt;
iv_signal_register(&self->sigint);

self->debugger_thread = g_thread_new(NULL, (GThreadFunc) _debugger_thread_func, self);
}

Expand Down Expand Up @@ -429,6 +482,7 @@ debugger_exit(Debugger *self)
{
main_loop_assert_main_thread();

iv_signal_unregister(&self->sigint);
tracer_cancel(self->tracer);
g_thread_join(self->debugger_thread);
}
Expand Down
26 changes: 21 additions & 5 deletions lib/debugger/tracer.c
Original file line number Diff line number Diff line change
Expand Up @@ -62,10 +62,20 @@ tracer_stop_on_breakpoint(Tracer *self, BreakpointSite *breakpoint_site)
g_mutex_unlock(&self->breakpoint_mutex);
}

void
tracer_stop_on_interrupt(Tracer *self)
{
g_mutex_lock(&self->breakpoint_mutex);
/* send interrupt signal as a NULL */
g_queue_push_tail(self->waiting_breakpoints, NULL);
g_cond_signal(&self->breakpoint_cond);
g_mutex_unlock(&self->breakpoint_mutex);
}

/* NOTE: called by the interactive debugger to wait for a breakpoint to
* trigger, a return of FALSE indicates that the tracing was cancelled */
gboolean
tracer_wait_for_breakpoint(Tracer *self, BreakpointSite **breakpoint_site)
tracer_wait_for_event(Tracer *self, BreakpointSite **breakpoint_site)
{
gboolean cancelled = FALSE;
g_mutex_lock(&self->breakpoint_mutex);
Expand All @@ -92,13 +102,19 @@ tracer_wait_for_breakpoint(Tracer *self, BreakpointSite **breakpoint_site)

/* NOTE: called by the interactive debugger to resume the worker after a breakpoint */
void
tracer_resume_after_breakpoint(Tracer *self, BreakpointSite *breakpoint_site)
tracer_resume_after_event(Tracer *self, BreakpointSite *breakpoint_site)
{
g_mutex_lock(&self->breakpoint_mutex);
g_assert(self->pending_breakpoint == breakpoint_site);
self->pending_breakpoint->resume_requested = TRUE;
self->pending_breakpoint = NULL;
g_cond_broadcast(&self->resume_cond);
if (self->pending_breakpoint)
{
/* we might be returning from an interrupt in which case
* pending_breakpoint is NULL, nothing to resume */

self->pending_breakpoint->resume_requested = TRUE;
self->pending_breakpoint = NULL;
g_cond_broadcast(&self->resume_cond);
}
g_mutex_unlock(&self->breakpoint_mutex);
}

Expand Down
5 changes: 3 additions & 2 deletions lib/debugger/tracer.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,10 @@ typedef struct _BreakpointSite
} BreakpointSite;


void tracer_stop_on_interrupt(Tracer *self);
void tracer_stop_on_breakpoint(Tracer *self, BreakpointSite *breakpoint_site);
gboolean tracer_wait_for_breakpoint(Tracer *self, BreakpointSite **breakpoint_site);
void tracer_resume_after_breakpoint(Tracer *self, BreakpointSite *breakpoint_site);
gboolean tracer_wait_for_event(Tracer *self, BreakpointSite **breakpoint_site);
void tracer_resume_after_event(Tracer *self, BreakpointSite *breakpoint_site);
void tracer_cancel(Tracer *self);

Tracer *tracer_new(GlobalConfig *cfg);
Expand Down
1 change: 0 additions & 1 deletion lib/mainloop.c
Original file line number Diff line number Diff line change
Expand Up @@ -550,7 +550,6 @@ _register_signal_handler(struct iv_signal *signal_poll, gint signum, void (*hand
{
IV_SIGNAL_INIT(signal_poll);
signal_poll->signum = signum;
signal_poll->flags = IV_SIGNAL_FLAG_EXCLUSIVE;
signal_poll->cookie = user_data;
signal_poll->handler = handler;
iv_signal_register(signal_poll);
Expand Down
Loading