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

Feature remote debugger #1162

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
4 changes: 4 additions & 0 deletions debugger/ChangeLog
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
29-01-2022 Pascal Martin <[email protected]>

* add remote and cross development debugging mode.

14-06-2012 Alexander Petukhov <[email protected]>

* fixed reverse children order in watch/autos
Expand Down
1 change: 1 addition & 0 deletions debugger/README
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ Features
* Saving debug session data in a Geany project (can be switched through settings)
* Double or single panel modes
* Hotkeys
* Remote and cross development debug sessions.

Usage
-----
Expand Down
120 changes: 96 additions & 24 deletions debugger/src/dbm_gdb.c
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,11 @@ enum sr {
static dbg_callbacks* dbg_cbs;

/* GDB command line arguments*/
static const gchar *gdb_args[] = { "gdb", "-i=mi", NULL };
static const gchar *gdb_args_local[] = { "gdb", "-i=mi", NULL };
static const gchar *gdb_args_multi[] = { "gdb-multiarch", "-i=mi", NULL };

static gboolean remote_session = FALSE;
static gboolean detected_prompt = FALSE; /* Async commands */

/* GDB pid*/
static GPid gdb_pid = 0;
Expand Down Expand Up @@ -197,6 +201,18 @@ static void on_gdb_exit(GPid pid, gint status, gpointer data)
dbg_cbs->set_exited(0);
}

static void discard_until_prompt (void)
{
gchar *line = NULL;

while (G_IO_STATUS_NORMAL == g_io_channel_read_line(gdb_ch_out, &line, NULL, NULL, NULL))
{
int prompted = !strcmp(GDB_PROMPT, line);
g_free(line);
if (prompted) return;
}
}

/*
* reads gdb_out until "(gdb)" prompt met
*/
Expand All @@ -209,12 +225,14 @@ static GList* read_until_prompt(void)
while (G_IO_STATUS_NORMAL == g_io_channel_read_line(gdb_ch_out, &line, NULL, &terminator, NULL))
{
if (!strcmp(GDB_PROMPT, line))
{
g_free (line);
break;
}

line[terminator] = '\0';
lines = g_list_prepend (lines, line);
}

return g_list_reverse(lines);
}

Expand Down Expand Up @@ -319,7 +337,6 @@ static gboolean on_read_async_output(GIOChannel * src, GIOCondition cond, gpoint
{
/* got some result */

GList *lines;
GList *commands = (GList*)data;

if (gdb_id_out)
Expand All @@ -328,9 +345,7 @@ static gboolean on_read_async_output(GIOChannel * src, GIOCondition cond, gpoint
gdb_id_out = 0;
}

lines = read_until_prompt();
g_list_foreach(lines, (GFunc)g_free, NULL);
g_list_free (lines);
discard_until_prompt();

if (!strcmp(record->klass, "done"))
{
Expand Down Expand Up @@ -369,7 +384,10 @@ static gboolean on_read_async_output(GIOChannel * src, GIOCondition cond, gpoint
update_files();

/* -exec-run */
exec_async_command("-exec-run");
if (remote_session)
exec_async_command("-exec-continue");
else
exec_async_command("-exec-run");
}
}
else
Expand Down Expand Up @@ -419,6 +437,21 @@ static gboolean on_read_from_gdb(GIOChannel * src, GIOCondition cond, gpointer d
if (G_IO_STATUS_NORMAL != g_io_channel_read_line(src, &line, NULL, &length, NULL))
return TRUE;

/* When the target program stops, gdb sometimes sends the "(gdb)" prompt
* before sending in the "stopped" event. There might be some timing..
* It is important to make sure that we received the prompt before
* issuing any new command, or else a leftover prompt will cause
* the next commands to be off by one prompt, and nothing works.
* The solution is to detect the prompt asynchronously. When the target
* is stopped, wait for the prompt only if it has not been received yet.
*/
if (!strcmp(GDB_PROMPT, line))
{
g_free(line);
detected_prompt = TRUE;
return TRUE;
}

record = gdb_mi_record_parse(line);

if (! record || record->type != GDB_MI_TYPE_PROMPT)
Expand Down Expand Up @@ -475,6 +508,8 @@ static gboolean on_read_from_gdb(GIOChannel * src, GIOCondition cond, gpointer d
gdb_id_out = 0;
}

if (!detected_prompt) discard_until_prompt();

/* looking for a reason to stop */
if ((reason = gdb_mi_result_var(record->first, "reason", GDB_MI_VAL_STRING)) != NULL)
{
Expand Down Expand Up @@ -575,15 +610,18 @@ static gboolean on_read_from_gdb(GIOChannel * src, GIOCondition cond, gpointer d
}

/* reading until prompt */
lines = read_until_prompt();
for (iter = lines; iter; iter = iter->next)
{
gchar *l = (gchar*)iter->data;
if (strcmp(l, GDB_PROMPT))
colorize_message(l);
g_free(l);
if (!detected_prompt)
{
lines = read_until_prompt();
for (iter = lines; iter; iter = iter->next)
{
gchar *l = (gchar*)iter->data;
if (strcmp(l, GDB_PROMPT))
colorize_message(l);
g_free(l);
}
g_list_free (lines);
}
g_list_free (lines);

/* send error message */
dbg_cbs->report_error(msg);
Expand All @@ -610,6 +648,7 @@ static void exec_async_command(const gchar* command)
gdb_input_write_line(command);

/* connect read callback to the output chanel */
detected_prompt = FALSE;
gdb_id_out = g_io_add_watch(gdb_ch_out, G_IO_IN, on_read_from_gdb, NULL);
}

Expand Down Expand Up @@ -715,7 +754,7 @@ static gchar *escape_string(const gchar *str)
/*
* starts gdb, collects commands and start the first one
*/
static gboolean run(const gchar* file, const gchar* commandline, GList* env, GList *witer, GList *biter, const gchar* terminal_device, dbg_callbacks* callbacks)
static gboolean run(gint dbgmode, const gchar* file, const gchar* commandline, GList* env, GList *witer, GList *biter, const gchar* terminal_device, dbg_callbacks* callbacks)
{
const gchar *exclude[] = { "LANG", NULL };
gchar **gdb_env = utils_copy_environment(exclude, "LANG", "C", NULL);
Expand All @@ -726,9 +765,21 @@ static gboolean run(const gchar* file, const gchar* commandline, GList* env, GLi
gchar *escaped;
int bp_index;
queue_item *item;
const gchar **gdb_args = gdb_args_local;

dbg_cbs = callbacks;

remote_session = FALSE;
switch (dbgmode)
{
case 1:
remote_session = TRUE;
break;
case 2:
remote_session = TRUE;
gdb_args = gdb_args_multi;
}

/* spawn GDB */
if (!g_spawn_async_with_pipes(working_directory, (gchar**)gdb_args, gdb_env,
GDB_SPAWN_FLAGS, NULL,
Expand Down Expand Up @@ -778,12 +829,17 @@ static gboolean run(const gchar* file, const gchar* commandline, GList* env, GLi

/* collect commands */

/* loading file */
escaped = escape_string(file);
command = g_strdup_printf("-file-exec-and-symbols \"%s\"", escaped);
commands = add_to_queue(commands, _("~\"Loading target file.\\n\""), command, _("Error loading file"), FALSE);
g_free(command);
g_free(escaped);
if (file && file[0]) /* File is optional in remote mode. */
{
/* loading file (Only for symbols in remote mode) */
const gchar *keyword =
remote_session ? "-file-symbol-file" : "-file-exec-and-symbols";
escaped = escape_string(file);
command = g_strdup_printf("%s \"%s\"", keyword, escaped);
commands = add_to_queue(commands, _("~\"Loading target file.\\n\""), command, _("Error loading file"), FALSE);
g_free(command);
g_free(escaped);
}

/* setting asyncronous mode */
commands = add_to_queue(commands, NULL, "-gdb-set target-async 1", _("Error configuring GDB"), FALSE);
Expand All @@ -800,11 +856,27 @@ static gboolean run(const gchar* file, const gchar* commandline, GList* env, GLi
g_free(command);

/* set arguments */
command = g_strdup_printf("-exec-arguments %s", commandline);
if (remote_session)
{
/* (remote sessions handle the progran name on debugserver side) */
if (strchr(commandline, ':') == NULL)
{
/* Use gdbserver's default port. */
command = g_strdup_printf("target remote %s:3278", commandline);
}
else
{
command = g_strdup_printf("target remote %s", commandline);
}
}
else
{
command = g_strdup_printf("-exec-arguments %s", commandline);
}
commands = add_to_queue(commands, NULL, command, NULL, FALSE);
g_free(command);

/* set passed evironment */
/* set passed environment */
iter = env;
while (iter)
{
Expand Down
3 changes: 3 additions & 0 deletions debugger/src/dconfig.c
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ static void debug_load_from_keyfile(GKeyFile *keyfile)
g_free(value);
/* debugger */
tpage_set_debugger(value = g_key_file_get_string(keyfile, DEBUGGER_GROUP, "debugger", NULL));
tpage_set_debugger_mode(value = g_key_file_get_string(keyfile, DEBUGGER_GROUP, "debugger_mode", NULL));
g_free(value);
/* arguments */
tpage_set_commandline(value = g_key_file_get_string(keyfile, DEBUGGER_GROUP, "arguments", NULL));
Expand Down Expand Up @@ -186,6 +187,7 @@ static void save_to_keyfile(GKeyFile *keyfile)

g_key_file_set_string(keyfile, DEBUGGER_GROUP, "target", tpage_get_target());
g_key_file_set_string(keyfile, DEBUGGER_GROUP, "debugger", tpage_get_debugger());
g_key_file_set_string(keyfile, DEBUGGER_GROUP, "debugger_mode", tpage_get_debugger_mode());
g_key_file_set_string(keyfile, DEBUGGER_GROUP, "arguments", tpage_get_commandline());

/* environment */
Expand Down Expand Up @@ -404,6 +406,7 @@ static void config_set_debug_defaults(GKeyFile *keyfile)
{
g_key_file_set_string(keyfile, DEBUGGER_GROUP, "target", "");
g_key_file_set_string(keyfile, DEBUGGER_GROUP, "debugger", "");
g_key_file_set_string(keyfile, DEBUGGER_GROUP, "debugger_mode", "");
g_key_file_set_string(keyfile, DEBUGGER_GROUP, "arguments", "");

g_key_file_set_integer(keyfile, DEBUGGER_GROUP, "envvar_count", 0);
Expand Down
75 changes: 48 additions & 27 deletions debugger/src/debug.c
Original file line number Diff line number Diff line change
Expand Up @@ -628,15 +628,8 @@ static void enable_sensitive_widgets(gboolean enable)
* Debug state changed hanflers
*/

/*
* called from debug module when debugger is being run
*/
static void on_debugger_run (void)
static void clear_stack (void)
{
/* update debug state */
debug_state = DBS_RUNNING;

/* if curren instruction marker was set previously - remove it */
if (stack)
{
remove_stack_markers();
Expand All @@ -646,6 +639,20 @@ static void on_debugger_run (void)

stree_remove_frames();
}
}

/*
* called from debug module when debugger is being run
*/
static void on_debugger_run (void)
{
/* update debug state */
debug_state = DBS_RUNNING;

/* Do not clear the stack yet. We do not want to loose the position
* if the target program stops at a new position with no source info.
* The previous position is better than some random editor cursor.
*/

/* disable widgets */
enable_sensitive_widgets(FALSE);
Expand Down Expand Up @@ -699,9 +706,35 @@ static void on_debugger_stopped (int thread_id)
stree_set_active_thread_id(thread_id);

/* get current stack trace and put in the tree view */
stack = active_module->get_stack();
stree_add (stack);
stree_select_first_frame(TRUE);
GList* new_stack = active_module->get_stack();
if (new_stack)
{
/* Is this new stack referencing a source file that we can access?
* If not, it is better to keep the previous stack info, as it is
* closer to the truth than some random editor cursor.
*/
gboolean found_source = FALSE;
frame *current = (frame*)new_stack->data;

if (current->have_source)
{
/* open current instruction position */
found_source = editor_open_position_with_status(current->file, current->line);
}

if (found_source)
{
/* clear previous stack tree view */
clear_stack();
stack = new_stack;

stree_add (stack);
stree_select_first_frame(TRUE);

/* add current instruction marker */
add_stack_markers();
}
}

/* files */
files = active_module->get_files();
Expand Down Expand Up @@ -760,20 +793,6 @@ static void on_debugger_stopped (int thread_id)
watches = active_module->get_watches();
update_variables(GTK_TREE_VIEW(wtree), NULL, watches);

if (stack)
{
frame *current = (frame*)stack->data;

if (current->have_source)
{
/* open current instruction position */
editor_open_position(current->file, current->line);
}

/* add current instruction marker */
add_stack_markers();
}

/* enable widgets */
enable_sensitive_widgets(TRUE);

Expand Down Expand Up @@ -1189,11 +1208,13 @@ void debug_run(void)
{
if (DBS_IDLE == debug_state)
{
int mode;
gchar *target, *commandline;
GList *env, *watches, *breaks;

target = g_strstrip(tpage_get_target());
if (!strlen(target))
mode = tpage_get_debug_mode_index();
if ((!strlen(target)) && (!mode))
{
g_free(target);
return;
Expand All @@ -1205,7 +1226,7 @@ void debug_run(void)

/* init selected debugger module */
active_module = modules[tpage_get_debug_module_index()].module;
if(active_module->run(target, commandline, env, watches, breaks, ttyname(pty_slave), &callbacks))
if(active_module->run(mode, target, commandline, env, watches, breaks, ttyname(pty_slave), &callbacks))
{
/* set target page - readonly */
tpage_set_readonly(TRUE);
Expand Down
Loading