Garbage collect thread continuations
[deliverable/binutils-gdb.git] / gdb / infrun.c
index c3e7dd78842cf3e0371785df9f12fb807f45a768..e89e02aeb0814f1045baf066987348e43bbf262a 100644 (file)
@@ -62,6 +62,7 @@
 #include "terminal.h"
 #include "solist.h"
 #include "event-loop.h"
+#include "thread-fsm.h"
 
 /* Prototypes for local functions */
 
@@ -131,6 +132,14 @@ infrun_async (int enable)
     }
 }
 
+/* See infrun.h.  */
+
+void
+mark_infrun_async_event_handler (void)
+{
+  mark_async_event_handler (infrun_async_inferior_event_token);
+}
+
 /* When set, stop the 'step' command if we enter a function which has
    no line number information.  The normal behavior is that we step
    over such function.  */
@@ -1562,7 +1571,7 @@ add_displaced_stepping_state (int pid)
     if (state->pid == pid)
       return state;
 
-  state = xcalloc (1, sizeof (*state));
+  state = XCNEW (struct displaced_step_inferior_state);
   state->pid = pid;
   state->next = displaced_step_inferior_states;
   displaced_step_inferior_states = state;
@@ -2741,6 +2750,9 @@ clear_proceed_status_thread (struct thread_info *tp)
   if (!signal_pass_state (tp->suspend.stop_signal))
     tp->suspend.stop_signal = GDB_SIGNAL_0;
 
+  thread_fsm_delete (tp->thread_fsm);
+  tp->thread_fsm = NULL;
+
   tp->control.trap_expected = 0;
   tp->control.step_range_start = 0;
   tp->control.step_range_end = 0;
@@ -3094,15 +3106,11 @@ proceed (CORE_ADDR addr, enum gdb_signal siggnal)
 
   discard_cleanups (old_chain);
 
-  /* Wait for it to stop (if not standalone)
-     and in any case decode why it stopped, and act accordingly.  */
-  /* Do this only if we are not using the event loop, or if the target
-     does not support asynchronous execution.  */
+  /* Tell the event loop to wait for it to stop.  If the target
+     supports asynchronous execution, it'll do this from within
+     target_resume.  */
   if (!target_can_async_p ())
-    {
-      wait_for_inferior ();
-      normal_stop ();
-    }
+    mark_async_event_handler (infrun_async_inferior_event_token);
 }
 \f
 
@@ -3216,7 +3224,7 @@ infrun_thread_stop_requested_callback (struct thread_info *info, void *arg)
         have consistent output as if the stop event had been
         reported.  */
       ecs->ptid = info->ptid;
-      ecs->event_thread = find_thread_ptid (info->ptid);
+      ecs->event_thread = info;
       ecs->ws.kind = TARGET_WAITKIND_STOPPED;
       ecs->ws.value.sig = GDB_SIGNAL_0;
 
@@ -3224,14 +3232,10 @@ infrun_thread_stop_requested_callback (struct thread_info *info, void *arg)
 
       if (!ecs->wait_some_more)
        {
-         struct thread_info *tp;
+         /* Cancel any running execution command.  */
+         thread_cancel_execution_command (info);
 
          normal_stop ();
-
-         /* Finish off the continuations.  */
-         tp = inferior_thread ();
-         do_all_intermediate_continuations_thread (tp, 1);
-         do_all_continuations_thread (tp, 1);
        }
 
       do_cleanups (old_chain);
@@ -3714,6 +3718,35 @@ reinstall_readline_callback_handler_cleanup (void *arg)
     gdb_rl_callback_handler_reinstall ();
 }
 
+/* Clean up the FSMs of threads that are now stopped.  In non-stop,
+   that's just the event thread.  In all-stop, that's all threads.  */
+
+static void
+clean_up_just_stopped_threads_fsms (struct execution_control_state *ecs)
+{
+  struct thread_info *thr = ecs->event_thread;
+
+  if (thr != NULL && thr->thread_fsm != NULL)
+    thread_fsm_clean_up (thr->thread_fsm);
+
+  if (!non_stop)
+    {
+      ALL_NON_EXITED_THREADS (thr)
+        {
+         if (thr->thread_fsm == NULL)
+           continue;
+         if (thr == ecs->event_thread)
+           continue;
+
+         switch_to_thread (thr->ptid);
+         thread_fsm_clean_up (thr->thread_fsm);
+       }
+
+      if (ecs->event_thread != NULL)
+       switch_to_thread (ecs->event_thread->ptid);
+    }
+}
+
 /* Asynchronous version of wait_for_inferior.  It is called by the
    event loop whenever a change of state is detected on the file
    descriptor corresponding to the target.  It can be called more than
@@ -3766,7 +3799,8 @@ fetch_inferior_event (void *client_data)
   make_cleanup_restore_integer (&execution_direction);
   execution_direction = target_execution_direction ();
 
-  ecs->ptid = do_target_wait (waiton_ptid, &ecs->ws, TARGET_WNOHANG);
+  ecs->ptid = do_target_wait (waiton_ptid, &ecs->ws,
+                             target_can_async_p () ? TARGET_WNOHANG : 0);
 
   if (debug_infrun)
     print_target_wait_results (waiton_ptid, ecs->ptid, &ecs->ws);
@@ -3791,24 +3825,43 @@ fetch_inferior_event (void *client_data)
   if (!ecs->wait_some_more)
     {
       struct inferior *inf = find_inferior_ptid (ecs->ptid);
+      int should_stop = 1;
+      struct thread_info *thr = ecs->event_thread;
+      int should_notify_stop = 1;
 
       delete_just_stopped_threads_infrun_breakpoints ();
 
-      /* We may not find an inferior if this was a process exit.  */
-      if (inf == NULL || inf->control.stop_soon == NO_STOP_QUIETLY)
-       normal_stop ();
-
-      if (target_has_execution
-         && ecs->ws.kind != TARGET_WAITKIND_NO_RESUMED
-         && ecs->ws.kind != TARGET_WAITKIND_EXITED
-         && ecs->ws.kind != TARGET_WAITKIND_SIGNALLED
-         && ecs->event_thread->step_multi
-         && ecs->event_thread->control.stop_step)
-       inferior_event_handler (INF_EXEC_CONTINUE, NULL);
+      if (thr != NULL)
+       {
+         struct thread_fsm *thread_fsm = thr->thread_fsm;
+
+         if (thread_fsm != NULL)
+           should_stop = thread_fsm_should_stop (thread_fsm);
+       }
+
+      if (!should_stop)
+       {
+         keep_going (ecs);
+       }
       else
        {
-         inferior_event_handler (INF_EXEC_COMPLETE, NULL);
-         cmd_done = 1;
+         clean_up_just_stopped_threads_fsms (ecs);
+
+         if (thr != NULL && thr->thread_fsm != NULL)
+           {
+             should_notify_stop
+               = thread_fsm_should_notify_stop (thr->thread_fsm);
+           }
+
+         if (should_notify_stop)
+           {
+             /* We may not find an inferior if this was a process exit.  */
+             if (inf == NULL || inf->control.stop_soon == NO_STOP_QUIETLY)
+               normal_stop ();
+
+             inferior_event_handler (INF_EXEC_COMPLETE, NULL);
+             cmd_done = 1;
+           }
        }
     }
 
@@ -5893,6 +5946,10 @@ process_event_stop_test (struct execution_control_state *ecs)
       stop_stack_dummy = what.call_dummy;
     }
 
+  /* A few breakpoint types have callbacks associated (e.g.,
+     bp_jit_event).  Run them now.  */
+  bpstat_run_callbacks (ecs->event_thread->control.stop_bpstat);
+
   /* If we hit an internal event that triggers symbol changes, the
      current frame will be invalidated within bpstat_what (e.g., if we
      hit an internal solib event).  Re-fetch it.  */
@@ -7520,10 +7577,10 @@ prepare_to_wait (struct execution_control_state *ecs)
   if (debug_infrun)
     fprintf_unfiltered (gdb_stdlog, "infrun: prepare_to_wait\n");
 
-  /* This is the old end of the while loop.  Let everybody know we
-     want to wait for the inferior some more and get called again
-     soon.  */
   ecs->wait_some_more = 1;
+
+  if (!target_is_async_p ())
+    mark_infrun_async_event_handler ();
 }
 
 /* We are done with the step range of a step/next/si/ni command.
@@ -7658,8 +7715,8 @@ print_no_history_reason (struct ui_out *uiout)
    bpstat_print contains the logic deciding in detail what to print,
    based on the event(s) that just occurred.  */
 
-void
-print_stop_event (struct target_waitstatus *ws)
+static void
+print_stop_location (struct target_waitstatus *ws)
 {
   int bpstat_ret;
   enum print_what source_flag;
@@ -7710,9 +7767,67 @@ print_stop_event (struct target_waitstatus *ws)
      SRC_AND_LOC: Print location and source line.  */
   if (do_frame_printing)
     print_stack_frame (get_selected_frame (NULL), 0, source_flag, 1);
+}
+
+/* Cleanup that restores a previous current uiout.  */
+
+static void
+restore_current_uiout_cleanup (void *arg)
+{
+  struct ui_out *saved_uiout = arg;
+
+  current_uiout = saved_uiout;
+}
+
+/* See infrun.h.  */
+
+void
+print_stop_event (struct ui_out *uiout)
+{
+  struct cleanup *old_chain;
+  struct target_waitstatus last;
+  ptid_t last_ptid;
+  struct thread_info *tp;
+
+  get_last_target_status (&last_ptid, &last);
+
+  old_chain = make_cleanup (restore_current_uiout_cleanup, current_uiout);
+  current_uiout = uiout;
+
+  print_stop_location (&last);
 
   /* Display the auto-display expressions.  */
   do_displays ();
+
+  do_cleanups (old_chain);
+
+  tp = inferior_thread ();
+  if (tp->thread_fsm != NULL
+      && thread_fsm_finished_p (tp->thread_fsm))
+    {
+      struct return_value_info *rv;
+
+      rv = thread_fsm_return_value (tp->thread_fsm);
+      if (rv != NULL)
+       print_return_value (uiout, rv);
+    }
+}
+
+/* See infrun.h.  */
+
+void
+maybe_remove_breakpoints (void)
+{
+  if (!breakpoints_should_be_inserted_now () && target_has_execution)
+    {
+      if (remove_breakpoints ())
+       {
+         target_terminal_ours_for_output ();
+         printf_filtered (_("Cannot remove breakpoints because "
+                            "program is no longer writable.\nFurther "
+                            "execution is probably impossible.\n"));
+       }
+    }
 }
 
 /* Here to return control to GDB when the inferior stops for real.
@@ -7808,16 +7923,7 @@ normal_stop (void)
     }
 
   /* Note: this depends on the update_thread_list call above.  */
-  if (!breakpoints_should_be_inserted_now () && target_has_execution)
-    {
-      if (remove_breakpoints ())
-       {
-         target_terminal_ours_for_output ();
-         printf_filtered (_("Cannot remove breakpoints because "
-                            "program is no longer writable.\nFurther "
-                            "execution is probably impossible.\n"));
-       }
-    }
+  maybe_remove_breakpoints ();
 
   /* If an auto-display called a function and that got a signal,
      delete that auto-display to avoid an infinite recursion.  */
@@ -7825,41 +7931,39 @@ normal_stop (void)
   if (stopped_by_random_signal)
     disable_current_display ();
 
-  /* Notify observers if we finished a "step"-like command, etc.  */
-  if (target_has_execution
-      && last.kind != TARGET_WAITKIND_SIGNALLED
-      && last.kind != TARGET_WAITKIND_EXITED
-      && inferior_thread ()->control.stop_step)
+  target_terminal_ours ();
+  async_enable_stdin ();
+
+  /* Let the user/frontend see the threads as stopped.  */
+  do_cleanups (old_chain);
+
+  /* Select innermost stack frame - i.e., current frame is frame 0,
+     and current location is based on that.  Handle the case where the
+     dummy call is returning after being stopped.  E.g. the dummy call
+     previously hit a breakpoint.  (If the dummy call returns
+     normally, we won't reach here.)  Do this before the stop hook is
+     run, so that it doesn't get to see the temporary dummy frame,
+     which is not where we'll present the stop.  */
+  if (has_stack_frames ())
     {
-      /* But not if in the middle of doing a "step n" operation for
-        n > 1 */
-      if (inferior_thread ()->step_multi)
-       goto done;
+      if (stop_stack_dummy == STOP_STACK_DUMMY)
+       {
+         /* Pop the empty frame that contains the stack dummy.  This
+            also restores inferior state prior to the call (struct
+            infcall_suspend_state).  */
+         struct frame_info *frame = get_current_frame ();
 
-      observer_notify_end_stepping_range ();
-    }
+         gdb_assert (get_frame_type (frame) == DUMMY_FRAME);
+         frame_pop (frame);
+         /* frame_pop calls reinit_frame_cache as the last thing it
+            does which means there's now no selected frame.  */
+       }
 
-  target_terminal_ours ();
-  async_enable_stdin ();
+      select_frame (get_current_frame ());
 
-  /* Set the current source location.  This will also happen if we
-     display the frame below, but the current SAL will be incorrect
-     during a user hook-stop function.  */
-  if (has_stack_frames () && !stop_stack_dummy)
-    set_current_sal_from_frame (get_current_frame ());
-
-  /* Let the user/frontend see the threads as stopped, but defer to
-     call_function_by_hand if the thread finished an infcall
-     successfully.  We may be e.g., evaluating a breakpoint condition.
-     In that case, the thread had state THREAD_RUNNING before the
-     infcall, and shall remain marked running, all without informing
-     the user/frontend about state transition changes.  */
-  if (target_has_execution
-      && inferior_thread ()->control.in_infcall
-      && stop_stack_dummy == STOP_STACK_DUMMY)
-    discard_cleanups (old_chain);
-  else
-    do_cleanups (old_chain);
+      /* Set the current source location.  */
+      set_current_sal_from_frame (get_current_frame ());
+    }
 
   /* Look up the hook_stop and run it (CLI internally handles problem
      of stop_command's pre-hook not existing).  */
@@ -7867,82 +7971,16 @@ normal_stop (void)
     catch_errors (hook_stop_stub, stop_command,
                  "Error while running hook_stop:\n", RETURN_MASK_ALL);
 
-  if (!has_stack_frames ())
-    goto done;
-
-  if (last.kind == TARGET_WAITKIND_SIGNALLED
-      || last.kind == TARGET_WAITKIND_EXITED)
-    goto done;
-
-  /* Select innermost stack frame - i.e., current frame is frame 0,
-     and current location is based on that.
-     Don't do this on return from a stack dummy routine,
-     or if the program has exited.  */
-
-  if (!stop_stack_dummy)
-    {
-      select_frame (get_current_frame ());
-
-      /* If --batch-silent is enabled then there's no need to print the current
-        source location, and to try risks causing an error message about
-        missing source files.  */
-      if (stop_print_frame && !batch_silent)
-       print_stop_event (&last);
-    }
-
-  if (stop_stack_dummy == STOP_STACK_DUMMY)
-    {
-      /* Pop the empty frame that contains the stack dummy.
-        This also restores inferior state prior to the call
-        (struct infcall_suspend_state).  */
-      struct frame_info *frame = get_current_frame ();
-
-      gdb_assert (get_frame_type (frame) == DUMMY_FRAME);
-      frame_pop (frame);
-      /* frame_pop() calls reinit_frame_cache as the last thing it
-        does which means there's currently no selected frame.  We
-        don't need to re-establish a selected frame if the dummy call
-        returns normally, that will be done by
-        restore_infcall_control_state.  However, we do have to handle
-        the case where the dummy call is returning after being
-        stopped (e.g. the dummy call previously hit a breakpoint).
-        We can't know which case we have so just always re-establish
-        a selected frame here.  */
-      select_frame (get_current_frame ());
-    }
+  /* Notify observers about the stop.  This is where the interpreters
+     print the stop event.  */
+  if (!ptid_equal (inferior_ptid, null_ptid))
+    observer_notify_normal_stop (inferior_thread ()->control.stop_bpstat,
+                                stop_print_frame);
+  else
+    observer_notify_normal_stop (NULL, stop_print_frame);
 
-done:
   annotate_stopped ();
 
-  /* Suppress the stop observer if we're in the middle of:
-
-     - a step n (n > 1), as there still more steps to be done.
-
-     - a "finish" command, as the observer will be called in
-       finish_command_continuation, so it can include the inferior
-       function's return value.
-
-     - calling an inferior function, as we pretend we inferior didn't
-       run at all.  The return value of the call is handled by the
-       expression evaluator, through call_function_by_hand.  */
-
-  if (!target_has_execution
-      || last.kind == TARGET_WAITKIND_SIGNALLED
-      || last.kind == TARGET_WAITKIND_EXITED
-      || last.kind == TARGET_WAITKIND_NO_RESUMED
-      || (!(inferior_thread ()->step_multi
-           && inferior_thread ()->control.stop_step)
-         && !(inferior_thread ()->control.stop_bpstat
-              && inferior_thread ()->control.proceed_to_finish)
-         && !inferior_thread ()->control.in_infcall))
-    {
-      if (!ptid_equal (inferior_ptid, null_ptid))
-       observer_notify_normal_stop (inferior_thread ()->control.stop_bpstat,
-                                    stop_print_frame);
-      else
-       observer_notify_normal_stop (NULL, stop_print_frame);
-    }
-
   if (target_has_execution)
     {
       if (last.kind != TARGET_WAITKIND_SIGNALLED
@@ -8586,7 +8624,8 @@ struct infcall_control_state
 struct infcall_control_state *
 save_infcall_control_state (void)
 {
-  struct infcall_control_state *inf_status = xmalloc (sizeof (*inf_status));
+  struct infcall_control_state *inf_status =
+    XNEW (struct infcall_control_state);
   struct thread_info *tp = inferior_thread ();
   struct inferior *inf = current_inferior ();
 
@@ -8726,9 +8765,8 @@ restore_inferior_ptid (void *arg)
 struct cleanup *
 save_inferior_ptid (void)
 {
-  ptid_t *saved_ptid_ptr;
+  ptid_t *saved_ptid_ptr = XNEW (ptid_t);
 
-  saved_ptid_ptr = xmalloc (sizeof (ptid_t));
   *saved_ptid_ptr = inferior_ptid;
   return make_cleanup (restore_inferior_ptid, saved_ptid_ptr);
 }
@@ -8816,14 +8854,6 @@ static const struct internalvar_funcs siginfo_funcs =
 static void
 infrun_async_inferior_event_handler (gdb_client_data data)
 {
-  /* If the target is closed while this event source is marked, we
-     will reach here without execution, or a target to call
-     target_wait on, which is an error.  Instead of tracking whether
-     the target has been popped already, or whether we do have threads
-     with pending statutes, simply ignore the event.  */
-  if (!target_is_async_p ())
-    return;
-
   inferior_event_handler (INF_REG_EVENT, NULL);
 }
 
@@ -8914,15 +8944,11 @@ leave it stopped or free to run as needed."),
                           &showlist);
 
   numsigs = (int) GDB_SIGNAL_LAST;
-  signal_stop = (unsigned char *) xmalloc (sizeof (signal_stop[0]) * numsigs);
-  signal_print = (unsigned char *)
-    xmalloc (sizeof (signal_print[0]) * numsigs);
-  signal_program = (unsigned char *)
-    xmalloc (sizeof (signal_program[0]) * numsigs);
-  signal_catch = (unsigned char *)
-    xmalloc (sizeof (signal_catch[0]) * numsigs);
-  signal_pass = (unsigned char *)
-    xmalloc (sizeof (signal_pass[0]) * numsigs);
+  signal_stop = XNEWVEC (unsigned char, numsigs);
+  signal_print = XNEWVEC (unsigned char, numsigs);
+  signal_program = XNEWVEC (unsigned char, numsigs);
+  signal_catch = XNEWVEC (unsigned char, numsigs);
+  signal_pass = XNEWVEC (unsigned char, numsigs);
   for (i = 0; i < numsigs; i++)
     {
       signal_stop[i] = 1;
This page took 0.033683 seconds and 4 git commands to generate.