)]}'
{
  "commit": "3df7843699ff3610f89ac880685396b531d8ec1b",
  "tree": "d516d794bbee2ee18e3f8946da1de45884efbf6e",
  "parents": [
    "07505b613a0605ef42a004364ab8ccb70fd3c8eb"
  ],
  "author": {
    "name": "Andrew Burgess",
    "email": "aburgess@redhat.com",
    "time": "Fri Oct 09 13:27:13 2020 +0200"
  },
  "committer": {
    "name": "Andrew Burgess",
    "email": "aburgess@redhat.com",
    "time": "Mon Mar 25 17:25:06 2024 +0000"
  },
  "message": "gdb: fix b/p conditions with infcalls in multi-threaded inferiors\n\nThis commit fixes bug PR 28942, that is, creating a conditional\nbreakpoint in a multi-threaded inferior, where the breakpoint\ncondition includes an inferior function call.\n\nCurrently, when a user tries to create such a breakpoint, then GDB\nwill fail with:\n\n  (gdb) break infcall-from-bp-cond-single.c:61 if (return_true ())\n  Breakpoint 2 at 0x4011fa: file /tmp/build/gdb/testsuite/../../../src/gdb/testsuite/gdb.threads/infcall-from-bp-cond-single.c, line 61.\n  (gdb) continue\n  Continuing.\n  [New Thread 0x7ffff7c5d700 (LWP 2460150)]\n  [New Thread 0x7ffff745c700 (LWP 2460151)]\n  [New Thread 0x7ffff6c5b700 (LWP 2460152)]\n  [New Thread 0x7ffff645a700 (LWP 2460153)]\n  [New Thread 0x7ffff5c59700 (LWP 2460154)]\n  Error in testing breakpoint condition:\n  Couldn\u0027t get registers: No such process.\n  An error occurred while in a function called from GDB.\n  Evaluation of the expression containing the function\n  (return_true) will be abandoned.\n  When the function is done executing, GDB will silently stop.\n  Selected thread is running.\n  (gdb)\n\nOr, in some cases, like this:\n\n  (gdb) break infcall-from-bp-cond-simple.c:56 if (is_matching_tid (arg, 1))\n  Breakpoint 2 at 0x401194: file /tmp/build/gdb/testsuite/../../../src/gdb/testsuite/gdb.threads/infcall-from-bp-cond-simple.c, line 56.\n  (gdb) continue\n  Continuing.\n  [New Thread 0x7ffff7c5d700 (LWP 2461106)]\n  [New Thread 0x7ffff745c700 (LWP 2461107)]\n  ../../src.release/gdb/nat/x86-linux-dregs.c:146: internal-error: x86_linux_update_debug_registers: Assertion `lwp_is_stopped (lwp)\u0027 failed.\n  A problem internal to GDB has been detected,\n  further debugging may prove unreliable.\n\nThe precise error depends on the exact thread state; so there\u0027s race\nconditions depending on which threads have fully started, and which\nhave not.  But the underlying problem is always the same; when GDB\ntries to execute the inferior function call from within the breakpoint\ncondition, GDB will, incorrectly, try to resume threads that are\nalready running - GDB doesn\u0027t realise that some threads might already\nbe running.\n\nThe solution proposed in this patch requires an additional member\nvariable thread_info::in_cond_eval.  This flag is set to true (in\nbreakpoint.c) when GDB is evaluating a breakpoint condition.\n\nIn user_visible_resume_ptid (infrun.c), when the in_cond_eval flag is\ntrue, then GDB will only try to resume the current thread, that is,\nthe thread for which the breakpoint condition is being evaluated.\nThis solves the problem of GDB trying to resume threads that are\nalready running.\n\nThe next problem is that inferior function calls are assumed to be\nsynchronous, that is, GDB doesn\u0027t expect to start an inferior function\ncall in thread #1, then receive a stop from thread #2 for some other,\nunrelated reason.  To prevent GDB responding to an event from another\nthread, we update fetch_inferior_event and do_target_wait in infrun.c,\nso that, when an inferior function call (on behalf of a breakpoint\ncondition) is in progress, we only wait for events from the current\nthread (the one evaluating the condition).\n\nIn do_target_wait I had to change the inferior_matches lambda\nfunction, which is used to select which inferior to wait on.\nPreviously the logic was this:\n\n   auto inferior_matches \u003d [\u0026wait_ptid] (inferior *inf)\n     {\n       return (inf-\u003eprocess_target () !\u003d nullptr\n               \u0026\u0026 ptid_t (inf-\u003epid).matches (wait_ptid));\n     };\n\nThis compares the pid of the inferior against the complete ptid we\nwant to wait on.  Before this commit wait_ptid was only ever\nminus_one_ptid (which is special, and means any process), and so every\ninferior would match.\n\nAfter this commit though wait_ptid might represent a specific thread\nin a specific inferior.  If we compare the pid of the inferior to a\nspecific ptid then these will not match.  The fix is to compare\nagainst the pid extracted from the wait_ptid, not against the complete\nwait_ptid itself.\n\nIn fetch_inferior_event, after receiving the event, we only want to\nstop all the other threads, and call inferior_event_handler with\nINF_EXEC_COMPLETE, if we are not evaluating a conditional breakpoint.\nIf we are, then all the other threads should be left doing whatever\nthey were before.  The inferior_event_handler call will be performed\nonce the breakpoint condition has finished being evaluated, and GDB\ndecides to stop or not.\n\nThe final problem that needs solving relates to GDB\u0027s commit-resume\nmechanism, which allows GDB to collect resume requests into a single\npacket in order to reduce traffic to a remote target.\n\nThe problem is that the commit-resume mechanism will not send any\nresume requests for an inferior if there are already events pending on\nthe GDB side.\n\nImagine an inferior with two threads.  Both threads hit a breakpoint,\nmaybe the same conditional breakpoint.  At this point there are two\npending events, one for each thread.\n\nGDB selects one of the events and spots that this is a conditional\nbreakpoint, GDB evaluates the condition.\n\nThe condition includes an inferior function call, so GDB sets up for\nthe call and resumes the one thread, the resume request is added to\nthe commit-resume queue.\n\nWhen the commit-resume queue is committed GDB sees that there is a\npending event from another thread, and so doesn\u0027t send any resume\nrequests to the actual target, GDB is assuming that when we wait we\nwill select the event from the other thread.\n\nHowever, as this is an inferior function call for a condition\nevaluation, we will not select the event from the other thread, we\nonly care about events from the thread that is evaluating the\ncondition - and the resume for this thread was never sent to the\ntarget.\n\nAnd so, GDB hangs, waiting for an event from a thread that was never\nfully resumed.\n\nTo fix this issue I have added the concept of \"forcing\" the\ncommit-resume queue.  When enabling commit resume, if the force flag\nis true, then any resumes will be committed to the target, even if\nthere are other threads with pending events.\n\nA note on authorship: this patch was based on some work done by\nNatalia Saiapova and Tankut Baris Aktemur from Intel[1].  I have made\nsome changes to their work in this version.\n\nBug: https://sourceware.org/bugzilla/show_bug.cgi?id\u003d28942\n\n[1] https://sourceware.org/pipermail/gdb-patches/2020-October/172454.html\n\nCo-authored-by: Natalia Saiapova \u003cnatalia.saiapova@intel.com\u003e\nCo-authored-by: Tankut Baris Aktemur \u003ctankut.baris.aktemur@intel.com\u003e\nReviewed-By: Tankut Baris Aktemur \u003ctankut.baris.aktemur@intel.com\u003e\nTested-By: Luis Machado \u003cluis.machado@arm.com\u003e\nTested-By: Keith Seitz \u003ckeiths@redhat.com\u003e\n",
  "tree_diff": [
    {
      "type": "modify",
      "old_id": "102bd7fad419ebf89dca94a2a0e26694e13cee11",
      "old_mode": 33188,
      "old_path": "gdb/breakpoint.c",
      "new_id": "39c13107ba2672e6f0d43a905682d0f497021918",
      "new_mode": 33188,
      "new_path": "gdb/breakpoint.c"
    },
    {
      "type": "modify",
      "old_id": "11c553a99ca14b6e8dff922a1cf0d9a4acd4368e",
      "old_mode": 33188,
      "old_path": "gdb/gdbthread.h",
      "new_id": "73f6895fe467f07c7c7af6e52c87af0b483dc9f1",
      "new_mode": 33188,
      "new_path": "gdb/gdbthread.h"
    },
    {
      "type": "modify",
      "old_id": "145ce25b0d1beb22b5354b8de5e09988e5bc07de",
      "old_mode": 33188,
      "old_path": "gdb/infcall.c",
      "new_id": "bfcac20e3f57c918fafaf1cd1a2a857c48abc8fc",
      "new_mode": 33188,
      "new_path": "gdb/infcall.c"
    },
    {
      "type": "modify",
      "old_id": "e0e0ba35a68d7a216a704cd6fa592f09bb35b17a",
      "old_mode": 33188,
      "old_path": "gdb/infrun.c",
      "new_id": "b06972bb96872a27087c94c29906aaada96902e3",
      "new_mode": 33188,
      "new_path": "gdb/infrun.c"
    },
    {
      "type": "modify",
      "old_id": "6339fd997e15d57610208c7e3091fb9322104fd3",
      "old_mode": 33188,
      "old_path": "gdb/infrun.h",
      "new_id": "5f83ca2b4c3c1422b28278d8cb0af8d441707d2a",
      "new_mode": 33188,
      "new_path": "gdb/infrun.h"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "38a5bc284dfb9e44d8cc3bbe100eef533ea21740",
      "new_mode": 33188,
      "new_path": "gdb/testsuite/gdb.threads/infcall-from-bp-cond-other-thread-event.c"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "b5c0d84afbfaf48176ec64b1f1a0d84c02db091f",
      "new_mode": 33188,
      "new_path": "gdb/testsuite/gdb.threads/infcall-from-bp-cond-other-thread-event.exp"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "2e23f1229c599f9c4f3faabb346996130ee9103a",
      "new_mode": 33188,
      "new_path": "gdb/testsuite/gdb.threads/infcall-from-bp-cond-simple.c"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "d6069ebaa584fb0edf1f315c5a2b3c32df42bb9b",
      "new_mode": 33188,
      "new_path": "gdb/testsuite/gdb.threads/infcall-from-bp-cond-simple.exp"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "0b045f86fad44dd36ddff2a721e48ed32f8b669d",
      "new_mode": 33188,
      "new_path": "gdb/testsuite/gdb.threads/infcall-from-bp-cond-single.c"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "2c3623ec3a5b9f190a9972ed21c6b85534b3c62c",
      "new_mode": 33188,
      "new_path": "gdb/testsuite/gdb.threads/infcall-from-bp-cond-single.exp"
    }
  ]
}
