)]}'
{
  "commit": "e6bdfed6f5d6d5338b552f8a07577a12d9b49279",
  "tree": "b224b2fc5680086944612803d21d1644ee098534",
  "parents": [
    "98c740fa46f95fb5f4cfb7880e23c596d7994908"
  ],
  "author": {
    "name": "Andrew Burgess",
    "email": "aburgess@redhat.com",
    "time": "Thu Jan 22 17:51:11 2026 +0000"
  },
  "committer": {
    "name": "Andrew Burgess",
    "email": "aburgess@redhat.com",
    "time": "Thu Mar 05 17:45:25 2026 +0000"
  },
  "message": "gdb: fix frame_unwind_caller_WHAT functions for inline and tail calls\n\nThe 3 frame_unwind_caller_WHAT functions:\n\n  + frame_unwind_caller_id\n  + frame_unwind_caller_pc\n  + frame_unwind_caller_arch\n\nAre, I believe, currently all broken with respect to inline and tail\ncall functions.\n\nThe Python FinishBreakpoint type creates a breakpoint in the caller\nfunction which, when triggered, indicates that the FinishBreakpoint\nhas gone out of scope.\n\nI was writing a test for the FinishBreakpoint type which included a\ntail call function, and the FinishBreakpoint was being created for the\ntail call function frame.  What I observed is that the out of scope\nbreakpoint was never being hit.\n\nThe call stack in my new test looked like this:\n\n  main -\u003e tailcall_function -\u003e normal_function\n\nI would stop in normal_function, and then create a FinishBreakpoint\nfor the parent (tailcall_function) frame.  The FinishBreakpoint\u0027s out\nof scope breakpoint was being correctly placed in the \u0027main\u0027 function,\nbut would never trigger.\n\nThe problem is that the breakpoint placed in \u0027main\u0027 holds a frame-id.\nThis frame-id is the frame in which the breakpoint should trigger.\nThis frame-id exists to prevent premature stops due to recursion.  But\nin this case, when the breakpoint in \u0027main\u0027 was hit, despite no\nrecursion having occurred, the frame-id didn\u0027t match, and so the\nbreakpoint was ignored.\n\nThe problem is that in bpfinishpy_init we call frame_unwind_caller_id\nto compute the frame-id of the frame in which we should stop, and\nframe_unwind_caller_id was returning the wrong frame-id.  As far as I\ncan tell frame_unwind_caller_id has been broken since it was updated\nfor inline functions in commit edb3359dff90ef8a.\n\nThe frame_unwind_caller_id function, and all the\nframe_unwind_caller_WHAT functions, are intended to return the\nprevious frame, but should skip over any inline, or tail call frames.\nLet\u0027s look at an example call stack:\n\n  #0 A\t\t// A normal function.\n  #1 B\t\t// An inline function.\n  #2 C\t\t// An inline function.\n  #3 D\t\t// A normal function.\n  #4 E\t\t// A normal function.\n\nStarting from #0, a normal function, frame_unwind_caller_id, should\nreturn the frame-id for #3, and this is what happens.\n\nBut if we start in #1 and call frame_unwind_caller_id, then we should\nstill return the frame-id for #3, but this is not what happens.\nInstead we return the frame-id for #4, skipping a frame.\n\nThe problem is that frame_unwind_caller_id starts by calling\nskip_artificial_frames, which calls get_prev_frame_always until we\nreach a non-inline (or non-tail call) frame, this moves us from #1 to\n\nThen, back in frame_unwind_caller_id we call get_prev_frame_always,\nwhich moves us to #4.\n\nThen frame_unwind_caller_id finishes with a call to\nskip_artificial_frames, this could potentially result in additional\nframes being skipped, but in my example above this isn\u0027t the case.\n\nThe problem here is that if skip_artificial_frames skips anything,\nthen we have already unwound to the caller frame, and the\nget_prev_frame_always call in frame_unwind_caller_id is unnecessary.\n\nI propose to add a new helper function frame_unwind_caller_frame,\nwhich should do the correct thing; it unwinds one frame and then calls\nskip_artificial_frames.  This should do exactly what is needed.\n\nThen all the frame_unwind_caller_WHAT functions will be updated to use\nthis helper function, and just extract the required property from the\nresulting frame.\n\nWith this fix in place I could then write the FinishBreakpoint test,\nwhich now works.\n\nI took a look for other places where frame_unwind_caller_id is used\nand spotted that the \u0027until\u0027 command does much the same thing, placing\na breakpoint in the caller frame.  As predicted, the \u0027until\u0027 command\nis also broken when used within a tail call frame.  This patch fixes\nthat issue too.  There\u0027s also a test for the until command.\n\nThe bug PR gdb/28683 seems to describe this exact problem with a\nspecific AArch64 case given.  I haven\u0027t actually setup the environment\nneeded to test this bug, but I\u0027m reasonably sure that this patch will\nfix the bug.  Even if it doesn\u0027t then it\u0027s certainly related and worth\nlinking into the bug report.\n\nBug: https://sourceware.org/bugzilla/show_bug.cgi?id\u003d28683\n\nApproved-By: Tom Tromey \u003ctom@tromey.com\u003e\n",
  "tree_diff": [
    {
      "type": "modify",
      "old_id": "5509e5ab7d7338092ab6fd2ec7c88cbd58c44f44",
      "old_mode": 33188,
      "old_path": "gdb/frame.c",
      "new_id": "14d43f93e7fc086a5dd829a401abad983d5f5434",
      "new_mode": 33188,
      "new_path": "gdb/frame.c"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "3e3853ba6dffadf16c03bdadf8f5e82e313e3f5a",
      "new_mode": 33188,
      "new_path": "gdb/testsuite/gdb.base/until-in-tailcall.c"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "cd66d0ceaf8c8cd7e36a8bb93539798c21aed6c0",
      "new_mode": 33188,
      "new_path": "gdb/testsuite/gdb.base/until-in-tailcall.exp"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "d129d7e41b4aad0f36e8225e4d1f0c9d1739dfc6",
      "new_mode": 33188,
      "new_path": "gdb/testsuite/gdb.python/py-finish-breakpoint-tailcall.c"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "e5b5ebc5183e090a7fdff630ed6e7f0217775dd7",
      "new_mode": 33188,
      "new_path": "gdb/testsuite/gdb.python/py-finish-breakpoint-tailcall.exp"
    },
    {
      "type": "add",
      "old_id": "0000000000000000000000000000000000000000",
      "old_mode": 0,
      "old_path": "/dev/null",
      "new_id": "bd10109a3dc81a353128b52e7aabb4f7349d155b",
      "new_mode": 33188,
      "new_path": "gdb/testsuite/gdb.python/py-finish-breakpoint-tailcall.py"
    }
  ]
}
