Bug 22748 - crash from custom unwinder
Summary: crash from custom unwinder
Status: RESOLVED FIXED
Alias: None
Product: gdb
Classification: Unclassified
Component: python (show other bugs)
Version: 8.0.1
: P2 normal
Target Milestone: ---
Assignee: Not yet assigned to anyone
URL:
Keywords:
Depends on:
Blocks:
 
Reported: 2018-01-26 03:28 UTC by Tom Tromey
Modified: 2022-06-15 15:04 UTC (History)
6 users (show)

See Also:
Host:
Target:
Build:
Last reconfirmed:


Attachments
Fix for fi->level assert (406 bytes, patch)
2020-01-14 17:03 UTC, Mark
Details | Diff
c++ file to help repro the bug (71 bytes, text/plain)
2020-01-14 17:04 UTC, Mark
Details
python unwinder to help repro the bug (165 bytes, text/plain)
2020-01-14 17:04 UTC, Mark
Details

Note You need to log in before you can comment on or make changes to this bug.
Description Tom Tromey 2018-01-26 03:28:15 UTC
I wrote a minimal unwinder for libjit.  You can see it here:
http://lists.gnu.org/archive/html/libjit/2018-01/msg00013.html

With an unmodified gdb, this unwinder causes a crash when it
asks for the register "rip" in the pending frame.  See the
(partial) stack trace below.

Reverting this patch:

commit 33cc7d368f420326606695daafd6292e2779c6af
Author: Kevin Buettner <kevinb@redhat.com>
Date:   Tue Sep 27 22:45:19 2016 -0700

... fixes the problem for me.
That is, no more crash, and my unwinder works.

The assert in question is this one:

  gdb_assert (frame_id_p (get_frame_id (next_frame)));

in value_of_register_lazy.


(top-gdb) bt
#0  0x00000000005e0d00 in internal_error(char const*, int, char const*, ...) (file=0xd76098 "../../binutils-gdb/gdb/frame.c", line=534, fmt=0xca2d0f "%s: Assertion `%s' failed.") at ../../binutils-gdb/gdb/common/errors.c:51
#1  0x00000000006b5477 in get_frame_id(frame_info*) (fi=0x2acd4a0) at ../../binutils-gdb/gdb/frame.c:534
#2  0x00000000006b34c6 in value_of_register_lazy(frame_info*, int) (frame=0x2acd560, regnum=16) at ../../binutils-gdb/gdb/findvar.c:298
#3  0x00000000006b360f in value_of_register(int, frame_info*) (regnum=16, frame=0x2acd560) at ../../binutils-gdb/gdb/findvar.c:274
#4  0x0000000000593ff4 in pending_framepy_read_register(PyObject*, PyObject*) (self=0x7ffff7f5e290, args=<optimized out>) at ../../binutils-gdb/gdb/python/py-unwind.c:402
#5  0x00007ffff668979e in PyEval_EvalFrameEx () at /lib64/libpython2.7.so.1.0
#6  0x00007ffff668a3f8 in PyEval_EvalCodeEx () at /lib64/libpython2.7.so.1.0
#7  0x00007ffff65d97ee in function_call.lto_priv () at /lib64/libpython2.7.so.1.0
#8  0x00007ffff65a2a53 in PyObject_Call () at /lib64/libpython2.7.so.1.0
#9  0x00007ffff65ce88e in instancemethod_call.lto_priv () at /lib64/libpython2.7.so.1.0
#10 0x00007ffff65a2a53 in PyObject_Call () at /lib64/libpython2.7.so.1.0
#11 0x00007ffff665ca2b in slot_tp_call () at /lib64/libpython2.7.so.1.0
#12 0x00007ffff65a2a53 in PyObject_Call () at /lib64/libpython2.7.so.1.0
#13 0x00007ffff6685454 in PyEval_EvalFrameEx () at /lib64/libpython2.7.so.1.0
#14 0x00007ffff668a3f8 in PyEval_EvalCodeEx () at /lib64/libpython2.7.so.1.0
#15 0x00007ffff65d97ee in function_call.lto_priv () at /lib64/libpython2.7.so.1.0
#16 0x00007ffff65a2a53 in PyObject_Call () at /lib64/libpython2.7.so.1.0
#17 0x00007ffff65a4e5f in PyObject_CallFunctionObjArgs () at /lib64/libpython2.7.so.1.0
#18 0x0000000000593940 in pyuw_sniffer(frame_unwind const*, frame_info*, void**) (self=<optimized out>, this_frame=0x2acd560, cache_ptr=0x2acd578) at ../../binutils-gdb/gdb/python/py-unwind.c:539
#19 0x00000000006b915d in frame_unwind_try_unwinder(frame_info*, void**, frame_unwind const*) (this_frame=0x2acd560, this_cache=0x2acd578, unwinder=0x34294b0) at ../../binutils-gdb/gdb/frame-unwind.c:106
#20 0x00000000006b951f in frame_unwind_find_by_frame(frame_info*, void**) (this_frame=0x2acd560, this_cache=0x2acd578) at ../../binutils-gdb/gdb/frame-unwind.c:164
Comment 1 Tom Tromey 2018-01-26 16:43:47 UTC
Changing py-unwind.py (in the test suite) did not reproduce the failure,
so more investigation needs to be done.
Comment 2 Emiliano Testa 2018-07-26 09:47:31 UTC
I am having the same issue: using GDB-8.1, installed a python unwinder I created and I get either an assertion failure or a crash (depending on the value of 'show debug frame'): with 1 I get a crash, with 0 I get the assertion failure.

The root cause seems to be (when debug frame is 1):


static void
pyuw_this_id (struct frame_info *this_frame, void **cache_ptr,
              struct frame_id *this_id)
{
  *this_id = ((cached_frame_info *) *cache_ptr)->frame_id;
  if (pyuw_debug >= 1)
    {
      fprintf_unfiltered (gdb_stdlog, "%s: frame_id: ", __FUNCTION__);
      fprint_frame_id (gdb_stdlog, *this_id);
      fprintf_unfiltered (gdb_stdlog, "\n");
    }
}

cache_ptr is NULL hence the crash.

The backtrace for the above:

(gdb) bt
#0  0x00000000006d6723 in pyuw_this_id (this_frame=0x5650d80, cache_ptr=0x5650d98, this_id=0x5650de0) at ../../gdb-8.1/gdb/python/py-unwind.c:461
#1  0x00000000007e012c in compute_frame_id (fi=fi@entry=0x5650d80) at ../../gdb-8.1/gdb/frame.c:505
#2  0x00000000007e026a in get_frame_id (fi=0x5650d80) at ../../gdb-8.1/gdb/frame.c:537
#3  0x00000000007e3535 in get_prev_frame_id_by_id (id=...) at ../../gdb-8.1/gdb/frame.c:2368
#4  0x00000000008ffbe4 in value_fetch_lazy (val=val@entry=0x59550f0) at ../../gdb-8.1/gdb/value.c:3968
#5  0x00000000007de439 in value_of_register (regnum=16, frame=<optimised out>) at ../../gdb-8.1/gdb/findvar.c:275
#6  0x00000000006d64f8 in pending_framepy_read_register (self=0x57d04f0, args=<optimised out>) at ../../gdb-8.1/gdb/python/py-unwind.c:402
#7  0x0000000000ea4828 in call_function (oparg=<optimised out>, pp_stack=0x7fffa1325fe8) at ../Python-2.7.3/Python/ceval.c:4021
#8  PyEval_EvalFrameEx (f=f@entry=0x594f8d0, throwflag=throwflag@entry=0) at ../Python-2.7.3/Python/ceval.c:2666
#9  0x0000000000ea43e5 in fast_function (nk=0, na=<optimised out>, n=2, pp_stack=0x7fffa13260e8, func=0x58dd410) at ../Python-2.7.3/Python/ceval.c:4107
#10 call_function (oparg=<optimised out>, pp_stack=0x7fffa13260e8) at ../Python-2.7.3/Python/ceval.c:4042
#11 PyEval_EvalFrameEx (f=f@entry=0x5970750, throwflag=throwflag@entry=0) at ../Python-2.7.3/Python/ceval.c:2666
#12 0x0000000000ea5bbc in PyEval_EvalCodeEx (co=<optimised out>, globals=<optimised out>, locals=locals@entry=0x0, args=args@entry=0x57f6c38, argcount=<optimised out>, kws=kws@entry=0x0, kwcount=0, defs=0x0, defcount=0, closure=0x0)
    at ../Python-2.7.3/Python/ceval.c:3253
#13 0x0000000000f14b0c in function_call (func=0x58dd398, arg=0x57f6c20, kw=0x0) at ../Python-2.7.3/Objects/funcobject.c:526
#14 0x0000000000e1a37a in PyObject_Call (func=func@entry=0x58dd398, arg=arg@entry=0x57f6c20, kw=kw@entry=0x0) at ../Python-2.7.3/Objects/abstract.c:2529
#15 0x0000000000e2321f in instancemethod_call (func=0x58dd398, arg=0x57f6c20, kw=0x0) at ../Python-2.7.3/Objects/classobject.c:2578
#16 0x0000000000e1a37a in PyObject_Call (func=func@entry=0x593ed20, arg=arg@entry=0x67a4dd0, kw=kw@entry=0x0) at ../Python-2.7.3/Objects/abstract.c:2529
#17 0x0000000000e77935 in slot_tp_call (self=<optimised out>, args=0x67a4dd0, kwds=0x0) at ../Python-2.7.3/Objects/typeobject.c:5403
#18 0x0000000000e1a37a in PyObject_Call (func=func@entry=0x57d60d0, arg=arg@entry=0x67a4dd0, kw=kw@entry=0x0) at ../Python-2.7.3/Objects/abstract.c:2529
#19 0x0000000000ea27e5 in do_call (nk=0, na=<optimised out>, pp_stack=0x7fffa1326618, func=0x57d60d0) at ../Python-2.7.3/Python/ceval.c:4239
#20 call_function (oparg=<optimised out>, pp_stack=0x7fffa1326618) at ../Python-2.7.3/Python/ceval.c:4044
#21 PyEval_EvalFrameEx (f=f@entry=0x44af150, throwflag=throwflag@entry=0) at ../Python-2.7.3/Python/ceval.c:2666
#22 0x0000000000ea5bbc in PyEval_EvalCodeEx (co=<optimised out>, globals=<optimised out>, locals=locals@entry=0x0, args=args@entry=0x37d67e8, argcount=<optimised out>, kws=kws@entry=0x0, kwcount=0, defs=0x0, defcount=0, closure=0x0)
    at ../Python-2.7.3/Python/ceval.c:3253
#23 0x0000000000f14b0c in function_call (func=0x3773b90, arg=0x37d67d0, kw=0x0) at ../Python-2.7.3/Objects/funcobject.c:526
#24 0x0000000000e1afe5 in PyObject_Call (kw=0x0, arg=0x37d67d0, func=0x3773b90) at ../Python-2.7.3/Objects/abstract.c:2529
#25 PyObject_CallFunctionObjArgs (callable=0x3773b90) at ../Python-2.7.3/Objects/abstract.c:2760
#26 0x00000000006d7256 in pyuw_sniffer (self=<optimised out>, this_frame=0x5650d80, cache_ptr=0x5650d98) at ../../gdb-8.1/gdb/python/py-unwind.c:539
#27 0x00000000007e404d in frame_unwind_try_unwinder (this_frame=this_frame@entry=0x5650d80, this_cache=this_cache@entry=0x5650d98, unwinder=0x39aca00) at ../../gdb-8.1/gdb/frame-unwind.c:106
#28 0x00000000007e4427 in frame_unwind_find_by_frame (this_frame=this_frame@entry=0x5650d80, this_cache=this_cache@entry=0x5650d98) at ../../gdb-8.1/gdb/frame-unwind.c:164
#29 0x00000000007e01e3 in compute_frame_id (fi=fi@entry=0x5650d80) at ../../gdb-8.1/gdb/frame.c:501
#30 0x00000000007e026a in get_frame_id (fi=fi@entry=0x5650d80) at ../../gdb-8.1/gdb/frame.c:537
#31 0x00000000008ce65b in scoped_restore_current_thread::scoped_restore_current_thread (this=0x7fffa1326b10) at ../../gdb-8.1/gdb/thread.c:1573
#32 0x00000000008cf564 in print_thread_info_1 (uiout=0x391ff10, requested_threads=requested_threads@entry=0x0, global_ids=global_ids@entry=0, pid=pid@entry=-1, show_global_ids=show_global_ids@entry=0, this=<optimised out>, 
    this=<optimised out>) at ../../gdb-8.1/gdb/thread.c:1250
#33 0x00000000008d0049 in info_threads_command (arg=0x0, from_tty=<optimised out>) at ../../gdb-8.1/gdb/thread.c:1390
#34 0x000000000069db89 in cmd_func (cmd=<optimised out>, args=<optimised out>, from_tty=<optimised out>) at ../../gdb-8.1/gdb/cli/cli-decode.c:1886
#35 0x00000000008d26b1 in execute_command (p=<optimised out>, p@entry=0x3591c20 "info threads ", from_tty=1) at ../../gdb-8.1/gdb/top.c:637
#36 0x00000000007d7a9c in command_handler (command=0x3591c20 "info threads ") at ../../gdb-8.1/gdb/event-top.c:583
#37 0x00000000007d7da8 in command_line_handler (rl=<optimised out>) at ../../gdb-8.1/gdb/event-top.c:774

With debug frame 0 I get an assertion failure:

gdb_assert (fi->level == 0);

inside get_frame_id()

and the backtrace:

(gdb) bt
#0  0x00007f7aea353428 in __GI_raise (sig=sig@entry=6) at ../sysdeps/unix/sysv/linux/raise.c:54
#1  0x00007f7aea35502a in __GI_abort () at abort.c:89
#2  0x00000000008e9086 in dump_core () at ../../gdb-8.1/gdb/utils.c:284
#3  0x00000000008ed1dd in internal_vproblem(internal_problem *, const char *, int, const char *, typedef __va_list_tag __va_list_tag *) (problem=problem@entry=0x20ff340 <internal_error_problem>, file=<optimised out>, 
    line=<optimised out>, fmt=<optimised out>, ap=ap@entry=0x7ffff3d626b8) at ../../gdb-8.1/gdb/utils.c:493
#4  0x00000000008ed2e9 in internal_verror (file=<optimised out>, line=<optimised out>, fmt=<optimised out>, 
    ap=ap@entry=0x7ffff3d626b8) at ../../gdb-8.1/gdb/utils.c:518
#5  0x000000000070f5f2 in internal_error (file=file@entry=0x10a67d7 "../../gdb-8.1/gdb/frame.c", line=line@entry=534, 
    fmt=<optimised out>) at ../../gdb-8.1/gdb/common/errors.c:55
#6  0x00000000007e031b in get_frame_id (fi=0x4a88fa0) at ../../gdb-8.1/gdb/frame.c:534
#7  0x0000000000816dec in inline_frame_this_id (this_frame=0x4a88ee0, this_cache=<optimised out>, this_id=0x4a88f40)
    at ../../gdb-8.1/gdb/inline-frame.c:160
#8  0x00000000007e012c in compute_frame_id (fi=fi@entry=0x4a88ee0) at ../../gdb-8.1/gdb/frame.c:505
#9  0x00000000007e026a in get_frame_id (fi=fi@entry=0x4a88ee0) at ../../gdb-8.1/gdb/frame.c:537
#10 0x00000000007de2d8 in value_of_register_lazy (frame=frame@entry=0x4a88fa0, regnum=regnum@entry=16)
    at ../../gdb-8.1/gdb/findvar.c:298
#11 0x00000000007de42e in value_of_register (regnum=16, frame=0x4a88fa0) at ../../gdb-8.1/gdb/findvar.c:274
#12 0x00000000006d64f8 in pending_framepy_read_register (self=0x59494f0, args=<optimised out>)
    at ../../gdb-8.1/gdb/python/py-unwind.c:402
#13 0x0000000000ea4828 in call_function (oparg=<optimised out>, pp_stack=0x7ffff3d629b8)
    at ../Python-2.7.3/Python/ceval.c:4021
#14 PyEval_EvalFrameEx (f=f@entry=0x4a905e0, throwflag=throwflag@entry=0) at ../Python-2.7.3/Python/ceval.c:2666
#15 0x0000000000ea43e5 in fast_function (nk=0, na=<optimised out>, n=2, pp_stack=0x7ffff3d62ab8, func=0x4c19410)
    at ../Python-2.7.3/Python/ceval.c:4107
#16 call_function (oparg=<optimised out>, pp_stack=0x7ffff3d62ab8) at ../Python-2.7.3/Python/ceval.c:4042
#17 PyEval_EvalFrameEx (f=f@entry=0x3c0ef90, throwflag=throwflag@entry=0) at ../Python-2.7.3/Python/ceval.c:2666
#18 0x0000000000ea5bbc in PyEval_EvalCodeEx (co=<optimised out>, globals=<optimised out>, locals=locals@entry=0x0, 
    args=args@entry=0x596fc80, argcount=<optimised out>, kws=kws@entry=0x0, kwcount=0, defs=0x0, defcount=0, 
    closure=0x0) at ../Python-2.7.3/Python/ceval.c:3253
#19 0x0000000000f14b0c in function_call (func=0x4c19398, arg=0x596fc68, kw=0x0)
---Type <return> to continue, or q <return> to quit---
   :526
#20 0x0000000000e1a37a in PyObject_Call (func=func@entry=0x4c19398, arg=arg@entry=0x596fc68, kw=kw@entry=0x0) at ../Python-2.7.3/Objects/abstract.c:2529
#21 0x0000000000e2321f in instancemethod_call (func=0x4c19398, arg=0x596fc68, kw=0x0) at ../Python-2.7.3/Objects/classobject.c:2578
#22 0x0000000000e1a37a in PyObject_Call (func=func@entry=0x4d77d20, arg=arg@entry=0x5bdfe50, kw=kw@entry=0x0) at ../Python-2.7.3/Objects/abstract.c:2529
#23 0x0000000000e77935 in slot_tp_call (self=<optimised out>, args=0x5bdfe50, kwds=0x0) at ../Python-2.7.3/Objects/typeobject.c:5403
#24 0x0000000000e1a37a in PyObject_Call (func=func@entry=0x5951150, arg=arg@entry=0x5bdfe50, kw=kw@entry=0x0) at ../Python-2.7.3/Objects/abstract.c:2529
#25 0x0000000000ea27e5 in do_call (nk=0, na=<optimised out>, pp_stack=0x7ffff3d62fe8, func=0x5951150) at ../Python-2.7.3/Python/ceval.c:4239
#26 call_function (oparg=<optimised out>, pp_stack=0x7ffff3d62fe8) at ../Python-2.7.3/Python/ceval.c:4044
#27 PyEval_EvalFrameEx (f=f@entry=0x3a9d380, throwflag=throwflag@entry=0) at ../Python-2.7.3/Python/ceval.c:2666
#28 0x0000000000ea5bbc in PyEval_EvalCodeEx (co=<optimised out>, globals=<optimised out>, locals=locals@entry=0x0, args=args@entry=0x2c067e8, argcount=<optimised out>, kws=kws@entry=0x0, kwcount=0, defs=0x0, defcount=0, closure=0x0)
    at ../Python-2.7.3/Python/ceval.c:3253
#29 0x0000000000f14b0c in function_call (func=0x2ba3b90, arg=0x2c067d0, kw=0x0) at ../Python-2.7.3/Objects/funcobject.c:526
#30 0x0000000000e1afe5 in PyObject_Call (kw=0x0, arg=0x2c067d0, func=0x2ba3b90) at ../Python-2.7.3/Objects/abstract.c:2529
#31 PyObject_CallFunctionObjArgs (callable=0x2ba3b90) at ../Python-2.7.3/Objects/abstract.c:2760
#32 0x00000000006d7256 in pyuw_sniffer (self=<optimised out>, this_frame=0x4a88fa0, cache_ptr=0x4a88fb8) at ../../gdb-8.1/gdb/python/py-unwind.c:539
#33 0x00000000007e404d in frame_unwind_try_unwinder (this_frame=this_frame@entry=0x4a88fa0, this_cache=this_cache@entry=0x4a88fb8, unwinder=0x2de4ae0) at ../../gdb-8.1/gdb/frame-unwind.c:106
#34 0x00000000007e4427 in frame_unwind_find_by_frame (this_frame=this_frame@entry=0x4a88fa0, this_cache=this_cache@entry=0x4a88fb8) at ../../gdb-8.1/gdb/frame-unwind.c:164
#35 0x00000000007e01e3 in compute_frame_id (fi=fi@entry=0x4a88fa0) at ../../gdb-8.1/gdb/frame.c:501
#36 0x00000000007e0738 in get_prev_frame_if_no_cycle (this_frame=this_frame@entry=0x4a88ee0) at ../../gdb-8.1/gdb/frame.c:1913
#37 0x00000000007e2757 in get_prev_frame_always_1 (this_frame=this_frame@entry=0x4a88ee0) at ../../gdb-8.1/gdb/frame.c:2087
#38 0x00000000007e2ccd in get_prev_frame_always (this_frame=this_frame@entry=0x4a88ee0) at ../../gdb-8.1/gdb/frame.c:2103
#39 0x0000000000816de1 in inline_frame_this_id (this_frame=0x4a88ee0, this_cache=<optimised out>, this_id=0x4a88f40) at ../../gdb-8.1/gdb/inline-frame.c:160
#40 0x00000000007e012c in compute_frame_id (fi=fi@entry=0x4a88ee0) at ../../gdb-8.1/gdb/frame.c:505
#41 0x00000000007e026a in get_frame_id (fi=fi@entry=0x4a88ee0) at ../../gdb-8.1/gdb/frame.c:537
#42 0x00000000007de9f5 in value_from_register (type=type@entry=0x5acaee0, regnum=regnum@entry=6, frame=frame@entry=0x4a88ee0) at ../../gdb-8.1/gdb/findvar.c:938
#43 0x00000000007979fd in dwarf2_evaluate_loc_desc_full (type=0x5acaee0, frame=frame@entry=0x4a88ee0, data=<optimised out>, size=<optimised out>, per_cu=<optimised out>, subobj_type=0x5acaee0, subobj_type@entry=0x0, subobj_byte_offset=0)
    at ../../gdb-8.1/gdb/dwarf2loc.c:2418
#44 0x00000000007982d3 in dwarf2_evaluate_loc_desc (per_cu=<optimised out>, size=<optimised out>, data=<optimised out>, frame=0x4a88ee0, type=<optimised out>) at ../../gdb-8.1/gdb/dwarf2loc.c:2544
#45 loclist_read_variable (symbol=0x5acb140, frame=0x4a88ee0) at ../../gdb-8.1/gdb/dwarf2loc.c:4456
#46 0x00000000007debe7 in default_read_var_value (var=0x5acb140, var_block=0x0, frame=0x4a88ee0) at ../../gdb-8.1/gdb/findvar.c:612
#47 0x0000000000899637 in read_frame_arg (sym=sym@entry=0x5acb140, frame=frame@entry=0x4a88ee0, argp=argp@entry=0x7ffff3d637d0, entryargp=entryargp@entry=0x7ffff3d637f0) at ../../gdb-8.1/gdb/stack.c:349
#48 0x000000000089a1ad in print_frame_args (func=<optimised out>, frame=frame@entry=0x4a88ee0, num=num@entry=-1, stream=0x2d40090) at ../../gdb-8.1/gdb/stack.c:681
#49 0x000000000089ac65 in print_frame (print_args=<optimised out>, print_what=LOCATION, print_level=-1, frame=0x4a88ee0, sal=...) at ../../gdb-8.1/gdb/stack.c:1203
#50 print_frame_info (frame=frame@entry=0x4a88ee0, print_level=print_level@entry=0, print_what=print_what@entry=LOCATION, print_args=print_args@entry=1, set_current_sal=set_current_sal@entry=0) at ../../gdb-8.1/gdb/stack.c:860
#51 0x000000000089b0c6 in print_stack_frame (frame=0x4a88ee0, print_level=0, print_what=LOCATION, set_current_sal=0) at ../../gdb-8.1/gdb/stack.c:177
#52 0x00000000008cf810 in print_thread_info_1 (uiout=0x2d4ff10, requested_threads=requested_threads@entry=0x0, global_ids=global_ids@entry=0, pid=pid@entry=-1, show_global_ids=show_global_ids@entry=0, this=<optimised out>, 
    this=<optimised out>) at ../../gdb-8.1/gdb/thread.c:1321
#53 0x00000000008d0049 in info_threads_command (arg=0x0, from_tty=<optimised out>) at ../../gdb-8.1/gdb/thread.c:1390
#54 0x000000000069db89 in cmd_func (cmd=<optimised out>, args=<optimised out>, from_tty=<optimised out>) at ../../gdb-8.1/gdb/cli/cli-decode.c:1886
#55 0x00000000008d26b1 in execute_command (p=<optimised out>, p@entry=0x29c1c20 "info threads ", from_tty=1) at ../../gdb-8.1/gdb/top.c:637
#56 0x00000000007d7a9c in command_handler (command=0x29c1c20 "info threads ") at ../../gdb-8.1/gdb/event-top.c:583


It is possible to reproduce the bug with a vanilla gdb-8.1 installation and the unwinder available at: https://github.com/tromey/spidermonkey-unwinder

just launch gdb, attach to any running process and type

(gdb) info threads
Comment 3 Emiliano Testa 2018-07-26 09:53:40 UTC
(In reply to Emiliano Testa from comment #2)
> I am having the same issue: using GDB-8.1, installed a python unwinder I
> created and I get either an assertion failure or a crash (depending on the
> value of 'show debug frame'): with 1 I get a crash, with 0 I get the
> assertion failure.
> 
> The root cause seems to be (when debug frame is 1):
> 
> 
> static void
> pyuw_this_id (struct frame_info *this_frame, void **cache_ptr,
>               struct frame_id *this_id)
> {
>   *this_id = ((cached_frame_info *) *cache_ptr)->frame_id;
>   if (pyuw_debug >= 1)
>     {
>       fprintf_unfiltered (gdb_stdlog, "%s: frame_id: ", __FUNCTION__);
>       fprint_frame_id (gdb_stdlog, *this_id);
>       fprintf_unfiltered (gdb_stdlog, "\n");
>     }
> }
> 
> cache_ptr is NULL hence the crash.
> 
> The backtrace for the above:
> 
> (gdb) bt
> #0  0x00000000006d6723 in pyuw_this_id (this_frame=0x5650d80,
> cache_ptr=0x5650d98, this_id=0x5650de0) at
> ../../gdb-8.1/gdb/python/py-unwind.c:461
> #1  0x00000000007e012c in compute_frame_id (fi=fi@entry=0x5650d80) at
> ../../gdb-8.1/gdb/frame.c:505
> #2  0x00000000007e026a in get_frame_id (fi=0x5650d80) at
> ../../gdb-8.1/gdb/frame.c:537
> #3  0x00000000007e3535 in get_prev_frame_id_by_id (id=...) at
> ../../gdb-8.1/gdb/frame.c:2368
> #4  0x00000000008ffbe4 in value_fetch_lazy (val=val@entry=0x59550f0) at
> ../../gdb-8.1/gdb/value.c:3968
> #5  0x00000000007de439 in value_of_register (regnum=16, frame=<optimised
> out>) at ../../gdb-8.1/gdb/findvar.c:275
> #6  0x00000000006d64f8 in pending_framepy_read_register (self=0x57d04f0,
> args=<optimised out>) at ../../gdb-8.1/gdb/python/py-unwind.c:402
> #7  0x0000000000ea4828 in call_function (oparg=<optimised out>,
> pp_stack=0x7fffa1325fe8) at ../Python-2.7.3/Python/ceval.c:4021
> #8  PyEval_EvalFrameEx (f=f@entry=0x594f8d0, throwflag=throwflag@entry=0) at
> ../Python-2.7.3/Python/ceval.c:2666
> #9  0x0000000000ea43e5 in fast_function (nk=0, na=<optimised out>, n=2,
> pp_stack=0x7fffa13260e8, func=0x58dd410) at
> ../Python-2.7.3/Python/ceval.c:4107
> #10 call_function (oparg=<optimised out>, pp_stack=0x7fffa13260e8) at
> ../Python-2.7.3/Python/ceval.c:4042
> #11 PyEval_EvalFrameEx (f=f@entry=0x5970750, throwflag=throwflag@entry=0) at
> ../Python-2.7.3/Python/ceval.c:2666
> #12 0x0000000000ea5bbc in PyEval_EvalCodeEx (co=<optimised out>,
> globals=<optimised out>, locals=locals@entry=0x0, args=args@entry=0x57f6c38,
> argcount=<optimised out>, kws=kws@entry=0x0, kwcount=0, defs=0x0,
> defcount=0, closure=0x0)
>     at ../Python-2.7.3/Python/ceval.c:3253
> #13 0x0000000000f14b0c in function_call (func=0x58dd398, arg=0x57f6c20,
> kw=0x0) at ../Python-2.7.3/Objects/funcobject.c:526
> #14 0x0000000000e1a37a in PyObject_Call (func=func@entry=0x58dd398,
> arg=arg@entry=0x57f6c20, kw=kw@entry=0x0) at
> ../Python-2.7.3/Objects/abstract.c:2529
> #15 0x0000000000e2321f in instancemethod_call (func=0x58dd398,
> arg=0x57f6c20, kw=0x0) at ../Python-2.7.3/Objects/classobject.c:2578
> #16 0x0000000000e1a37a in PyObject_Call (func=func@entry=0x593ed20,
> arg=arg@entry=0x67a4dd0, kw=kw@entry=0x0) at
> ../Python-2.7.3/Objects/abstract.c:2529
> #17 0x0000000000e77935 in slot_tp_call (self=<optimised out>,
> args=0x67a4dd0, kwds=0x0) at ../Python-2.7.3/Objects/typeobject.c:5403
> #18 0x0000000000e1a37a in PyObject_Call (func=func@entry=0x57d60d0,
> arg=arg@entry=0x67a4dd0, kw=kw@entry=0x0) at
> ../Python-2.7.3/Objects/abstract.c:2529
> #19 0x0000000000ea27e5 in do_call (nk=0, na=<optimised out>,
> pp_stack=0x7fffa1326618, func=0x57d60d0) at
> ../Python-2.7.3/Python/ceval.c:4239
> #20 call_function (oparg=<optimised out>, pp_stack=0x7fffa1326618) at
> ../Python-2.7.3/Python/ceval.c:4044
> #21 PyEval_EvalFrameEx (f=f@entry=0x44af150, throwflag=throwflag@entry=0) at
> ../Python-2.7.3/Python/ceval.c:2666
> #22 0x0000000000ea5bbc in PyEval_EvalCodeEx (co=<optimised out>,
> globals=<optimised out>, locals=locals@entry=0x0, args=args@entry=0x37d67e8,
> argcount=<optimised out>, kws=kws@entry=0x0, kwcount=0, defs=0x0,
> defcount=0, closure=0x0)
>     at ../Python-2.7.3/Python/ceval.c:3253
> #23 0x0000000000f14b0c in function_call (func=0x3773b90, arg=0x37d67d0,
> kw=0x0) at ../Python-2.7.3/Objects/funcobject.c:526
> #24 0x0000000000e1afe5 in PyObject_Call (kw=0x0, arg=0x37d67d0,
> func=0x3773b90) at ../Python-2.7.3/Objects/abstract.c:2529
> #25 PyObject_CallFunctionObjArgs (callable=0x3773b90) at
> ../Python-2.7.3/Objects/abstract.c:2760
> #26 0x00000000006d7256 in pyuw_sniffer (self=<optimised out>,
> this_frame=0x5650d80, cache_ptr=0x5650d98) at
> ../../gdb-8.1/gdb/python/py-unwind.c:539
> #27 0x00000000007e404d in frame_unwind_try_unwinder
> (this_frame=this_frame@entry=0x5650d80,
> this_cache=this_cache@entry=0x5650d98, unwinder=0x39aca00) at
> ../../gdb-8.1/gdb/frame-unwind.c:106
> #28 0x00000000007e4427 in frame_unwind_find_by_frame
> (this_frame=this_frame@entry=0x5650d80,
> this_cache=this_cache@entry=0x5650d98) at
> ../../gdb-8.1/gdb/frame-unwind.c:164
> #29 0x00000000007e01e3 in compute_frame_id (fi=fi@entry=0x5650d80) at
> ../../gdb-8.1/gdb/frame.c:501
> #30 0x00000000007e026a in get_frame_id (fi=fi@entry=0x5650d80) at
> ../../gdb-8.1/gdb/frame.c:537
> #31 0x00000000008ce65b in
> scoped_restore_current_thread::scoped_restore_current_thread
> (this=0x7fffa1326b10) at ../../gdb-8.1/gdb/thread.c:1573
> #32 0x00000000008cf564 in print_thread_info_1 (uiout=0x391ff10,
> requested_threads=requested_threads@entry=0x0,
> global_ids=global_ids@entry=0, pid=pid@entry=-1,
> show_global_ids=show_global_ids@entry=0, this=<optimised out>, 
>     this=<optimised out>) at ../../gdb-8.1/gdb/thread.c:1250
> #33 0x00000000008d0049 in info_threads_command (arg=0x0, from_tty=<optimised
> out>) at ../../gdb-8.1/gdb/thread.c:1390
> #34 0x000000000069db89 in cmd_func (cmd=<optimised out>, args=<optimised
> out>, from_tty=<optimised out>) at ../../gdb-8.1/gdb/cli/cli-decode.c:1886
> #35 0x00000000008d26b1 in execute_command (p=<optimised out>,
> p@entry=0x3591c20 "info threads ", from_tty=1) at ../../gdb-8.1/gdb/top.c:637
> #36 0x00000000007d7a9c in command_handler (command=0x3591c20 "info threads
> ") at ../../gdb-8.1/gdb/event-top.c:583
> #37 0x00000000007d7da8 in command_line_handler (rl=<optimised out>) at
> ../../gdb-8.1/gdb/event-top.c:774
> 
> With debug frame 0 I get an assertion failure:
> 
> gdb_assert (fi->level == 0);
> 
> inside get_frame_id()
> 
> and the backtrace:
> 
> (gdb) bt
> #0  0x00007f7aea353428 in __GI_raise (sig=sig@entry=6) at
> ../sysdeps/unix/sysv/linux/raise.c:54
> #1  0x00007f7aea35502a in __GI_abort () at abort.c:89
> #2  0x00000000008e9086 in dump_core () at ../../gdb-8.1/gdb/utils.c:284
> #3  0x00000000008ed1dd in internal_vproblem(internal_problem *, const char
> *, int, const char *, typedef __va_list_tag __va_list_tag *)
> (problem=problem@entry=0x20ff340 <internal_error_problem>, file=<optimised
> out>, 
>     line=<optimised out>, fmt=<optimised out>, ap=ap@entry=0x7ffff3d626b8)
> at ../../gdb-8.1/gdb/utils.c:493
> #4  0x00000000008ed2e9 in internal_verror (file=<optimised out>,
> line=<optimised out>, fmt=<optimised out>, 
>     ap=ap@entry=0x7ffff3d626b8) at ../../gdb-8.1/gdb/utils.c:518
> #5  0x000000000070f5f2 in internal_error (file=file@entry=0x10a67d7
> "../../gdb-8.1/gdb/frame.c", line=line@entry=534, 
>     fmt=<optimised out>) at ../../gdb-8.1/gdb/common/errors.c:55
> #6  0x00000000007e031b in get_frame_id (fi=0x4a88fa0) at
> ../../gdb-8.1/gdb/frame.c:534
> #7  0x0000000000816dec in inline_frame_this_id (this_frame=0x4a88ee0,
> this_cache=<optimised out>, this_id=0x4a88f40)
>     at ../../gdb-8.1/gdb/inline-frame.c:160
> #8  0x00000000007e012c in compute_frame_id (fi=fi@entry=0x4a88ee0) at
> ../../gdb-8.1/gdb/frame.c:505
> #9  0x00000000007e026a in get_frame_id (fi=fi@entry=0x4a88ee0) at
> ../../gdb-8.1/gdb/frame.c:537
> #10 0x00000000007de2d8 in value_of_register_lazy
> (frame=frame@entry=0x4a88fa0, regnum=regnum@entry=16)
>     at ../../gdb-8.1/gdb/findvar.c:298
> #11 0x00000000007de42e in value_of_register (regnum=16, frame=0x4a88fa0) at
> ../../gdb-8.1/gdb/findvar.c:274
> #12 0x00000000006d64f8 in pending_framepy_read_register (self=0x59494f0,
> args=<optimised out>)
>     at ../../gdb-8.1/gdb/python/py-unwind.c:402
> #13 0x0000000000ea4828 in call_function (oparg=<optimised out>,
> pp_stack=0x7ffff3d629b8)
>     at ../Python-2.7.3/Python/ceval.c:4021
> #14 PyEval_EvalFrameEx (f=f@entry=0x4a905e0, throwflag=throwflag@entry=0) at
> ../Python-2.7.3/Python/ceval.c:2666
> #15 0x0000000000ea43e5 in fast_function (nk=0, na=<optimised out>, n=2,
> pp_stack=0x7ffff3d62ab8, func=0x4c19410)
>     at ../Python-2.7.3/Python/ceval.c:4107
> #16 call_function (oparg=<optimised out>, pp_stack=0x7ffff3d62ab8) at
> ../Python-2.7.3/Python/ceval.c:4042
> #17 PyEval_EvalFrameEx (f=f@entry=0x3c0ef90, throwflag=throwflag@entry=0) at
> ../Python-2.7.3/Python/ceval.c:2666
> #18 0x0000000000ea5bbc in PyEval_EvalCodeEx (co=<optimised out>,
> globals=<optimised out>, locals=locals@entry=0x0, 
>     args=args@entry=0x596fc80, argcount=<optimised out>, kws=kws@entry=0x0,
> kwcount=0, defs=0x0, defcount=0, 
>     closure=0x0) at ../Python-2.7.3/Python/ceval.c:3253
> #19 0x0000000000f14b0c in function_call (func=0x4c19398, arg=0x596fc68,
> kw=0x0)
> ---Type <return> to continue, or q <return> to quit---
>    :526
> #20 0x0000000000e1a37a in PyObject_Call (func=func@entry=0x4c19398,
> arg=arg@entry=0x596fc68, kw=kw@entry=0x0) at
> ../Python-2.7.3/Objects/abstract.c:2529
> #21 0x0000000000e2321f in instancemethod_call (func=0x4c19398,
> arg=0x596fc68, kw=0x0) at ../Python-2.7.3/Objects/classobject.c:2578
> #22 0x0000000000e1a37a in PyObject_Call (func=func@entry=0x4d77d20,
> arg=arg@entry=0x5bdfe50, kw=kw@entry=0x0) at
> ../Python-2.7.3/Objects/abstract.c:2529
> #23 0x0000000000e77935 in slot_tp_call (self=<optimised out>,
> args=0x5bdfe50, kwds=0x0) at ../Python-2.7.3/Objects/typeobject.c:5403
> #24 0x0000000000e1a37a in PyObject_Call (func=func@entry=0x5951150,
> arg=arg@entry=0x5bdfe50, kw=kw@entry=0x0) at
> ../Python-2.7.3/Objects/abstract.c:2529
> #25 0x0000000000ea27e5 in do_call (nk=0, na=<optimised out>,
> pp_stack=0x7ffff3d62fe8, func=0x5951150) at
> ../Python-2.7.3/Python/ceval.c:4239
> #26 call_function (oparg=<optimised out>, pp_stack=0x7ffff3d62fe8) at
> ../Python-2.7.3/Python/ceval.c:4044
> #27 PyEval_EvalFrameEx (f=f@entry=0x3a9d380, throwflag=throwflag@entry=0) at
> ../Python-2.7.3/Python/ceval.c:2666
> #28 0x0000000000ea5bbc in PyEval_EvalCodeEx (co=<optimised out>,
> globals=<optimised out>, locals=locals@entry=0x0, args=args@entry=0x2c067e8,
> argcount=<optimised out>, kws=kws@entry=0x0, kwcount=0, defs=0x0,
> defcount=0, closure=0x0)
>     at ../Python-2.7.3/Python/ceval.c:3253
> #29 0x0000000000f14b0c in function_call (func=0x2ba3b90, arg=0x2c067d0,
> kw=0x0) at ../Python-2.7.3/Objects/funcobject.c:526
> #30 0x0000000000e1afe5 in PyObject_Call (kw=0x0, arg=0x2c067d0,
> func=0x2ba3b90) at ../Python-2.7.3/Objects/abstract.c:2529
> #31 PyObject_CallFunctionObjArgs (callable=0x2ba3b90) at
> ../Python-2.7.3/Objects/abstract.c:2760
> #32 0x00000000006d7256 in pyuw_sniffer (self=<optimised out>,
> this_frame=0x4a88fa0, cache_ptr=0x4a88fb8) at
> ../../gdb-8.1/gdb/python/py-unwind.c:539
> #33 0x00000000007e404d in frame_unwind_try_unwinder
> (this_frame=this_frame@entry=0x4a88fa0,
> this_cache=this_cache@entry=0x4a88fb8, unwinder=0x2de4ae0) at
> ../../gdb-8.1/gdb/frame-unwind.c:106
> #34 0x00000000007e4427 in frame_unwind_find_by_frame
> (this_frame=this_frame@entry=0x4a88fa0,
> this_cache=this_cache@entry=0x4a88fb8) at
> ../../gdb-8.1/gdb/frame-unwind.c:164
> #35 0x00000000007e01e3 in compute_frame_id (fi=fi@entry=0x4a88fa0) at
> ../../gdb-8.1/gdb/frame.c:501
> #36 0x00000000007e0738 in get_prev_frame_if_no_cycle
> (this_frame=this_frame@entry=0x4a88ee0) at ../../gdb-8.1/gdb/frame.c:1913
> #37 0x00000000007e2757 in get_prev_frame_always_1
> (this_frame=this_frame@entry=0x4a88ee0) at ../../gdb-8.1/gdb/frame.c:2087
> #38 0x00000000007e2ccd in get_prev_frame_always
> (this_frame=this_frame@entry=0x4a88ee0) at ../../gdb-8.1/gdb/frame.c:2103
> #39 0x0000000000816de1 in inline_frame_this_id (this_frame=0x4a88ee0,
> this_cache=<optimised out>, this_id=0x4a88f40) at
> ../../gdb-8.1/gdb/inline-frame.c:160
> #40 0x00000000007e012c in compute_frame_id (fi=fi@entry=0x4a88ee0) at
> ../../gdb-8.1/gdb/frame.c:505
> #41 0x00000000007e026a in get_frame_id (fi=fi@entry=0x4a88ee0) at
> ../../gdb-8.1/gdb/frame.c:537
> #42 0x00000000007de9f5 in value_from_register (type=type@entry=0x5acaee0,
> regnum=regnum@entry=6, frame=frame@entry=0x4a88ee0) at
> ../../gdb-8.1/gdb/findvar.c:938
> #43 0x00000000007979fd in dwarf2_evaluate_loc_desc_full (type=0x5acaee0,
> frame=frame@entry=0x4a88ee0, data=<optimised out>, size=<optimised out>,
> per_cu=<optimised out>, subobj_type=0x5acaee0, subobj_type@entry=0x0,
> subobj_byte_offset=0)
>     at ../../gdb-8.1/gdb/dwarf2loc.c:2418
> #44 0x00000000007982d3 in dwarf2_evaluate_loc_desc (per_cu=<optimised out>,
> size=<optimised out>, data=<optimised out>, frame=0x4a88ee0, type=<optimised
> out>) at ../../gdb-8.1/gdb/dwarf2loc.c:2544
> #45 loclist_read_variable (symbol=0x5acb140, frame=0x4a88ee0) at
> ../../gdb-8.1/gdb/dwarf2loc.c:4456
> #46 0x00000000007debe7 in default_read_var_value (var=0x5acb140,
> var_block=0x0, frame=0x4a88ee0) at ../../gdb-8.1/gdb/findvar.c:612
> #47 0x0000000000899637 in read_frame_arg (sym=sym@entry=0x5acb140,
> frame=frame@entry=0x4a88ee0, argp=argp@entry=0x7ffff3d637d0,
> entryargp=entryargp@entry=0x7ffff3d637f0) at ../../gdb-8.1/gdb/stack.c:349
> #48 0x000000000089a1ad in print_frame_args (func=<optimised out>,
> frame=frame@entry=0x4a88ee0, num=num@entry=-1, stream=0x2d40090) at
> ../../gdb-8.1/gdb/stack.c:681
> #49 0x000000000089ac65 in print_frame (print_args=<optimised out>,
> print_what=LOCATION, print_level=-1, frame=0x4a88ee0, sal=...) at
> ../../gdb-8.1/gdb/stack.c:1203
> #50 print_frame_info (frame=frame@entry=0x4a88ee0,
> print_level=print_level@entry=0, print_what=print_what@entry=LOCATION,
> print_args=print_args@entry=1, set_current_sal=set_current_sal@entry=0) at
> ../../gdb-8.1/gdb/stack.c:860
> #51 0x000000000089b0c6 in print_stack_frame (frame=0x4a88ee0, print_level=0,
> print_what=LOCATION, set_current_sal=0) at ../../gdb-8.1/gdb/stack.c:177
> #52 0x00000000008cf810 in print_thread_info_1 (uiout=0x2d4ff10,
> requested_threads=requested_threads@entry=0x0,
> global_ids=global_ids@entry=0, pid=pid@entry=-1,
> show_global_ids=show_global_ids@entry=0, this=<optimised out>, 
>     this=<optimised out>) at ../../gdb-8.1/gdb/thread.c:1321
> #53 0x00000000008d0049 in info_threads_command (arg=0x0, from_tty=<optimised
> out>) at ../../gdb-8.1/gdb/thread.c:1390
> #54 0x000000000069db89 in cmd_func (cmd=<optimised out>, args=<optimised
> out>, from_tty=<optimised out>) at ../../gdb-8.1/gdb/cli/cli-decode.c:1886
> #55 0x00000000008d26b1 in execute_command (p=<optimised out>,
> p@entry=0x29c1c20 "info threads ", from_tty=1) at ../../gdb-8.1/gdb/top.c:637
> #56 0x00000000007d7a9c in command_handler (command=0x29c1c20 "info threads
> ") at ../../gdb-8.1/gdb/event-top.c:583
> 
> 
> It is possible to reproduce the bug with a vanilla gdb-8.1 installation and
> the unwinder available at: https://github.com/tromey/spidermonkey-unwinder
> 
> just launch gdb, attach to any running process and type
> 
> (gdb) info threads

I forgot to mention that reverting the commit as Tom did fixed the crash
Comment 4 Paul Carroll 2019-04-12 20:08:42 UTC
We are seeing the same problem with GDB 8.2.1, with the same assertion.
And we are working around it by reverting that 1-line patch.

What is interesting is that I see no difference in the GDB test results with or without that patch.
As noted by Tom, the changes to gdb/testsuite/gdb.python/py-recurse-unwind*, that were made at the same time as that one-line patch, don't seem to be affected by reverting this patch.

Is there a test case that can be produced that fails when it is not present?
I haven't found one yet.
Comment 5 Johan Tufvesson 2019-06-18 06:20:16 UTC
I guess that this is the bug I see. I have a custom unwinder which causes this fault:
/tmp/jenkins/jenkins-GCC-7-build_toolchain_docker-775_20180622_1529687456/src/gdb/gdb/frame.c:534: internal-error: frame_
id get_frame_id(frame_info*): Assertion `fi->level == 0' failed.

I get this error with a pre-built GDB for ARM from developer.arm.com with version 8.1, and a pre-built GDB from https://gnu-mcu-eclipse.github.io/blog/2019/05/24/arm-none-eabi-gcc-v8-2-1-1-7-released/ with version 8.3, but not with a pre-built GDB from developer.arm.com with version 7.10.

The Python unwinder can be minimized to just this code:

class MyUnwind( gdb.unwinder.Unwinder ):

    def __init__( self ):
        super( MyUnwind, self ).__init__( "MyUnwinder" )

    def __call__( self, pending_frame ):
        # If the next line is removed, everything works as expected
        Pc = pending_frame.read_register( 'pc' )
        return None

If this unwinder is used to unwind a backtrace going through an inline stack frame, the above internal-error is shown.

This bug unfortunately keeps us from going forward and use modern releases of GDB.
Comment 6 Mark 2020-01-14 17:03:08 UTC
Created attachment 12206 [details]
Fix for fi->level assert
Comment 7 Mark 2020-01-14 17:04:12 UTC
Created attachment 12207 [details]
c++ file to help repro the bug
Comment 8 Mark 2020-01-14 17:04:44 UTC
Created attachment 12208 [details]
python unwinder to help repro the bug
Comment 9 Mark 2020-01-14 17:08:09 UTC
The bug happens because inline frames don't get a frame id until their caller does, but  value_of_register_lazy needs a frame id so that value_fetch_lazy can find the frame again.

But there's no need to do things lazily, so we can just fetch the value directly, bypassing the need for a frame id.

To repro the bug:

% g++ -O2 bug.cpp -o bug -g2
% gdb -ex "source bug.py" -ex "b printf" -ex run -ex bt bug
GNU gdb (GDB) 10.0.50.20200114-git
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Type "show copying" and "show warranty" for details.
This GDB was configured as "x86_64-pc-linux-gnu".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
    <http://www.gnu.org/software/gdb/documentation/>.

For help, type "help".
Type "apropos word" to search for commands related to "word"...
Reading symbols from bug...
Breakpoint 1 at 0x400420
Starting program: /data/users/mwilliams/hphp-7/fbcode/foobar/bug 

Breakpoint 1, 0x00007ffff7242290 in printf () from /lib64/libc.so.6
../../gdb/frame.c:579: internal-error: frame_id get_frame_id(frame_info*): Assertion `fi->level == 0' failed.
A problem internal to GDB has been detected,
further debugging may prove unreliable.
Quit this debugging session? (y or n) y
Comment 10 Sourceware Commits 2020-07-06 17:40:52 UTC
The master branch has been updated by Andrew Burgess <aburgess@sourceware.org>:

https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=9fc501fdfe5dc82b5e5388cde4ac2ab70ed69d75

commit 9fc501fdfe5dc82b5e5388cde4ac2ab70ed69d75
Author: Andrew Burgess <andrew.burgess@embecosm.com>
Date:   Mon Jun 8 11:36:13 2020 +0100

    gdb: Python unwinders, inline frames, and tail-call frames
    
    This started with me running into the bug described in python/22748,
    in summary, if the frame sniffing code accessed any registers within
    an inline frame then GDB would crash with this error:
    
      gdb/frame.c:579: internal-error: frame_id get_frame_id(frame_info*): Assertion `fi->level == 0' failed.
    
    The problem is that, when in the Python unwinder I write this:
    
      pending_frame.read_register ("register-name")
    
    This is translated internally into a call to `value_of_register',
    which in turn becomes a call to `value_of_register_lazy'.
    
    Usually this isn't a problem, `value_of_register_lazy' requires the
    next frame (more inner) to have a valid frame_id, which will be the
    case (if we're sniffing frame #1, then frame #0 will have had its
    frame-id figured out).
    
    Unfortunately if frame #0 is inline within frame #1, then the frame-id
    for frame #0 can't be computed until we have the frame-id for #1.  As
    a result we can't create a lazy register for frame #1 when frame #0 is
    inline.
    
    Initially I proposed a solution inline with that proposed in bugzilla,
    changing value_of_register to avoid creating a lazy register value.
    However, when this was discussed on the mailing list I got this reply:
    
      https://sourceware.org/pipermail/gdb-patches/2020-June/169633.html
    
    Which led me to look at these two patches:
    
      [1] https://sourceware.org/pipermail/gdb-patches/2020-April/167612.html
      [2] https://sourceware.org/pipermail/gdb-patches/2020-April/167930.html
    
    When I considered patches [1] and [2] I saw that all of the issues
    being addressed here were related, and that there was a single
    solution that could address all of these issues.
    
    First I wrote the new test gdb.opt/inline-frame-tailcall.exp, which
    shows that [1] and [2] regress the inline tail-call unwinder, the
    reason for this is that these two patches replace a call to
    gdbarch_unwind_pc with a call to get_frame_register, however, this is
    not correct.  The previous call to gdbarch_unwind_pc takes THIS_FRAME
    and returns the $pc value in the previous frame.  In contrast
    get_frame_register takes THIS_FRAME and returns the value of the $pc
    in THIS_FRAME; these calls are not equivalent.
    
    The reason these patches appear (or do) fix the regressions listed in
    [1] is that the tail call sniffer depends on identifying the address
    of a caller and a callee, GDB then looks for a tail-call sequence that
    takes us from the caller address to the callee, if such a series is
    found then tail-call frames are added.
    
    The bug that was being hit, and which was address in patch [1] is that
    in order to find the address of the caller, GDB ended up creating a
    lazy register value for an inline frame with to frame-id.  The
    solution in patch [1] is to instead take the address of the callee and
    treat this as the address of the caller.  Getting the address of the
    callee works, but we then end up looking for a tail-call series from
    the callee to the callee, which obviously doesn't return any sane
    results, so we don't insert any tail call frames.
    
    The original patch [1] did cause some breakage, so patch [2] undid
    patch [1] in all cases except those where we had an inline frame with
    no frame-id.  It just so happens that there were no tests that fitted
    this description _and_ which required tail-call frames to be
    successfully spotted, as a result patch [2] appeared to work.
    
    The new test inline-frame-tailcall.exp, exposes the flaw in patch [2].
    
    This commit undoes patch [1] and [2], and replaces them with a new
    solution, which is also different to the solution proposed in the
    python/22748 bug report.
    
    In this solution I propose that we introduce some special case logic
    to value_of_register_lazy.  To understand what this logic is we must
    first look at how inline frames unwind registers, this is very simple,
    they do this:
    
      static struct value *
      inline_frame_prev_register (struct frame_info *this_frame,
                                  void **this_cache, int regnum)
      {
        return get_frame_register_value (this_frame, regnum);
      }
    
    And remember:
    
      struct value *
      get_frame_register_value (struct frame_info *frame, int regnum)
      {
        return frame_unwind_register_value (frame->next, regnum);
      }
    
    So in all cases, unwinding a register in an inline frame just asks the
    next frame to unwind the register, this makes sense, as an inline
    frame doesn't really exist, when we unwind a register in an inline
    frame, we're really just asking the next frame for the value of the
    register in the previous, non-inline frame.
    
    So, if we assume that we only get into the missing frame-id situation
    when we try to unwind a register from an inline frame during the frame
    sniffing process, then we can change value_of_register_lazy to not
    create lazy register values for an inline frame.
    
    Imagine this stack setup, where #1 is inline within #2.
    
      #3 -> #2 -> #1 -> #0
            \______/
             inline
    
    Now when trying to figure out the frame-id for #1, we need to compute
    the frame-id for #2.  If the frame sniffer for #2 causes a lazy
    register read in #2, either due to a Python Unwinder, or for the
    tail-call sniffer, then we call value_of_register_lazy passing in
    frame #2.
    
    In value_of_register_lazy, we grab the next frame, which is #1, and we
    used to then ask for the frame-id of #1, which was not computed, and
    this was our bug.
    
    Now, I propose we spot that #1 is an inline frame, and so lookup the
    next frame of #1, which is #0.  As #0 is not inline it will have a
    valid frame-id, and so we create a lazy register value using #0 as the
    next-frame-id.  This will give us the exact same result we had
    previously (thanks to the code we inspected above).
    
    Encoding into value_of_register_lazy the knowledge that reading an
    inline frame register will always just forward to the next frame
    feels.... not ideal, but this seems like the cleanest solution to this
    recursive frame-id computation/sniffing issue that appears to crop
    up.
    
    The following two commits are fully reverted with this commit, these
    correspond to patches [1] and [2] respectively:
    
      commit 5939967b355ba6a940887d19847b7893a4506067
      Date:   Tue Apr 14 17:26:22 2020 -0300
    
          Fix inline frame unwinding breakage
    
      commit 991a3e2e9944a4b3a27bd989ac03c18285bd545d
      Date:   Sat Apr 25 00:32:44 2020 -0300
    
          Fix remaining inline/tailcall unwinding breakage for x86_64
    
    gdb/ChangeLog:
    
            PR python/22748
            * dwarf2/frame-tailcall.c (dwarf2_tailcall_sniffer_first): Remove
            special handling for inline frames.
            * findvar.c (value_of_register_lazy): Skip inline frames when
            creating lazy register values.
            * frame.c (frame_id_computed_p): Delete definition.
            * frame.h (frame_id_computed_p): Delete declaration.
    
    gdb/testsuite/ChangeLog:
    
            PR python/22748
            * gdb.opt/inline-frame-tailcall.c: New file.
            * gdb.opt/inline-frame-tailcall.exp: New file.
            * gdb.python/py-unwind-inline.c: New file.
            * gdb.python/py-unwind-inline.exp: New file.
            * gdb.python/py-unwind-inline.py: New file.
Comment 11 Tom Tromey 2021-06-07 14:45:02 UTC
Andrew, is this bug fixed?
It seems to be, to me, but I wanted to double-check.
Comment 12 Ludvig Janiuk 2022-05-11 15:46:01 UTC
I'd also like to know if it has been fixed. Experienced this issue on 9.2 on Ubuntu.
Comment 13 Tom Tromey 2022-06-10 22:25:57 UTC
Aha, Andrew wasn't CC'd on the bug last time around.
Comment 14 Andrew Burgess 2022-06-15 15:04:07 UTC
This issue should now be resolved.  If anyone can still reproduce this issue, please feel free to reopen this bug.