From 32c1e744c108afe9bebd41766ab3025279878f85 Mon Sep 17 00:00:00 2001 From: Vladimir Prus Date: Fri, 14 Mar 2008 18:57:44 +0000 Subject: [PATCH] Async mode fixes. * Makefile.in (infcmd.o, inf-loop.o): Update dependencies. * breakpoint.c (bpstat_do_actions): In async mode, don't jump to top expecting stop_bpstat to be already updated. * event-loop.c (start_event_loop): Call async_enable_stdin on exception. * event-top.c (async_enable_stdin): Do nothing if sync_execution is not set. (command_handler): Do not setup continuation here. (command_line_handler_continuation): Move to... * top.c (command_line_handler_continuation): ... here. (execute_command): In async mode, register continuation. Don't check frame's language in running in async mode. * exceptions.c (throw_exception): Don't do exec_error_cleanups. * inf-loop.c (complete_execution): Inline into... (inferior_event_handler): ... here. Clear target_executing before doing any cleanups. Don't try to show prompt if the target was resumed. * infcmd.c (signal_command): Add support for async mode. (finish_command): Only add continuation if the target was successfully resumed. * remote.c (init_async_opts): Register to_get_thread_local_address handler. * mi/mi-interp.c (mi_cmd_interpreter_exec): Don't mess with sync_execution. * tui/tui-interp.c (tui_command_loop): Call async_enable_stdin on exception. --- gdb/ChangeLog | 31 ++++++++++++++++++ gdb/Makefile.in | 5 +-- gdb/breakpoint.c | 29 +++++++++++++--- gdb/event-loop.c | 4 +++ gdb/event-top.c | 75 ++++++------------------------------------ gdb/exceptions.c | 6 ++-- gdb/inf-loop.c | 78 ++++++++++++++++++++++++++------------------ gdb/infcmd.c | 29 +++++++++++++--- gdb/mi/mi-interp.c | 24 ++++---------- gdb/remote.c | 2 ++ gdb/top.c | 77 ++++++++++++++++++++++++++++++++++++++++++- gdb/tui/tui-interp.c | 4 +++ 12 files changed, 237 insertions(+), 127 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 082c44c2be6..9526ba37485 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,34 @@ +2008-03-14 Vladimir Prus + + Async mode fixes. + * Makefile.in (infcmd.o, inf-loop.o): Update dependencies. + * breakpoint.c (bpstat_do_actions): In async mode, + don't jump to top expecting stop_bpstat to be already + updated. + * event-loop.c (start_event_loop): Call async_enable_stdin + on exception. + * event-top.c (async_enable_stdin): Do nothing if sync_execution + is not set. + (command_handler): Do not setup continuation here. + (command_line_handler_continuation): Move to... + * top.c (command_line_handler_continuation): ... here. + (execute_command): In async mode, register continuation. + Don't check frame's language in running in async mode. + * exceptions.c (throw_exception): Don't do exec_error_cleanups. + * inf-loop.c (complete_execution): Inline into... + (inferior_event_handler): ... here. Clear target_executing before + doing any cleanups. Don't try to show prompt if the target was + resumed. + * infcmd.c (signal_command): Add support for async mode. + (finish_command): Only add continuation if the target was + successfully resumed. + * remote.c (init_async_opts): Register to_get_thread_local_address + handler. + * mi/mi-interp.c (mi_cmd_interpreter_exec): Don't mess + with sync_execution. + * tui/tui-interp.c (tui_command_loop): Call async_enable_stdin + on exception. + 2008-03-14 Daniel Jacobowitz * corefile.c (reopen_exec_file): Use exec_bfd_mtime. diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 04db23391ac..a3e73b914fe 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -2291,9 +2291,10 @@ infcmd.o: infcmd.c $(defs_h) $(gdb_string_h) $(symtab_h) $(gdbtypes_h) \ $(objfiles_h) $(completer_h) $(ui_out_h) $(event_top_h) \ $(parser_defs_h) $(regcache_h) $(reggroups_h) $(block_h) \ $(solib_h) $(gdb_assert_h) $(observer_h) $(target_descriptions_h) \ - $(user_regs_h) + $(user_regs_h) $(exceptions_h) inf-loop.o: inf-loop.c $(defs_h) $(inferior_h) $(target_h) $(event_loop_h) \ - $(event_top_h) $(inf_loop_h) $(remote_h) $(exceptions_h) + $(event_top_h) $(inf_loop_h) $(remote_h) $(exceptions_h) \ + $(language_h) inflow.o: inflow.c $(defs_h) $(frame_h) $(inferior_h) $(command_h) \ $(serial_h) $(terminal_h) $(target_h) $(gdbthread_h) $(gdb_string_h) \ $(inflow_h) $(gdb_select_h) diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c index 4fbda0b6e8e..6830efef120 100644 --- a/gdb/breakpoint.c +++ b/gdb/breakpoint.c @@ -2114,11 +2114,30 @@ top: do_cleanups (this_cmd_tree_chain); if (breakpoint_proceeded) - /* The inferior is proceeded by the command; bomb out now. - The bpstat chain has been blown away by wait_for_inferior. - But since execution has stopped again, there is a new bpstat - to look at, so start over. */ - goto top; + { + if (target_can_async_p ()) + /* If we are in async mode, then the target might + be still running, not stopped at any breakpoint, + so nothing for us to do here -- just return to + the event loop. */ + break; + else + /* In sync mode, when execute_control_command returns + we're already standing on the next breakpoint. + Breakpoint commands for that stop were not run, + since execute_command does not run breakpoint + commands -- only command_line_handler does, but + that one is not involved in execution of breakpoint + commands. So, we can now execute breakpoint commands. + There's an implicit assumption that we're called with + stop_bpstat, so our parameter is the new bpstat to + handle. + It should be noted that making execute_command do + bpstat actions is not an option -- in this case we'll + have recursive invocation of bpstat for each breakpoint + with a command, and can easily blow up GDB stack. */ + goto top; + } } do_cleanups (old_chain); } diff --git a/gdb/event-loop.c b/gdb/event-loop.c index 9a04e32a0a0..ceff6997ff4 100644 --- a/gdb/event-loop.c +++ b/gdb/event-loop.c @@ -411,6 +411,10 @@ start_event_loop (void) if (gdb_result == 0) { + /* If any exception escaped to here, we better enable + stdin. Otherwise, any command that calls async_disable_stdin, + and then throws, will leave stdin inoperable. */ + async_enable_stdin ((void *) 0); /* FIXME: this should really be a call to a hook that is interface specific, because interfaces can display the prompt in their own way. */ diff --git a/gdb/event-top.c b/gdb/event-top.c index f798eee8726..79dd4c32ae5 100644 --- a/gdb/event-top.c +++ b/gdb/event-top.c @@ -44,7 +44,6 @@ static void rl_callback_read_char_wrapper (gdb_client_data client_data); static void command_line_handler (char *rl); -static void command_line_handler_continuation (struct continuation_arg *arg); static void change_line_handler (void); static void change_annotation_level (void); static void command_handler (char *command); @@ -438,13 +437,16 @@ stdin_event_handler (int error, gdb_client_data client_data) void async_enable_stdin (void *dummy) { - /* See NOTE in async_disable_stdin() */ - /* FIXME: cagney/1999-09-27: Call this before clearing - sync_execution. Current target_terminal_ours() implementations - check for sync_execution before switching the terminal. */ - target_terminal_ours (); - pop_prompt (); - sync_execution = 0; + if (sync_execution) + { + /* See NOTE in async_disable_stdin() */ + /* FIXME: cagney/1999-09-27: Call this before clearing + sync_execution. Current target_terminal_ours() implementations + check for sync_execution before switching the terminal. */ + target_terminal_ours (); + pop_prompt (); + sync_execution = 0; + } } /* Disable reads from stdin (the console) marking the command as @@ -480,8 +482,6 @@ command_handler (char *command) { struct cleanup *old_chain; int stdin_is_tty = ISATTY (stdin); - struct continuation_arg *arg1; - struct continuation_arg *arg2; long time_at_cmd_start; #ifdef HAVE_SBRK long space_at_cmd_start = 0; @@ -517,24 +517,6 @@ command_handler (char *command) execute_command (command, instream == stdin); - /* Set things up for this function to be compete later, once the - execution has completed, if we are doing an execution command, - otherwise, just go ahead and finish. */ - if (target_can_async_p () && target_executing) - { - arg1 = - (struct continuation_arg *) xmalloc (sizeof (struct continuation_arg)); - arg2 = - (struct continuation_arg *) xmalloc (sizeof (struct continuation_arg)); - arg1->next = arg2; - arg2->next = NULL; - arg1->data.longint = time_at_cmd_start; -#ifdef HAVE_SBRK - arg2->data.longint = space_at_cmd_start; -#endif - add_continuation (command_line_handler_continuation, arg1); - } - /* Do any commands attached to breakpoint we stopped at. Only if we are always running synchronously. Or if we have just executed a command that doesn't start the target. */ @@ -567,43 +549,6 @@ command_handler (char *command) } } -/* Do any commands attached to breakpoint we stopped at. Only if we - are always running synchronously. Or if we have just executed a - command that doesn't start the target. */ -void -command_line_handler_continuation (struct continuation_arg *arg) -{ - extern int display_time; - extern int display_space; - - long time_at_cmd_start = arg->data.longint; - long space_at_cmd_start = arg->next->data.longint; - - bpstat_do_actions (&stop_bpstat); - /*do_cleanups (old_chain); *//*?????FIXME????? */ - - if (display_time) - { - long cmd_time = get_run_time () - time_at_cmd_start; - - printf_unfiltered (_("Command execution time: %ld.%06ld\n"), - cmd_time / 1000000, cmd_time % 1000000); - } - if (display_space) - { -#ifdef HAVE_SBRK - char *lim = (char *) sbrk (0); - long space_now = lim - lim_at_start; - long space_diff = space_now - space_at_cmd_start; - - printf_unfiltered (_("Space used: %ld (%c%ld for this command)\n"), - space_now, - (space_diff >= 0 ? '+' : '-'), - space_diff); -#endif - } -} - /* Handle a complete line of input. This is called by the callback mechanism within the readline library. Deal with incomplete commands as well, by saving the partial input in a global buffer. */ diff --git a/gdb/exceptions.c b/gdb/exceptions.c index ae30367f9bc..89d14551a57 100644 --- a/gdb/exceptions.c +++ b/gdb/exceptions.c @@ -221,10 +221,12 @@ throw_exception (struct gdb_exception exception) disable_current_display (); do_cleanups (ALL_CLEANUPS); + /* When we implement non-stop mode, this should be redone. If we get + exception in a command pertaining to one thread, or maybe even not + involving inferior at all, we should not do exec cleanups for all + threads. */ if (target_can_async_p () && !target_executing) do_exec_cleanups (ALL_CLEANUPS); - if (sync_execution) - do_exec_error_cleanups (ALL_CLEANUPS); /* Jump to the containing catch_errors() call, communicating REASON to that call via setjmp's return value. Note that REASON can't diff --git a/gdb/inf-loop.c b/gdb/inf-loop.c index b6f8bb8d728..c4fb1111994 100644 --- a/gdb/inf-loop.c +++ b/gdb/inf-loop.c @@ -25,9 +25,9 @@ #include "inf-loop.h" #include "remote.h" #include "exceptions.h" +#include "language.h" static int fetch_inferior_event_wrapper (gdb_client_data client_data); -static void complete_execution (void); void inferior_event_handler_wrapper (gdb_client_data client_data) @@ -43,6 +43,7 @@ void inferior_event_handler (enum inferior_event_type event_type, gdb_client_data client_data) { + int was_sync = 0; switch (event_type) { case INF_ERROR: @@ -70,11 +71,52 @@ inferior_event_handler (enum inferior_event_type event_type, break; case INF_EXEC_COMPLETE: - /* Is there anything left to do for the command issued to - complete? */ + + /* This is the first thing to do -- so that continuations know that + the target is stopped. For example, command_line_handler_continuation + will run breakpoint commands, and if we think that the target is + running, we'll refuse to execute most commands. MI continuation + presently uses target_executing to either print or not print *stopped. */ + target_executing = 0; + + /* Unregister the inferior from the event loop. This is done so that + when the inferior is not running we don't get distracted by + spurious inferior output. */ + if (target_has_execution) + target_async (NULL, 0); + + /* Calls to do_exec_error_cleanup below will call async_enable_stdin, + and that resets 'sync_execution'. However, if we were running + in sync execution mode, we also need to display the prompt. */ + was_sync = sync_execution; + + if (was_sync) + do_exec_error_cleanups (ALL_CLEANUPS); + do_all_continuations (); - /* Reset things after target has stopped for the async commands. */ - complete_execution (); + + if (current_language != expected_language) + { + if (language_mode == language_mode_auto) + { + language_info (1); /* Print what changed. */ + } + } + + /* If the continuation did not start the target again, + prepare for interation with the user. */ + if (!target_executing) + { + if (was_sync) + { + display_gdb_prompt (0); + } + else + { + if (exec_done_display_p) + printf_unfiltered (_("completed.\n")); + } + } break; case INF_EXEC_CONTINUE: @@ -103,29 +145,3 @@ fetch_inferior_event_wrapper (gdb_client_data client_data) fetch_inferior_event (client_data); return 1; } - -/* Reset proper settings after an asynchronous command has finished. - If the execution command was in synchronous mode, register stdin - with the event loop, and reset the prompt. */ - -static void -complete_execution (void) -{ - target_executing = 0; - - /* Unregister the inferior from the event loop. This is done so that - when the inferior is not running we don't get distracted by - spurious inferior output. */ - target_async (NULL, 0); - - if (sync_execution) - { - do_exec_error_cleanups (ALL_CLEANUPS); - display_gdb_prompt (0); - } - else - { - if (exec_done_display_p) - printf_unfiltered (_("completed.\n")); - } -} diff --git a/gdb/infcmd.c b/gdb/infcmd.c index 70bf69536fc..3a22c1c25b1 100644 --- a/gdb/infcmd.c +++ b/gdb/infcmd.c @@ -48,6 +48,7 @@ #include "observer.h" #include "target-descriptions.h" #include "user-regs.h" +#include "exceptions.h" /* Functions exported for general use, in inferior.h: */ @@ -1005,10 +1006,28 @@ static void signal_command (char *signum_exp, int from_tty) { enum target_signal oursig; + int async_exec = 0; dont_repeat (); /* Too dangerous. */ ERROR_NO_INFERIOR; + /* Find out whether we must run in the background. */ + if (signum_exp != NULL) + async_exec = strip_bg_char (&signum_exp); + + /* If we must run in the background, but the target can't do it, + error out. */ + if (async_exec && !target_can_async_p ()) + error (_("Asynchronous execution not supported on this target.")); + + /* If we are not asked to run in the bg, then prepare to run in the + foreground, synchronously. */ + if (!async_exec && target_can_async_p ()) + { + /* Simulate synchronous execution. */ + async_disable_stdin (); + } + if (!signum_exp) error_no_arg (_("signal number")); @@ -1321,10 +1340,15 @@ finish_command (char *arg, int from_tty) print_stack_frame (get_selected_frame (NULL), 1, LOCATION); } + proceed_to_finish = 1; /* We want stop_registers, please... */ + proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 0); + /* If running asynchronously and the target support asynchronous execution, set things up for the rest of the finish command to be completed later on, when gdb has detected that the target has - stopped, in fetch_inferior_event. */ + stopped, in fetch_inferior_event. + Setup it only after proceed, so that if proceed throws, we don't + set continuation. */ if (target_can_async_p ()) { arg1 = @@ -1342,9 +1366,6 @@ finish_command (char *arg, int from_tty) add_continuation (finish_command_continuation, arg1); } - proceed_to_finish = 1; /* We want stop_registers, please... */ - proceed ((CORE_ADDR) -1, TARGET_SIGNAL_DEFAULT, 0); - /* Do this only if not running asynchronously or if the target cannot do async execution. Otherwise, complete this command when the target actually stops, in fetch_inferior_event. */ diff --git a/gdb/mi/mi-interp.c b/gdb/mi/mi-interp.c index 96229a01d29..05a64d811bb 100644 --- a/gdb/mi/mi-interp.c +++ b/gdb/mi/mi-interp.c @@ -224,23 +224,13 @@ mi_cmd_interpreter_exec (char *command, char **argv, int argc) for (i = 1; i < argc; i++) { - /* We had to set sync_execution = 0 for the mi (well really for Project - Builder's use of the mi - particularly so interrupting would work. - But for console commands to work, we need to initialize it to 1 - - since that is what the cli expects - before running the command, - and then set it back to 0 when we are done. */ - sync_execution = 1; - { - struct gdb_exception e = interp_exec (interp_to_use, argv[i]); - if (e.reason < 0) - { - mi_error_message = xstrdup (e.message); - result = MI_CMD_ERROR; - break; - } - } - do_exec_error_cleanups (ALL_CLEANUPS); - sync_execution = 0; + struct gdb_exception e = interp_exec (interp_to_use, argv[i]); + if (e.reason < 0) + { + mi_error_message = xstrdup (e.message); + result = MI_CMD_ERROR; + break; + } } mi_remove_notify_hooks (); diff --git a/gdb/remote.c b/gdb/remote.c index 88cde580bb2..73fcc2a60e9 100644 --- a/gdb/remote.c +++ b/gdb/remote.c @@ -7348,6 +7348,8 @@ Specify the serial device it is connected to (e.g. /dev/ttya)."; remote_async_ops.to_stop = remote_stop; remote_async_ops.to_xfer_partial = remote_xfer_partial; remote_async_ops.to_rcmd = remote_rcmd; + remote_async_ops.to_get_thread_local_address + = remote_get_thread_local_address; remote_async_ops.to_stratum = process_stratum; remote_async_ops.to_has_all_memory = 1; remote_async_ops.to_has_memory = 1; diff --git a/gdb/top.c b/gdb/top.c index c205ec33e91..3b9aaab7ffe 100644 --- a/gdb/top.c +++ b/gdb/top.c @@ -364,6 +364,42 @@ do_chdir_cleanup (void *old_dir) } #endif +/* Do any commands attached to breakpoint we stopped at. Only if we + are always running synchronously. Or if we have just executed a + command that doesn't start the target. */ +static void +command_line_handler_continuation (struct continuation_arg *arg) +{ + extern int display_time; + extern int display_space; + + long time_at_cmd_start = arg->data.longint; + long space_at_cmd_start = arg->next->data.longint; + + bpstat_do_actions (&stop_bpstat); + + if (display_time) + { + long cmd_time = get_run_time () - time_at_cmd_start; + + printf_unfiltered (_("Command execution time: %ld.%06ld\n"), + cmd_time / 1000000, cmd_time % 1000000); + } + if (display_space) + { +#ifdef HAVE_SBRK + char *lim = (char *) sbrk (0); + long space_now = lim - lim_at_start; + long space_diff = space_now - space_at_cmd_start; + + printf_unfiltered (_("Space used: %ld (%c%ld for this command)\n"), + space_now, + (space_diff >= 0 ? '+' : '-'), + space_diff); +#endif + } +} + /* Execute the line P as a command. Pass FROM_TTY as second argument to the defining function. */ @@ -374,6 +410,27 @@ execute_command (char *p, int from_tty) enum language flang; static int warned = 0; char *line; + struct continuation_arg *arg1; + struct continuation_arg *arg2; + long time_at_cmd_start; +#ifdef HAVE_SBRK + long space_at_cmd_start = 0; +#endif + extern int display_time; + extern int display_space; + + if (target_can_async_p ()) + { + time_at_cmd_start = get_run_time (); + + if (display_space) + { +#ifdef HAVE_SBRK + char *lim = (char *) sbrk (0); + space_at_cmd_start = lim - lim_at_start; +#endif + } + } free_all_values (); @@ -470,7 +527,7 @@ execute_command (char *p, int from_tty) /* FIXME: This should be cacheing the frame and only running when the frame changes. */ - if (target_has_stack) + if (!target_executing && target_has_stack) { flang = get_frame_language (); if (!warned @@ -481,6 +538,24 @@ execute_command (char *p, int from_tty) warned = 1; } } + + /* Set things up for this function to be compete later, once the + execution has completed, if we are doing an execution command, + otherwise, just go ahead and finish. */ + if (target_can_async_p () && target_executing) + { + arg1 = + (struct continuation_arg *) xmalloc (sizeof (struct continuation_arg)); + arg2 = + (struct continuation_arg *) xmalloc (sizeof (struct continuation_arg)); + arg1->next = arg2; + arg2->next = NULL; + arg1->data.longint = time_at_cmd_start; +#ifdef HAVE_SBRK + arg2->data.longint = space_at_cmd_start; +#endif + add_continuation (command_line_handler_continuation, arg1); + } } /* Read commands from `instream' and execute them diff --git a/gdb/tui/tui-interp.c b/gdb/tui/tui-interp.c index ca2e7436bea..8a166785272 100644 --- a/gdb/tui/tui-interp.c +++ b/gdb/tui/tui-interp.c @@ -164,6 +164,10 @@ tui_command_loop (void *data) if (result == 0) { + /* If any exception escaped to here, we better enable + stdin. Otherwise, any command that calls async_disable_stdin, + and then throws, will leave stdin inoperable. */ + async_enable_stdin ((void *) 0); /* FIXME: this should really be a call to a hook that is interface specific, because interfaces can display the prompt in their own way. */ -- 2.30.2