)]}'
{
  "commit": "6994a75776b3a40e9a7435be8e6c5518c18bd391",
  "tree": "39e8629bb5e00850db3360034ccdfbf044c5ef61",
  "parents": [
    "23948f56021f46bb2bdee7afad074aafe8329230"
  ],
  "author": {
    "name": "Pedro Alves",
    "email": "pedro@palves.net",
    "time": "Mon Jul 18 18:22:15 2022 +0100"
  },
  "committer": {
    "name": "Pedro Alves",
    "email": "pedro@palves.net",
    "time": "Mon Jul 18 19:54:54 2022 +0100"
  },
  "message": "Don\u0027t stop all threads prematurely after first step of \"step N\"\n\nIn all-stop mode, when the target is itself in non-stop mode (like\nGNU/Linux), if you use the \"step N\" (or \"stepi/next/nexti N\") to step\na thread a number of times:\n\n (gdb) help step\n step, s\n Step program until it reaches a different source line.\n Usage: step [N]\n Argument N means step N times (or till program stops for another reason).\n\n... GDB prematurely stops all threads after the first step, and\ndoesn\u0027t re-resume them for the subsequent N-1 steps.  It\u0027s as if for\nthe 2nd and subsequent steps, the command was running with\nscheduler-locking enabled.\n\nThis can be observed with the testcase added by this commit, which\nlooks like this:\n\n static pthread_barrier_t barrier;\n\n static void *\n thread_func (void *arg)\n {\n   pthread_barrier_wait (\u0026barrier);\n   return NULL;\n }\n\n int\n main ()\n {\n   pthread_t thread;\n   int ret;\n\n   pthread_barrier_init (\u0026barrier, NULL, 2);\n\n   /* We run to this line below, and then issue \"next 3\".  That should\n      step over the 3 lines below and land on the return statement.  If\n      GDB prematurely stops the thread_func thread after the first of\n      the 3 nexts (and never resumes it again), then the join won\u0027t\n      ever return.  */\n   pthread_create (\u0026thread, NULL, thread_func, NULL); /* set break here */\n   pthread_barrier_wait (\u0026barrier);\n   pthread_join (thread, NULL);\n\n   return 0;\n }\n\nThe test hangs and times out without the GDB fix:\n\n (gdb) next 3\n [New Thread 0x7ffff7d89700 (LWP 525772)]\n FAIL: gdb.threads/step-N-all-progress.exp: non-stop\u003doff: target-non-stop\u003don: next 3 (timeout)\n\nThe problem is a core gdb bug.\n\nWhen you do \"step/stepi/next/nexti N\", GDB internally creates a\nthread_fsm object and associates it with the stepping thread.  For the\nstepping commands, the FSM\u0027s class is step_command_fsm.  That object\nis what keeps track of how many steps are left to make.  When one step\nfinishes, handle_inferior_event calls stop_waiting and returns, and\nthen fetch_inferior_event calls the \"should_stop\" method of the event\nthread\u0027s FSM.  The implementation of that method decrements the\nsteps-left counter.  If the counter is 0, it returns true and we\nproceed to presenting the stop to the user.  If it isn\u0027t 0 yet, then\nthe method returns false, indicating to fetch_inferior_event to \"keep\ngoing\".\n\nFocusing now on when the first step finishes -- we\u0027re in \"all-stop\"\nmode, with the target in non-stop mode.  When a step finishes,\nhandle_inferior_event calls stop_waiting, which itself calls\nstop_all_threads to stop everything.  I.e., after the first step\ncompletes, all threads are stopped, before handle_inferior_event\nreturns.  And after that, now in fetch_inferior_event, we consult the\nthread\u0027s thread_fsm::should_stop, which as we\u0027ve seen, for the first\nstep returns false -- i.e., we need to keep_going for another step.\nHowever, since the target is in non-stop mode, keep_going resumes\n_only_ the current thread.  All the other threads remain stopped,\ninadvertently.\n\nIf the target is in non-stop mode, we don\u0027t actually need to stop all\nthreads right after each first step finishes, and then re-resume them\nagain.  We can instead defer stopping all threads until all the steps\nare completed.\n\nSo fix this by delaying the stopping of all threads until after we\ncalled the FSM\u0027s \"should_stop\" method.  I.e., move it from\nstop_waiting, to handle_inferior_events\u0027s callers,\nfetch_inferior_event and wait_for_inferior.\n\nNew test included.  Tested on x86-64 GNU/Linux native and gdbserver.\n\nChange-Id: Iaad50dcfea4464c84bdbac853a89df92ade6ae01\n",
  "tree_diff": [
    {
      "type": "modify",
      "old_id": "a6694230d2937c2a663fc1b8ba39f56545a0f666",
      "old_mode": 33188,
      "old_path": "gdb/infrun.c",
      "new_id": "a59cbe645374d40c1929d29fcb9169d49ebe66aa",
      "new_mode": 33188,
      "new_path": "gdb/infrun.c"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "26de9bcd3ed24639caad9c4793fa9bd2cb7fcb01",
      "new_mode": 33188,
      "new_path": "gdb/testsuite/gdb.threads/step-N-all-progress.c"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "b1d1ba75b67c40d2dc3bf12e0c94ef97ecc329f3",
      "new_mode": 33188,
      "new_path": "gdb/testsuite/gdb.threads/step-N-all-progress.exp"
    }
  ]
}
