* [PATCH 0/1] gdb: set the cache information in 'get_prev_frame_maybe_check_cycle' @ 2026-06-25 14:58 Stephan Rohr 2026-06-25 14:58 ` [PATCH 1/1] " Stephan Rohr 0 siblings, 1 reply; 17+ messages in thread From: Stephan Rohr @ 2026-06-25 14:58 UTC (permalink / raw) To: gdb-patches From: "Rohr, Stephan" <stephan.rohr@intel.com> Hello all, I encountered an issue that GDB tries to reinflate a frame whose ID is not cached yet. I could only reproduce by invoking an inferior call in conjunction with a Python pretty printer; more details in the commit message. Regression tested on Ubuntu 24 using the unix target board file. I appreciate your feedback. Thanks Stephan Rohr, Stephan (1): gdb: set the cache information in 'get_prev_frame_maybe_check_cycle' gdb/frame.c | 18 +++++++++++++++++- gdb/frame.h | 4 ++++ .../gdb.python/pretty-print-call-by-hand.exp | 2 ++ 3 files changed, 23 insertions(+), 1 deletion(-) -- 2.43.0 Intel Deutschland GmbH Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany Tel: +49 89 991 430, www.intel.de Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell Chairperson of the Supervisory Board: Nicole Lau Registered Seat: Munich Commercial Register: Amtsgericht Muenchen HRB 186928 ^ permalink raw reply [flat|nested] 17+ messages in thread
* [PATCH 1/1] gdb: set the cache information in 'get_prev_frame_maybe_check_cycle' 2026-06-25 14:58 [PATCH 0/1] gdb: set the cache information in 'get_prev_frame_maybe_check_cycle' Stephan Rohr @ 2026-06-25 14:58 ` Stephan Rohr 2026-06-26 15:06 ` Tom Tromey 0 siblings, 1 reply; 17+ messages in thread From: Stephan Rohr @ 2026-06-25 14:58 UTC (permalink / raw) To: gdb-patches From: "Rohr, Stephan" <stephan.rohr@intel.com> 'get_prev_frame_maybe_check_cycle' retrieves the previous frame and computes the frame id. At this point, the frame id is not cached yet, which prevents the frame from being reinflated once the frane cache is flushed. Explicitly update the cache information before returning 'prev_frame' so the frame can be reinflated properly. The 'up' command invokes the following sequence. 1. Build the 'prev_frame' and eventually calls 'get_prev_frame_maybe_check_cycle'. The frame_id is only computed for the raw frame pointer, the wrapping 'frame_info_ptr' it not updated. 2. Printing the frame calls 'do_print_frame_info'. 3. If a pretty-printer runs an inferior call, the frame cache is flushed. 4. This triggers the assertion when reinflating the frame_info_ptr. Extend the 'gdb.python/pretty-print-call-by-hand.exp' testcase to cover this use-case. --- gdb/frame.c | 18 +++++++++++++++++- gdb/frame.h | 4 ++++ .../gdb.python/pretty-print-call-by-hand.exp | 2 ++ 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/gdb/frame.c b/gdb/frame.c index 4137e1d5edd..1d4e231ea81 100644 --- a/gdb/frame.c +++ b/gdb/frame.c @@ -2332,6 +2332,12 @@ get_prev_frame_maybe_check_cycle (const frame_info_ptr &this_frame) throw; } + /* No cycle and the frame is added to the frame stash, update the cache + information to ensure the frame can be reinflated after the cache is + flushed. */ + if (prev_frame) + prev_frame.update_cache (); + return prev_frame; } @@ -3431,7 +3437,17 @@ frame_info_ptr::frame_info_ptr (struct frame_info *ptr) if (m_ptr == nullptr) return; - m_cached_level = ptr->level; + update_cache (); +} + +/* See frame-info-ptr.h. */ + +void +frame_info_ptr::update_cache () +{ + gdb_assert (m_ptr != nullptr); + + m_cached_level = m_ptr->level; if (m_cached_level != 0 || m_ptr->this_id.value.user_created_p) m_cached_id = m_ptr->this_id.value; diff --git a/gdb/frame.h b/gdb/frame.h index f6553fb7b6d..897518371b2 100644 --- a/gdb/frame.h +++ b/gdb/frame.h @@ -332,6 +332,10 @@ class frame_info_ptr : public intrusive_list_node<frame_info_ptr> m_ptr = nullptr; } + /* Update the cached copies of frame_id and the frame level based on the + underlying pointer. */ + void update_cache (); + private: /* We sometimes need to construct frame_info_ptr objects around the sentinel_frame, which has level -1. Therefore, make the invalid frame diff --git a/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp b/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp index 52162fc9952..a2a29c4d0f8 100644 --- a/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp +++ b/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp @@ -108,6 +108,8 @@ with_test_prefix "frame movement down" { with_test_prefix "frame movement up" { if { [start_test "TAG: final frame"] == 0 } { gdb_test "up" [multi_line "#1 .*in g \\(mt=mytype is .*\\, depth=1\\).*" ".*first frame.*"] + gdb_test "p f ()" " = 2" + gdb_test "frame" [multi_line "#1 .*in g \\(mt=mytype is .*\\, depth=1\\).*" ".*first frame.*"] } } -- 2.43.0 Intel Deutschland GmbH Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany Tel: +49 89 991 430, www.intel.de Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell Chairperson of the Supervisory Board: Nicole Lau Registered Seat: Munich Commercial Register: Amtsgericht Muenchen HRB 186928 ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH 1/1] gdb: set the cache information in 'get_prev_frame_maybe_check_cycle' 2026-06-25 14:58 ` [PATCH 1/1] " Stephan Rohr @ 2026-06-26 15:06 ` Tom Tromey 2026-06-29 14:04 ` Rohr, Stephan 0 siblings, 1 reply; 17+ messages in thread From: Tom Tromey @ 2026-06-26 15:06 UTC (permalink / raw) To: Stephan Rohr; +Cc: gdb-patches >>>>> Stephan Rohr <stephan.rohr@intel.com> writes: > From: "Rohr, Stephan" <stephan.rohr@intel.com> > 'get_prev_frame_maybe_check_cycle' retrieves the previous frame and > computes the frame id. At this point, the frame id is not cached yet, > which prevents the frame from being reinflated once the frane cache is s/frane/frame/ > 1. Build the 'prev_frame' and eventually calls > 'get_prev_frame_maybe_check_cycle'. The frame_id is only computed > for the raw frame pointer, the wrapping 'frame_info_ptr' it not > updated. s/it not/is not/ > + /* No cycle and the frame is added to the frame stash, update the cache > + information to ensure the frame can be reinflated after the cache is > + flushed. */ > + if (prev_frame) > + prev_frame.update_cache (); > + I wonder if some other approach could be taken here. It seems unfortunate to have to add a new public method to this class just to work around some sort of ordering issue. That said I'm not sure I understand the problem well enough to suggest a better solution. Like should maybe get_prev_frame_raw not return a frame_info_ptr? And then the frame_info_ptr be created after the call to frame_stash_add in get_prev_frame_maybe_check_cycle? Tom ^ permalink raw reply [flat|nested] 17+ messages in thread
* RE: [PATCH 1/1] gdb: set the cache information in 'get_prev_frame_maybe_check_cycle' 2026-06-26 15:06 ` Tom Tromey @ 2026-06-29 14:04 ` Rohr, Stephan 2026-07-06 15:06 ` Andrew Burgess 2026-07-07 10:33 ` Andrew Burgess 0 siblings, 2 replies; 17+ messages in thread From: Rohr, Stephan @ 2026-06-29 14:04 UTC (permalink / raw) To: Tom Tromey; +Cc: gdb-patches Hi Tom, thank you for your feedback! I will fix the typos in v2 of the patch. I'll try to explain the problem in more detail now: 1. If you run an inferior call after the up command invalidates the selected frame. 2. The selected frame is rebuild following the chain get_selected_frame () -> lookup_selected_frame () -> get_prev_frame_always_1 () -> get_prev_frame_maybe_check_cycle () 3. At this point, the frame_info_ptr is constructed before the frame-id is computed. The cache Information is not updated as 'compute_frame_id' only updates the frame id of the internal 'frame_info' pointer. 4. If we now run the "frame" command in combination with a pretty-printer, this invokes another infcall to evaluate the pretty-printer. 5. This clears the 'm_ptr' member of the frame_info_ptr. Since the cache id is not set, a subsequent reinflate fails. A very simple fix for this could be: @@ -2332,7 +2361,7 @@ get_prev_frame_maybe_check_cycle (const frame_info_ptr &this_frame) throw; } - return prev_frame; + return frame_info_ptr (prev_frame.get ()); } It constructs a new frame_info_ptr using the updated raw pointer of 'prev_frame'. This is somewhat redundant as another frame_info_ptr is added to the frame list in the frame_info_ptr ctor. The frame list entry from frame_info_ptr prev_frame = get_prev_frame_raw (this_frame); is removed again by the frame_info_ptr's dtor. This would add some overhead. I didn't find a better solution. We could modify 'get_prev_frame_raw' to return a raw pointer instead (it is the only location where is this called at all). But we'd need a temporary copy of the frame_info_ptr anyways, either to pass a frame_info_ptr to 'compute_frame_id' or inside of 'compute_frame_id' (to forward the frame id to 'frame_unwind_find_by_frame'). Changing these functions to accept a raw frame_info pointer is not desired in my point of view. I appreciate your feedback. Thanks Stephan > -----Original Message----- > From: Tom Tromey <tom@tromey.com> > Sent: Friday, 26 June 2026 17:07 > To: Rohr, Stephan <stephan.rohr@intel.com> > Cc: gdb-patches@sourceware.org > Subject: Re: [PATCH 1/1] gdb: set the cache information in > 'get_prev_frame_maybe_check_cycle' > > >>>>> Stephan Rohr <stephan.rohr@intel.com> writes: > > > From: "Rohr, Stephan" <stephan.rohr@intel.com> > > 'get_prev_frame_maybe_check_cycle' retrieves the previous frame and > > computes the frame id. At this point, the frame id is not cached yet, > > which prevents the frame from being reinflated once the frane cache is > > s/frane/frame/ > > > 1. Build the 'prev_frame' and eventually calls > > 'get_prev_frame_maybe_check_cycle'. The frame_id is only computed > > for the raw frame pointer, the wrapping 'frame_info_ptr' it not > > updated. > > s/it not/is not/ > > > + /* No cycle and the frame is added to the frame stash, update the cache > > + information to ensure the frame can be reinflated after the cache is > > + flushed. */ > > + if (prev_frame) > > + prev_frame.update_cache (); > > + > > I wonder if some other approach could be taken here. > It seems unfortunate to have to add a new public method to this class > just to work around some sort of ordering issue. > > That said I'm not sure I understand the problem well enough to suggest a > better solution. Like should maybe get_prev_frame_raw not return a > frame_info_ptr? And then the frame_info_ptr be created after the call > to frame_stash_add in get_prev_frame_maybe_check_cycle? > > Tom Intel Deutschland GmbH Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany Tel: +49 89 991 430, www.intel.de Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell Chairperson of the Supervisory Board: Nicole Lau Registered Seat: Munich Commercial Register: Amtsgericht Muenchen HRB 186928 ^ permalink raw reply [flat|nested] 17+ messages in thread
* RE: [PATCH 1/1] gdb: set the cache information in 'get_prev_frame_maybe_check_cycle' 2026-06-29 14:04 ` Rohr, Stephan @ 2026-07-06 15:06 ` Andrew Burgess 2026-07-07 7:57 ` Rohr, Stephan 2026-07-07 10:33 ` Andrew Burgess 1 sibling, 1 reply; 17+ messages in thread From: Andrew Burgess @ 2026-07-06 15:06 UTC (permalink / raw) To: Rohr, Stephan, Tom Tromey; +Cc: gdb-patches "Rohr, Stephan" <stephan.rohr@intel.com> writes: > Hi Tom, > > thank you for your feedback! I will fix the typos in v2 of the patch. > > I'll try to explain the problem in more detail now: > > 1. If you run an inferior call after the up command invalidates > the selected frame. > 2. The selected frame is rebuild following the chain > > get_selected_frame () -> lookup_selected_frame () > -> get_prev_frame_always_1 () > -> get_prev_frame_maybe_check_cycle () > > 3. At this point, the frame_info_ptr is constructed before the > frame-id is computed. The cache Information is not updated as > 'compute_frame_id' only updates the frame id of the internal > 'frame_info' pointer. > > 4. If we now run the "frame" command in combination with a > pretty-printer, this invokes another infcall to evaluate the > pretty-printer. > > 5. This clears the 'm_ptr' member of the frame_info_ptr. Since the cache > id is not set, a subsequent reinflate fails. > > A very simple fix for this could be: > > @@ -2332,7 +2361,7 @@ get_prev_frame_maybe_check_cycle (const frame_info_ptr &this_frame) > throw; > } > > - return prev_frame; > + return frame_info_ptr (prev_frame.get ()); > } > > It constructs a new frame_info_ptr using the updated raw pointer > of 'prev_frame'. This is somewhat redundant as another frame_info_ptr > is added to the frame list in the frame_info_ptr ctor. > > The frame list entry from > > frame_info_ptr prev_frame = get_prev_frame_raw (this_frame); > > is removed again by the frame_info_ptr's dtor. > > This would add some overhead. > > I didn't find a better solution. We could modify 'get_prev_frame_raw' to > return a raw pointer instead (it is the only location where is this called at > all). But we'd need a temporary copy of the frame_info_ptr anyways, > either to pass a frame_info_ptr to 'compute_frame_id' or inside of > 'compute_frame_id' (to forward the frame id to > 'frame_unwind_find_by_frame'). Changing these functions to > accept a raw frame_info pointer is not desired in my point of view. > > I appreciate your feedback. > Hi Stephan, I ran into a similar problem myself recently, and had a different fix queued up, but it was part of a larger change. I've pulled it out and the patch is below, along with your test change. The test still passes. There's no commit message yet, I still need to think about exactly what's going on in this case a bit more, it's still not clear to me how (or where) the frame_info_ptr without the frame-id is actually created, as I thought frames (after 0) always got a frame-id before they were placed into a frame_info_ptr. I'm sharing this now just so you can take a look at the change and let me know what you think, I'll dig into this a little more and write up a commit message tomorrow, but I'd like to hear what you think of this approach. Thanks, Andrew --- diff --git a/gdb/frame.c b/gdb/frame.c index cefdde5ed1e..7a0f313f4db 100644 --- a/gdb/frame.c +++ b/gdb/frame.c @@ -2190,15 +2190,15 @@ reinit_frame_cache (void) sentinel_frame = nullptr; } + for (frame_info_ptr &iter : frame_info_ptr::frame_list) + iter.invalidate (); + frame_stash_invalidate (); /* Since we can't really be sure what the first object allocated was. */ obstack_free (&frame_cache_obstack, 0); obstack_init (&frame_cache_obstack); - for (frame_info_ptr &iter : frame_info_ptr::frame_list) - iter.invalidate (); - frame_debug_printf ("generation=%d", frame_cache_generation); } @@ -3435,10 +3435,21 @@ frame_info_ptr::frame_info_ptr (struct frame_info *ptr) if (m_ptr == nullptr) return; - m_cached_level = ptr->level; + m_cached_level = m_ptr->level; +} + +void +frame_info_ptr::invalidate () +{ + if (m_ptr == nullptr) + return; + + gdb_assert (m_cached_level == m_ptr->level); if (m_cached_level != 0 || m_ptr->this_id.value.user_created_p) m_cached_id = m_ptr->this_id.value; + + m_ptr = nullptr; } /* See frame-info-ptr.h. */ diff --git a/gdb/frame.h b/gdb/frame.h index f6553fb7b6d..b386303d895 100644 --- a/gdb/frame.h +++ b/gdb/frame.h @@ -327,10 +327,7 @@ class frame_info_ptr : public intrusive_list_node<frame_info_ptr> } /* Invalidate this pointer. */ - void invalidate () - { - m_ptr = nullptr; - } + void invalidate (); private: /* We sometimes need to construct frame_info_ptr objects around the diff --git a/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp b/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp index 52162fc9952..a2a29c4d0f8 100644 --- a/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp +++ b/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp @@ -108,6 +108,8 @@ with_test_prefix "frame movement down" { with_test_prefix "frame movement up" { if { [start_test "TAG: final frame"] == 0 } { gdb_test "up" [multi_line "#1 .*in g \\(mt=mytype is .*\\, depth=1\\).*" ".*first frame.*"] + gdb_test "p f ()" " = 2" + gdb_test "frame" [multi_line "#1 .*in g \\(mt=mytype is .*\\, depth=1\\).*" ".*first frame.*"] } } ^ permalink raw reply [flat|nested] 17+ messages in thread
* RE: [PATCH 1/1] gdb: set the cache information in 'get_prev_frame_maybe_check_cycle' 2026-07-06 15:06 ` Andrew Burgess @ 2026-07-07 7:57 ` Rohr, Stephan 2026-07-07 16:04 ` Andrew Burgess 0 siblings, 1 reply; 17+ messages in thread From: Rohr, Stephan @ 2026-07-07 7:57 UTC (permalink / raw) To: Andrew Burgess; +Cc: gdb-patches, Tom Tromey Hi Andrew, thanks for sharing this! One option that could cause a frame_info_ptr w/o a set ID is the one I found in 'get_prev_frame_maybe_check_cycle ()'. It is not obvious why this causes an assertion when reinflating the pointer later. My explanation how to reproduce the bug is: 1. The 'up' command updates 'selected_frame' to the frame at level 1. 2. The inferior call 'p f()' triggers a re-init of the frame cache. The selected_frame pointer is nulled, but the cached id is still valid. 3. The 'frame' command find 'selected_frame' is null, so it rebuilds by calling get_selected_frame --> lookup_selected_frame --> get_prev_frame --> get_prev_frame_maybe_check_cycle. A new frame_info_ptr is setup but its ID is only computed a few lines later. select_frame () then copies this uncached frame_info_ptr into selected frame. 4. The pretty-printer triggers another infcall: 'MytypePrinter.to_string()' --> gdb.parse_and_eval("f()") --> call_function_by_hand () This invalidates the pointer while printing the 'mt' argument. When printing the next argument, the pointer is reinflated via read_frame_arg --> ... --> get_frame_pc --> reinflate. Since level > 0 and m_ptr == nullptr, reinflation is done by the cached id. This is not set and causes the assertion to fire. Does this help? In general, I'm in favour of your patch as it is more generic and fixes all issues where we have a frame_info_ptr setup w/o an ID. I have a few comments, see below. Thanks Stephan > -----Original Message----- > From: Andrew Burgess <aburgess@redhat.com> > Sent: Monday, 6 July 2026 17:07 > To: Rohr, Stephan <stephan.rohr@intel.com>; Tom Tromey > <tom@tromey.com> > Cc: gdb-patches@sourceware.org > Subject: RE: [PATCH 1/1] gdb: set the cache information in > 'get_prev_frame_maybe_check_cycle' > > "Rohr, Stephan" <stephan.rohr@intel.com> writes: > > > Hi Tom, > > > > thank you for your feedback! I will fix the typos in v2 of the patch. > > > > I'll try to explain the problem in more detail now: > > > > 1. If you run an inferior call after the up command invalidates > > the selected frame. > > 2. The selected frame is rebuild following the chain > > > > get_selected_frame () -> lookup_selected_frame () > > -> get_prev_frame_always_1 () > > -> get_prev_frame_maybe_check_cycle () > > > > 3. At this point, the frame_info_ptr is constructed before the > > frame-id is computed. The cache Information is not updated as > > 'compute_frame_id' only updates the frame id of the internal > > 'frame_info' pointer. > > > > 4. If we now run the "frame" command in combination with a > > pretty-printer, this invokes another infcall to evaluate the > > pretty-printer. > > > > 5. This clears the 'm_ptr' member of the frame_info_ptr. Since the cache > > id is not set, a subsequent reinflate fails. > > > > A very simple fix for this could be: > > > > @@ -2332,7 +2361,7 @@ get_prev_frame_maybe_check_cycle (const > frame_info_ptr &this_frame) > > throw; > > } > > > > - return prev_frame; > > + return frame_info_ptr (prev_frame.get ()); > > } > > > > It constructs a new frame_info_ptr using the updated raw pointer > > of 'prev_frame'. This is somewhat redundant as another frame_info_ptr > > is added to the frame list in the frame_info_ptr ctor. > > > > The frame list entry from > > > > frame_info_ptr prev_frame = get_prev_frame_raw (this_frame); > > > > is removed again by the frame_info_ptr's dtor. > > > > This would add some overhead. > > > > I didn't find a better solution. We could modify 'get_prev_frame_raw' to > > return a raw pointer instead (it is the only location where is this called at > > all). But we'd need a temporary copy of the frame_info_ptr anyways, > > either to pass a frame_info_ptr to 'compute_frame_id' or inside of > > 'compute_frame_id' (to forward the frame id to > > 'frame_unwind_find_by_frame'). Changing these functions to > > accept a raw frame_info pointer is not desired in my point of view. > > > > I appreciate your feedback. > > > > Hi Stephan, > > I ran into a similar problem myself recently, and had a different fix > queued up, but it was part of a larger change. I've pulled it out and > the patch is below, along with your test change. The test still passes. > > There's no commit message yet, I still need to think about exactly > what's going on in this case a bit more, it's still not clear to me how > (or where) the frame_info_ptr without the frame-id is actually created, > as I thought frames (after 0) always got a frame-id before they were > placed into a frame_info_ptr. > > I'm sharing this now just so you can take a look at the change and let > me know what you think, I'll dig into this a little more and write up a > commit message tomorrow, but I'd like to hear what you think of this > approach. > > Thanks, > Andrew > > --- > > diff --git a/gdb/frame.c b/gdb/frame.c > index cefdde5ed1e..7a0f313f4db 100644 > --- a/gdb/frame.c > +++ b/gdb/frame.c > @@ -2190,15 +2190,15 @@ reinit_frame_cache (void) > sentinel_frame = nullptr; > } > > + for (frame_info_ptr &iter : frame_info_ptr::frame_list) > + iter.invalidate (); > + I see why we need this, but it's worth a comment that the invalidate loop must run before invalidating the frame stash. > frame_stash_invalidate (); > > /* Since we can't really be sure what the first object allocated was. */ > obstack_free (&frame_cache_obstack, 0); > obstack_init (&frame_cache_obstack); > > - for (frame_info_ptr &iter : frame_info_ptr::frame_list) > - iter.invalidate (); > - > frame_debug_printf ("generation=%d", frame_cache_generation); > } > > @@ -3435,10 +3435,21 @@ frame_info_ptr::frame_info_ptr (struct > frame_info *ptr) > if (m_ptr == nullptr) > return; > > - m_cached_level = ptr->level; > + m_cached_level = m_ptr->level; Do we need this? At this point, we should have m_ptr == ptr? > +} > + > +void > +frame_info_ptr::invalidate () > +{ > + if (m_ptr == nullptr) > + return; > + > + gdb_assert (m_cached_level == m_ptr->level); > > if (m_cached_level != 0 || m_ptr->this_id.value.user_created_p) > m_cached_id = m_ptr->this_id.value; I was wondering if we should add another guard: if ((m_cached_id == null_frame_id) && (m_cached_level != 0 || m_ptr->this_id.value.user_created_p)) Or is this implicitly guarded by the 'm_cached_level != 0' check? > + > + m_ptr = nullptr; > } > > /* See frame-info-ptr.h. */ > diff --git a/gdb/frame.h b/gdb/frame.h > index f6553fb7b6d..b386303d895 100644 > --- a/gdb/frame.h > +++ b/gdb/frame.h > @@ -327,10 +327,7 @@ class frame_info_ptr : public > intrusive_list_node<frame_info_ptr> > } > > /* Invalidate this pointer. */ > - void invalidate () > - { > - m_ptr = nullptr; > - } > + void invalidate (); > > private: > /* We sometimes need to construct frame_info_ptr objects around the > diff --git a/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp > b/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp > index 52162fc9952..a2a29c4d0f8 100644 > --- a/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp > +++ b/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp > @@ -108,6 +108,8 @@ with_test_prefix "frame movement down" { > with_test_prefix "frame movement up" { > if { [start_test "TAG: final frame"] == 0 } { > gdb_test "up" [multi_line "#1 .*in g \\(mt=mytype is .*\\, > depth=1\\).*" ".*first frame.*"] > + gdb_test "p f ()" " = 2" > + gdb_test "frame" [multi_line "#1 .*in g \\(mt=mytype is .*\\, > depth=1\\).*" ".*first frame.*"] > } > } > Intel Deutschland GmbH Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany Tel: +49 89 991 430, www.intel.de Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell Chairperson of the Supervisory Board: Nicole Lau Registered Seat: Munich Commercial Register: Amtsgericht Muenchen HRB 186928 ^ permalink raw reply [flat|nested] 17+ messages in thread
* RE: [PATCH 1/1] gdb: set the cache information in 'get_prev_frame_maybe_check_cycle' 2026-07-07 7:57 ` Rohr, Stephan @ 2026-07-07 16:04 ` Andrew Burgess 2026-07-08 8:46 ` Rohr, Stephan 2026-07-20 17:39 ` Tom Tromey 0 siblings, 2 replies; 17+ messages in thread From: Andrew Burgess @ 2026-07-07 16:04 UTC (permalink / raw) To: Rohr, Stephan; +Cc: gdb-patches, Tom Tromey "Rohr, Stephan" <stephan.rohr@intel.com> writes: > Hi Andrew, > > thanks for sharing this! > > One option that could cause a frame_info_ptr w/o a set ID is > the one I found in 'get_prev_frame_maybe_check_cycle ()'. > > It is not obvious why this causes an assertion when reinflating the > pointer later. My explanation how to reproduce the bug is: > > 1. The 'up' command updates 'selected_frame' to the frame at > level 1. > > 2. The inferior call 'p f()' triggers a re-init of the frame cache. The > selected_frame pointer is nulled, but the cached id is still valid. > > 3. The 'frame' command find 'selected_frame' is null, so it rebuilds > by calling get_selected_frame --> lookup_selected_frame > --> get_prev_frame --> get_prev_frame_maybe_check_cycle. > A new frame_info_ptr is setup but its ID is only computed a few lines > later. select_frame () then copies this uncached frame_info_ptr into > selected frame. > > 4. The pretty-printer triggers another infcall: > > 'MytypePrinter.to_string()' --> gdb.parse_and_eval("f()") > --> call_function_by_hand () > > This invalidates the pointer while printing the 'mt' argument. > When printing the next argument, the pointer is reinflated via > read_frame_arg --> ... --> get_frame_pc --> reinflate. Since level > 0 > and m_ptr == nullptr, reinflation is done by the cached id. This is not > set and causes the assertion to fire. > > Does this help? > > In general, I'm in favour of your patch as it is more generic and fixes all > issues where we have a frame_info_ptr setup w/o an ID. > > I have a few comments, see below. > > Thanks > Stephan > >> -----Original Message----- >> From: Andrew Burgess <aburgess@redhat.com> >> Sent: Monday, 6 July 2026 17:07 >> To: Rohr, Stephan <stephan.rohr@intel.com>; Tom Tromey >> <tom@tromey.com> >> Cc: gdb-patches@sourceware.org >> Subject: RE: [PATCH 1/1] gdb: set the cache information in >> 'get_prev_frame_maybe_check_cycle' >> >> "Rohr, Stephan" <stephan.rohr@intel.com> writes: >> >> > Hi Tom, >> > >> > thank you for your feedback! I will fix the typos in v2 of the patch. >> > >> > I'll try to explain the problem in more detail now: >> > >> > 1. If you run an inferior call after the up command invalidates >> > the selected frame. >> > 2. The selected frame is rebuild following the chain >> > >> > get_selected_frame () -> lookup_selected_frame () >> > -> get_prev_frame_always_1 () >> > -> get_prev_frame_maybe_check_cycle () >> > >> > 3. At this point, the frame_info_ptr is constructed before the >> > frame-id is computed. The cache Information is not updated as >> > 'compute_frame_id' only updates the frame id of the internal >> > 'frame_info' pointer. >> > >> > 4. If we now run the "frame" command in combination with a >> > pretty-printer, this invokes another infcall to evaluate the >> > pretty-printer. >> > >> > 5. This clears the 'm_ptr' member of the frame_info_ptr. Since the cache >> > id is not set, a subsequent reinflate fails. >> > >> > A very simple fix for this could be: >> > >> > @@ -2332,7 +2361,7 @@ get_prev_frame_maybe_check_cycle (const >> frame_info_ptr &this_frame) >> > throw; >> > } >> > >> > - return prev_frame; >> > + return frame_info_ptr (prev_frame.get ()); >> > } >> > >> > It constructs a new frame_info_ptr using the updated raw pointer >> > of 'prev_frame'. This is somewhat redundant as another frame_info_ptr >> > is added to the frame list in the frame_info_ptr ctor. >> > >> > The frame list entry from >> > >> > frame_info_ptr prev_frame = get_prev_frame_raw (this_frame); >> > >> > is removed again by the frame_info_ptr's dtor. >> > >> > This would add some overhead. >> > >> > I didn't find a better solution. We could modify 'get_prev_frame_raw' to >> > return a raw pointer instead (it is the only location where is this called at >> > all). But we'd need a temporary copy of the frame_info_ptr anyways, >> > either to pass a frame_info_ptr to 'compute_frame_id' or inside of >> > 'compute_frame_id' (to forward the frame id to >> > 'frame_unwind_find_by_frame'). Changing these functions to >> > accept a raw frame_info pointer is not desired in my point of view. >> > >> > I appreciate your feedback. >> > >> >> Hi Stephan, >> >> I ran into a similar problem myself recently, and had a different fix >> queued up, but it was part of a larger change. I've pulled it out and >> the patch is below, along with your test change. The test still passes. >> >> There's no commit message yet, I still need to think about exactly >> what's going on in this case a bit more, it's still not clear to me how >> (or where) the frame_info_ptr without the frame-id is actually created, >> as I thought frames (after 0) always got a frame-id before they were >> placed into a frame_info_ptr. >> >> I'm sharing this now just so you can take a look at the change and let >> me know what you think, I'll dig into this a little more and write up a >> commit message tomorrow, but I'd like to hear what you think of this >> approach. >> >> Thanks, >> Andrew >> >> --- >> >> diff --git a/gdb/frame.c b/gdb/frame.c >> index cefdde5ed1e..7a0f313f4db 100644 >> --- a/gdb/frame.c >> +++ b/gdb/frame.c >> @@ -2190,15 +2190,15 @@ reinit_frame_cache (void) >> sentinel_frame = nullptr; >> } >> >> + for (frame_info_ptr &iter : frame_info_ptr::frame_list) >> + iter.invalidate (); >> + > > I see why we need this, but it's worth a comment that the invalidate > loop must run before invalidating the frame stash. Added in my next version. > >> frame_stash_invalidate (); >> >> /* Since we can't really be sure what the first object allocated was. */ >> obstack_free (&frame_cache_obstack, 0); >> obstack_init (&frame_cache_obstack); >> >> - for (frame_info_ptr &iter : frame_info_ptr::frame_list) >> - iter.invalidate (); >> - >> frame_debug_printf ("generation=%d", frame_cache_generation); >> } >> >> @@ -3435,10 +3435,21 @@ frame_info_ptr::frame_info_ptr (struct >> frame_info *ptr) >> if (m_ptr == nullptr) >> return; >> >> - m_cached_level = ptr->level; >> + m_cached_level = m_ptr->level; > > Do we need this? At this point, we should have m_ptr == ptr? I'll revert this for the final patch. > >> +} >> + >> +void >> +frame_info_ptr::invalidate () >> +{ >> + if (m_ptr == nullptr) >> + return; >> + >> + gdb_assert (m_cached_level == m_ptr->level); >> >> if (m_cached_level != 0 || m_ptr->this_id.value.user_created_p) >> m_cached_id = m_ptr->this_id.value; > > I was wondering if we should add another guard: > > if ((m_cached_id == null_frame_id) > && (m_cached_level != 0 || m_ptr->this_id.value.user_created_p)) > > Or is this implicitly guarded by the 'm_cached_level != 0' check? Comparing to null_frame_id always returns false, see frame_id::operator== for details. If we _could_ compare to null_frame_id then we could add this assert: gdb_assert (m_cached_id == null_frame_id || m_cached_id == m_ptr->this_id.value); This means that, if a frame_info_ptr is invalidated multiple times, then the m_cached_id will be updated multiple times, but it should never change. But, as we cannot compare to null_frame_id, I'm not sure how we'd write that assert. Given that we check the frame-id matches when we reinflate though, I'm not sure the above assert adds much. Your proposal (if we could compare to null_frame_id) would prevent the repeated updates, but isn't needed to prevent any invalid behaviour, for the same reason, the frame-id should never be different. The updated patch is below, let me know what you think. Thanks, Andrew --- commit d13aa44f8ac57e52324da409145f73a0e3ca19a2 Author: Andrew Burgess <aburgess@redhat.com> Date: Thu Jun 25 14:58:50 2026 +0000 gdb: set frame_info_ptr::m_cached_id in the destructor Currently frame_info_ptr caches the frame_id at construction time, see frame_info_ptr::frame_info_ptr in frame.c. The problem with this is that a frame's frame-id might not be known at this point. Consider get_prev_frame_maybe_check_cycle, this calls get_prev_frame_raw to create the previous frame, placing the result into a frame_info_ptr PREV_FRAME. Then (for frames other than frame 0) compute_frame_id is called, however, this only computes the frame_id for the frame_info object pointed to by the frame_info_ptr, the cached frame_id within the frame_info_ptr itself is not updated. What this means is that in get_prev_frame_maybe_check_cycle, the PREV_FRAME local has no cached frame-id. If we consider the call stack: get_selected_frame lookup_selected_frame frame_find_by_id get_prev_frame get_prev_frame_always get_prev_frame_always_1 get_prev_frame_maybe_check_cycle Then what we see is that the frame_info_ptr created in get_prev_frame_maybe_check_cycle, which lacks a cached frame_id, can be passed all the way back to lookup_selected_frame, where it will be stored in the SELECTED_FRAME global by a call to select_frame. The outer get_selected_frame call (in the above backtrace) will then return the SELECTED_FRAME global, which lacks a cached frame-id. If GDB ever tries to reinflate the SELECTED_FRAME frame_info_ptr (or a copy of it), then we will trigger the assert: `gdb_assert (frame_id_p (m_cached_id));` which can be found in `frame_info_ptr::reinflate` in frame.c. An example of how this can be triggered is included in the updated test case, frame #1 is selected and the frame is printed. The pretty printer performs an inferior call which invalidates the frame cache, the assertion then triggers when trying to reinflate the selected frame frame_info_ptr. The problem is that frame's don't always know their frame-id when they are placed into a frame_info_ptr, but they always do (for frame other than #0) after get_prev_frame_maybe_check_cycle has finished. We could try to have get_prev_frame_maybe_check_cycle or compute_frame_id push the computed frame-id into the frame_info_ptr, or we can just defer caching the frame-id until we know we might need it, i.e. when the frame cache is being flushed. This second approach is actually nice in that it defers the work until we know we need it, and frame_info_ptr objects that are created and destroyed without the frame cache ever being flushed no longer need to cache the frame-id. This isn't going to give any noticable performance improvement, but still, it feels nice. The changes in this commit then are: 1. In reinit_frame_cache we call frame_info_ptr::invalidate before deleting all the frame_info objects (by clearing the obstacks), this allows us to copy the frame_id from these objects. 2. In frame_info_ptr::frame_info_ptr we still need to record every frame_info_ptr in the global list, and for now at least, we still cache the frame level, this is needed for frame_info_ptr::is_null, which checks the cached level. 3. In frame_info::invalidate, we can assert that the cached level matches the stored frame's level, this should never change, and it is here that we now cache the frame-id. Co-Authored-By: Rohr, Stephan <stephan.rohr@intel.com> diff --git a/gdb/frame.c b/gdb/frame.c index cefdde5ed1e..e7974060cb8 100644 --- a/gdb/frame.c +++ b/gdb/frame.c @@ -2190,15 +2190,18 @@ reinit_frame_cache (void) sentinel_frame = nullptr; } + /* Invalidation copies the frame-id from the managed frame_info object + into the frame_info_ptr, so this must run before the frame_info + objects are invalidated. */ + for (frame_info_ptr &iter : frame_info_ptr::frame_list) + iter.invalidate (); + frame_stash_invalidate (); /* Since we can't really be sure what the first object allocated was. */ obstack_free (&frame_cache_obstack, 0); obstack_init (&frame_cache_obstack); - for (frame_info_ptr &iter : frame_info_ptr::frame_list) - iter.invalidate (); - frame_debug_printf ("generation=%d", frame_cache_generation); } @@ -3436,9 +3439,23 @@ frame_info_ptr::frame_info_ptr (struct frame_info *ptr) return; m_cached_level = ptr->level; +} +void +frame_info_ptr::invalidate () +{ + if (m_ptr == nullptr) + return; + + gdb_assert (m_cached_level == m_ptr->level); + + /* If a frame_info_ptr is invalidated multiple times then we will end up + updating m_cached_id multiple times. This should be harmless as the + underlying frame_id should never change. */ if (m_cached_level != 0 || m_ptr->this_id.value.user_created_p) m_cached_id = m_ptr->this_id.value; + + m_ptr = nullptr; } /* See frame-info-ptr.h. */ diff --git a/gdb/frame.h b/gdb/frame.h index f6553fb7b6d..b386303d895 100644 --- a/gdb/frame.h +++ b/gdb/frame.h @@ -327,10 +327,7 @@ class frame_info_ptr : public intrusive_list_node<frame_info_ptr> } /* Invalidate this pointer. */ - void invalidate () - { - m_ptr = nullptr; - } + void invalidate (); private: /* We sometimes need to construct frame_info_ptr objects around the diff --git a/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp b/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp index 52162fc9952..a2a29c4d0f8 100644 --- a/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp +++ b/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp @@ -108,6 +108,8 @@ with_test_prefix "frame movement down" { with_test_prefix "frame movement up" { if { [start_test "TAG: final frame"] == 0 } { gdb_test "up" [multi_line "#1 .*in g \\(mt=mytype is .*\\, depth=1\\).*" ".*first frame.*"] + gdb_test "p f ()" " = 2" + gdb_test "frame" [multi_line "#1 .*in g \\(mt=mytype is .*\\, depth=1\\).*" ".*first frame.*"] } } ^ permalink raw reply [flat|nested] 17+ messages in thread
* RE: [PATCH 1/1] gdb: set the cache information in 'get_prev_frame_maybe_check_cycle' 2026-07-07 16:04 ` Andrew Burgess @ 2026-07-08 8:46 ` Rohr, Stephan 2026-07-08 13:59 ` Andrew Burgess 2026-07-20 17:39 ` Tom Tromey 1 sibling, 1 reply; 17+ messages in thread From: Rohr, Stephan @ 2026-07-08 8:46 UTC (permalink / raw) To: Andrew Burgess; +Cc: gdb-patches, Tom Tromey HI Andrew, Thanks for sharing. The patch itself looks good. I have a few comments regarding the commit message, see below. Thanks Stephan > -----Original Message----- > From: Andrew Burgess <aburgess@redhat.com> > Sent: Tuesday, 7 July 2026 18:04 > To: Rohr, Stephan <stephan.rohr@intel.com> > Cc: gdb-patches@sourceware.org; Tom Tromey <tom@tromey.com> > Subject: RE: [PATCH 1/1] gdb: set the cache information in > 'get_prev_frame_maybe_check_cycle' > > "Rohr, Stephan" <stephan.rohr@intel.com> writes: > > > Hi Andrew, > > > > thanks for sharing this! > > > > One option that could cause a frame_info_ptr w/o a set ID is > > the one I found in 'get_prev_frame_maybe_check_cycle ()'. > > > > It is not obvious why this causes an assertion when reinflating the > > pointer later. My explanation how to reproduce the bug is: > > > > 1. The 'up' command updates 'selected_frame' to the frame at > > level 1. > > > > 2. The inferior call 'p f()' triggers a re-init of the frame cache. The > > selected_frame pointer is nulled, but the cached id is still valid. > > > > 3. The 'frame' command find 'selected_frame' is null, so it rebuilds > > by calling get_selected_frame --> lookup_selected_frame > > --> get_prev_frame --> get_prev_frame_maybe_check_cycle. > > A new frame_info_ptr is setup but its ID is only computed a few lines > > later. select_frame () then copies this uncached frame_info_ptr into > > selected frame. > > > > 4. The pretty-printer triggers another infcall: > > > > 'MytypePrinter.to_string()' --> gdb.parse_and_eval("f()") > > --> call_function_by_hand () > > > > This invalidates the pointer while printing the 'mt' argument. > > When printing the next argument, the pointer is reinflated via > > read_frame_arg --> ... --> get_frame_pc --> reinflate. Since level > 0 > > and m_ptr == nullptr, reinflation is done by the cached id. This is not > > set and causes the assertion to fire. > > > > Does this help? > > > > In general, I'm in favour of your patch as it is more generic and fixes all > > issues where we have a frame_info_ptr setup w/o an ID. > > > > I have a few comments, see below. > > > > Thanks > > Stephan > > > >> -----Original Message----- > >> From: Andrew Burgess <aburgess@redhat.com> > >> Sent: Monday, 6 July 2026 17:07 > >> To: Rohr, Stephan <stephan.rohr@intel.com>; Tom Tromey > >> <tom@tromey.com> > >> Cc: gdb-patches@sourceware.org > >> Subject: RE: [PATCH 1/1] gdb: set the cache information in > >> 'get_prev_frame_maybe_check_cycle' > >> > >> "Rohr, Stephan" <stephan.rohr@intel.com> writes: > >> > >> > Hi Tom, > >> > > >> > thank you for your feedback! I will fix the typos in v2 of the patch. > >> > > >> > I'll try to explain the problem in more detail now: > >> > > >> > 1. If you run an inferior call after the up command invalidates > >> > the selected frame. > >> > 2. The selected frame is rebuild following the chain > >> > > >> > get_selected_frame () -> lookup_selected_frame () > >> > -> get_prev_frame_always_1 () > >> > -> get_prev_frame_maybe_check_cycle () > >> > > >> > 3. At this point, the frame_info_ptr is constructed before the > >> > frame-id is computed. The cache Information is not updated as > >> > 'compute_frame_id' only updates the frame id of the internal > >> > 'frame_info' pointer. > >> > > >> > 4. If we now run the "frame" command in combination with a > >> > pretty-printer, this invokes another infcall to evaluate the > >> > pretty-printer. > >> > > >> > 5. This clears the 'm_ptr' member of the frame_info_ptr. Since the cache > >> > id is not set, a subsequent reinflate fails. > >> > > >> > A very simple fix for this could be: > >> > > >> > @@ -2332,7 +2361,7 @@ get_prev_frame_maybe_check_cycle (const > >> frame_info_ptr &this_frame) > >> > throw; > >> > } > >> > > >> > - return prev_frame; > >> > + return frame_info_ptr (prev_frame.get ()); > >> > } > >> > > >> > It constructs a new frame_info_ptr using the updated raw pointer > >> > of 'prev_frame'. This is somewhat redundant as another frame_info_ptr > >> > is added to the frame list in the frame_info_ptr ctor. > >> > > >> > The frame list entry from > >> > > >> > frame_info_ptr prev_frame = get_prev_frame_raw (this_frame); > >> > > >> > is removed again by the frame_info_ptr's dtor. > >> > > >> > This would add some overhead. > >> > > >> > I didn't find a better solution. We could modify 'get_prev_frame_raw' to > >> > return a raw pointer instead (it is the only location where is this called at > >> > all). But we'd need a temporary copy of the frame_info_ptr anyways, > >> > either to pass a frame_info_ptr to 'compute_frame_id' or inside of > >> > 'compute_frame_id' (to forward the frame id to > >> > 'frame_unwind_find_by_frame'). Changing these functions to > >> > accept a raw frame_info pointer is not desired in my point of view. > >> > > >> > I appreciate your feedback. > >> > > >> > >> Hi Stephan, > >> > >> I ran into a similar problem myself recently, and had a different fix > >> queued up, but it was part of a larger change. I've pulled it out and > >> the patch is below, along with your test change. The test still passes. > >> > >> There's no commit message yet, I still need to think about exactly > >> what's going on in this case a bit more, it's still not clear to me how > >> (or where) the frame_info_ptr without the frame-id is actually created, > >> as I thought frames (after 0) always got a frame-id before they were > >> placed into a frame_info_ptr. > >> > >> I'm sharing this now just so you can take a look at the change and let > >> me know what you think, I'll dig into this a little more and write up a > >> commit message tomorrow, but I'd like to hear what you think of this > >> approach. > >> > >> Thanks, > >> Andrew > >> > >> --- > >> > >> diff --git a/gdb/frame.c b/gdb/frame.c > >> index cefdde5ed1e..7a0f313f4db 100644 > >> --- a/gdb/frame.c > >> +++ b/gdb/frame.c > >> @@ -2190,15 +2190,15 @@ reinit_frame_cache (void) > >> sentinel_frame = nullptr; > >> } > >> > >> + for (frame_info_ptr &iter : frame_info_ptr::frame_list) > >> + iter.invalidate (); > >> + > > > > I see why we need this, but it's worth a comment that the invalidate > > loop must run before invalidating the frame stash. > > Added in my next version. > > > > >> frame_stash_invalidate (); > >> > >> /* Since we can't really be sure what the first object allocated was. */ > >> obstack_free (&frame_cache_obstack, 0); > >> obstack_init (&frame_cache_obstack); > >> > >> - for (frame_info_ptr &iter : frame_info_ptr::frame_list) > >> - iter.invalidate (); > >> - > >> frame_debug_printf ("generation=%d", frame_cache_generation); > >> } > >> > >> @@ -3435,10 +3435,21 @@ frame_info_ptr::frame_info_ptr (struct > >> frame_info *ptr) > >> if (m_ptr == nullptr) > >> return; > >> > >> - m_cached_level = ptr->level; > >> + m_cached_level = m_ptr->level; > > > > Do we need this? At this point, we should have m_ptr == ptr? > > I'll revert this for the final patch. > > > > >> +} > >> + > >> +void > >> +frame_info_ptr::invalidate () > >> +{ > >> + if (m_ptr == nullptr) > >> + return; > >> + > >> + gdb_assert (m_cached_level == m_ptr->level); > >> > >> if (m_cached_level != 0 || m_ptr->this_id.value.user_created_p) > >> m_cached_id = m_ptr->this_id.value; > > > > I was wondering if we should add another guard: > > > > if ((m_cached_id == null_frame_id) > > && (m_cached_level != 0 || m_ptr->this_id.value.user_created_p)) > > > > Or is this implicitly guarded by the 'm_cached_level != 0' check? > > Comparing to null_frame_id always returns false, see > frame_id::operator== for details. > > If we _could_ compare to null_frame_id then we could add this assert: > > gdb_assert (m_cached_id == null_frame_id > || m_cached_id == m_ptr->this_id.value); > > This means that, if a frame_info_ptr is invalidated multiple times, then > the m_cached_id will be updated multiple times, but it should never > change. But, as we cannot compare to null_frame_id, I'm not sure how > we'd write that assert. Given that we check the frame-id matches when > we reinflate though, I'm not sure the above assert adds much. > > Your proposal (if we could compare to null_frame_id) would prevent the > repeated updates, but isn't needed to prevent any invalid behaviour, for > the same reason, the frame-id should never be different. > > The updated patch is below, let me know what you think. > > Thanks, > Andrew > > --- > > commit d13aa44f8ac57e52324da409145f73a0e3ca19a2 > Author: Andrew Burgess <aburgess@redhat.com> > Date: Thu Jun 25 14:58:50 2026 +0000 > > gdb: set frame_info_ptr::m_cached_id in the destructor I think this is misleading as it's not updated in the dtor but when the frame_info_ptr is invalidated. > > Currently frame_info_ptr caches the frame_id at construction time, see > frame_info_ptr::frame_info_ptr in frame.c. The problem with this is > that a frame's frame-id might not be known at this point. > > Consider get_prev_frame_maybe_check_cycle, this calls > get_prev_frame_raw to create the previous frame, placing the result > into a frame_info_ptr PREV_FRAME. Then (for frames other than frame > 0) compute_frame_id is called, however, this only computes the > frame_id for the frame_info object pointed to by the frame_info_ptr, > the cached frame_id within the frame_info_ptr itself is not updated. > > What this means is that in get_prev_frame_maybe_check_cycle, the > PREV_FRAME local has no cached frame-id. > > If we consider the call stack: > > get_selected_frame > lookup_selected_frame > frame_find_by_id > get_prev_frame > get_prev_frame_always > get_prev_frame_always_1 > get_prev_frame_maybe_check_cycle > > Then what we see is that the frame_info_ptr created in > get_prev_frame_maybe_check_cycle, which lacks a cached frame_id, can > be passed all the way back to lookup_selected_frame, where it will be > stored in the SELECTED_FRAME global by a call to select_frame. The > outer get_selected_frame call (in the above backtrace) will then > return the SELECTED_FRAME global, which lacks a cached frame-id. > > If GDB ever tries to reinflate the SELECTED_FRAME frame_info_ptr (or a > copy of it), then we will trigger the assert: > `gdb_assert (frame_id_p (m_cached_id));` which can be found in > `frame_info_ptr::reinflate` in frame.c. > > An example of how this can be triggered is included in the updated > test case, frame #1 is selected and the frame is printed. The pretty > printer performs an inferior call which invalidates the frame cache, > the assertion then triggers when trying to reinflate the selected > frame frame_info_ptr. > We can put a bit more detail here, e.g.: An example of how this can be triggered is included in the updated test case: - The 'up' command sets the selected frame to a frame with level > 0. - An inferior call invalidates the selected frame. - The selected frame is rebuild following the call-stack above. The wrapping frame_info_ptr object doesn't cache the frame-id. - If the frame is printed with the 'frame' command, this invokes another inferior call for the pretty printer, which flushes the frame cache. - If the frame_info_ptr is reinflated, this hits the assertion mentioned above, e.g., to print the next argument. > The problem is that frame's don't always know their frame-id when they > are placed into a frame_info_ptr, but they always do (for frame other > than #0) after get_prev_frame_maybe_check_cycle has finished. We > could try to have get_prev_frame_maybe_check_cycle or > compute_frame_id > push the computed frame-id into the frame_info_ptr, or we can just > defer caching the frame-id until we know we might need it, i.e. when > the frame cache is being flushed. > > This second approach is actually nice in that it defers the work until > we know we need it, and frame_info_ptr objects that are created and > destroyed without the frame cache ever being flushed no longer need to > cache the frame-id. This isn't going to give any noticable > performance improvement, but still, it feels nice. > I think we don't need to explain the two options here. Maybe add one sentence summarizing the change, e.g.: Update the cache-id of the frame_info_ptr before removing flushing the frame-cache. > The changes in this commit then are: > > 1. In reinit_frame_cache we call frame_info_ptr::invalidate before > deleting all the frame_info objects (by clearing the obstacks), > this allows us to copy the frame_id from these objects. > > 2. In frame_info_ptr::frame_info_ptr we still need to record every > frame_info_ptr in the global list, and for now at least, we still > cache the frame level, this is needed for > frame_info_ptr::is_null, which checks the cached level. > > 3. In frame_info::invalidate, we can assert that the cached level > matches the stored frame's level, this should never change, and > it is here that we now cache the frame-id. > I think we don't need items 1. to 3. as part of the commit message. > Co-Authored-By: Rohr, Stephan <stephan.rohr@intel.com> > > diff --git a/gdb/frame.c b/gdb/frame.c > index cefdde5ed1e..e7974060cb8 100644 > --- a/gdb/frame.c > +++ b/gdb/frame.c > @@ -2190,15 +2190,18 @@ reinit_frame_cache (void) > sentinel_frame = nullptr; > } > > + /* Invalidation copies the frame-id from the managed frame_info object > + into the frame_info_ptr, so this must run before the frame_info > + objects are invalidated. */ > + for (frame_info_ptr &iter : frame_info_ptr::frame_list) > + iter.invalidate (); > + > frame_stash_invalidate (); > > /* Since we can't really be sure what the first object allocated was. */ > obstack_free (&frame_cache_obstack, 0); > obstack_init (&frame_cache_obstack); > > - for (frame_info_ptr &iter : frame_info_ptr::frame_list) > - iter.invalidate (); > - > frame_debug_printf ("generation=%d", frame_cache_generation); > } > > @@ -3436,9 +3439,23 @@ frame_info_ptr::frame_info_ptr (struct > frame_info *ptr) > return; > > m_cached_level = ptr->level; > +} > > +void > +frame_info_ptr::invalidate () > +{ > + if (m_ptr == nullptr) > + return; > + > + gdb_assert (m_cached_level == m_ptr->level); > + > + /* If a frame_info_ptr is invalidated multiple times then we will end up > + updating m_cached_id multiple times. This should be harmless as the > + underlying frame_id should never change. */ > if (m_cached_level != 0 || m_ptr->this_id.value.user_created_p) > m_cached_id = m_ptr->this_id.value; > + > + m_ptr = nullptr; > } > > /* See frame-info-ptr.h. */ > diff --git a/gdb/frame.h b/gdb/frame.h > index f6553fb7b6d..b386303d895 100644 > --- a/gdb/frame.h > +++ b/gdb/frame.h > @@ -327,10 +327,7 @@ class frame_info_ptr : public > intrusive_list_node<frame_info_ptr> > } > > /* Invalidate this pointer. */ > - void invalidate () > - { > - m_ptr = nullptr; > - } > + void invalidate (); > > private: > /* We sometimes need to construct frame_info_ptr objects around the > diff --git a/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp > b/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp > index 52162fc9952..a2a29c4d0f8 100644 > --- a/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp > +++ b/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp > @@ -108,6 +108,8 @@ with_test_prefix "frame movement down" { > with_test_prefix "frame movement up" { > if { [start_test "TAG: final frame"] == 0 } { > gdb_test "up" [multi_line "#1 .*in g \\(mt=mytype is .*\\, > depth=1\\).*" ".*first frame.*"] > + gdb_test "p f ()" " = 2" > + gdb_test "frame" [multi_line "#1 .*in g \\(mt=mytype is .*\\, > depth=1\\).*" ".*first frame.*"] > } > } > Intel Deutschland GmbH Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany Tel: +49 89 991 430, www.intel.de Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell Chairperson of the Supervisory Board: Nicole Lau Registered Seat: Munich Commercial Register: Amtsgericht Muenchen HRB 186928 ^ permalink raw reply [flat|nested] 17+ messages in thread
* RE: [PATCH 1/1] gdb: set the cache information in 'get_prev_frame_maybe_check_cycle' 2026-07-08 8:46 ` Rohr, Stephan @ 2026-07-08 13:59 ` Andrew Burgess 2026-07-08 15:18 ` Rohr, Stephan 0 siblings, 1 reply; 17+ messages in thread From: Andrew Burgess @ 2026-07-08 13:59 UTC (permalink / raw) To: Rohr, Stephan; +Cc: gdb-patches, Tom Tromey "Rohr, Stephan" <stephan.rohr@intel.com> writes: > HI Andrew, > > Thanks for sharing. The patch itself looks good. > > I have a few comments regarding the commit message, see below. Thanks Stephan. Below is an updated patch with an improved commit message. I also tweaked some of the comments in the actual code as, upon re-reading, I found some of them not ideal. Let me know what you think. Thanks, Andrew --- commit cec35caa5c173de5e98a9ac6cbc0b8ee8e15d912 Author: Andrew Burgess <aburgess@redhat.com> Date: Thu Jun 25 14:58:50 2026 +0000 gdb: set frame_info_ptr::m_cached_id during invalidation Currently frame_info_ptr caches the frame_id at construction time, see frame_info_ptr::frame_info_ptr in frame.c. The problem with this is that a frame's frame-id might not be known at this point. Consider get_prev_frame_maybe_check_cycle, this calls get_prev_frame_raw to create the previous frame, placing the result into a frame_info_ptr PREV_FRAME. Then (for frames other than frame 0) compute_frame_id is called, however, this only computes the frame_id for the frame_info object pointed to by the frame_info_ptr, the cached frame_id within the frame_info_ptr itself is not updated. What this means is that in get_prev_frame_maybe_check_cycle, the PREV_FRAME local has no cached frame-id. If we consider the call stack: get_selected_frame lookup_selected_frame frame_find_by_id get_prev_frame get_prev_frame_always get_prev_frame_always_1 get_prev_frame_maybe_check_cycle Then what we see is that the frame_info_ptr created in get_prev_frame_maybe_check_cycle, which lacks a cached frame_id, can be passed all the way back to lookup_selected_frame, where it will be stored in the SELECTED_FRAME global by a call to select_frame. The outer get_selected_frame call (in the above backtrace) will then return the SELECTED_FRAME global, which lacks a cached frame-id. If GDB ever tries to reinflate the SELECTED_FRAME frame_info_ptr (or a copy of it), then we will trigger the assert: `gdb_assert (frame_id_p (m_cached_id));` which can be found in `frame_info_ptr::reinflate` in frame.c. An example of how this can be triggered is included in the updated test case: - The 'up' command sets the selected frame to a frame with level > 0. - An inferior call invalidates the selected frame. - The selected frame is rebuilt following the call-stack above. The wrapping frame_info_ptr object doesn't cache the frame-id. - The 'frame' command invokes another inferior call for the pretty printer, which flushes the frame cache. - The frame_info_ptr is reinflated, e.g., to print the next argument, and this hits the assertion mentioned above. The problem is that frames don't always know their frame-id when they are placed into a frame_info_ptr, but they always do (for frames other than #0) after get_prev_frame_maybe_check_cycle has finished. This commit defers caching the frame-id in the frame_info_ptr until the frame cache is being flushed, at which point the frame-id is known. Co-Authored-By: Rohr, Stephan <stephan.rohr@intel.com> diff --git a/gdb/frame.c b/gdb/frame.c index cefdde5ed1e..a0a982dc866 100644 --- a/gdb/frame.c +++ b/gdb/frame.c @@ -2190,15 +2190,18 @@ reinit_frame_cache (void) sentinel_frame = nullptr; } + /* Invalidation copies the frame-id from the managed frame_info object + into the frame_info_ptr, so this must run before the frame_info + objects are freed (via obstack_free). */ + for (frame_info_ptr &iter : frame_info_ptr::frame_list) + iter.invalidate (); + frame_stash_invalidate (); /* Since we can't really be sure what the first object allocated was. */ obstack_free (&frame_cache_obstack, 0); obstack_init (&frame_cache_obstack); - for (frame_info_ptr &iter : frame_info_ptr::frame_list) - iter.invalidate (); - frame_debug_printf ("generation=%d", frame_cache_generation); } @@ -3436,9 +3439,24 @@ frame_info_ptr::frame_info_ptr (struct frame_info *ptr) return; m_cached_level = ptr->level; +} +void +frame_info_ptr::invalidate () +{ + if (m_ptr == nullptr) + return; + + gdb_assert (m_cached_level == m_ptr->level); + + /* If a frame_info_ptr is invalidated multiple times (with a reinflate + call between) then we will end up updating m_cached_id multiple times. + This should be harmless as the underlying frame_id should never + change. */ if (m_cached_level != 0 || m_ptr->this_id.value.user_created_p) m_cached_id = m_ptr->this_id.value; + + m_ptr = nullptr; } /* See frame-info-ptr.h. */ diff --git a/gdb/frame.h b/gdb/frame.h index f6553fb7b6d..b386303d895 100644 --- a/gdb/frame.h +++ b/gdb/frame.h @@ -327,10 +327,7 @@ class frame_info_ptr : public intrusive_list_node<frame_info_ptr> } /* Invalidate this pointer. */ - void invalidate () - { - m_ptr = nullptr; - } + void invalidate (); private: /* We sometimes need to construct frame_info_ptr objects around the diff --git a/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp b/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp index 52162fc9952..a2a29c4d0f8 100644 --- a/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp +++ b/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp @@ -108,6 +108,8 @@ with_test_prefix "frame movement down" { with_test_prefix "frame movement up" { if { [start_test "TAG: final frame"] == 0 } { gdb_test "up" [multi_line "#1 .*in g \\(mt=mytype is .*\\, depth=1\\).*" ".*first frame.*"] + gdb_test "p f ()" " = 2" + gdb_test "frame" [multi_line "#1 .*in g \\(mt=mytype is .*\\, depth=1\\).*" ".*first frame.*"] } } ^ permalink raw reply [flat|nested] 17+ messages in thread
* RE: [PATCH 1/1] gdb: set the cache information in 'get_prev_frame_maybe_check_cycle' 2026-07-08 13:59 ` Andrew Burgess @ 2026-07-08 15:18 ` Rohr, Stephan 2026-07-14 15:14 ` Andrew Burgess 0 siblings, 1 reply; 17+ messages in thread From: Rohr, Stephan @ 2026-07-08 15:18 UTC (permalink / raw) To: Andrew Burgess; +Cc: gdb-patches, Tom Tromey Hi Andrew, Two more nits for the commit message, see my comment bellow. Thanks Stephan > -----Original Message----- > From: Andrew Burgess <aburgess@redhat.com> > Sent: Wednesday, 8 July 2026 15:59 > To: Rohr, Stephan <stephan.rohr@intel.com> > Cc: gdb-patches@sourceware.org; Tom Tromey <tom@tromey.com> > Subject: RE: [PATCH 1/1] gdb: set the cache information in > 'get_prev_frame_maybe_check_cycle' > > "Rohr, Stephan" <stephan.rohr@intel.com> writes: > > > HI Andrew, > > > > Thanks for sharing. The patch itself looks good. > > > > I have a few comments regarding the commit message, see below. > > Thanks Stephan. Below is an updated patch with an improved commit > message. I also tweaked some of the comments in the actual code as, > upon re-reading, I found some of them not ideal. > > Let me know what you think. > > Thanks, > Andrew > > --- > > commit cec35caa5c173de5e98a9ac6cbc0b8ee8e15d912 > Author: Andrew Burgess <aburgess@redhat.com> > Date: Thu Jun 25 14:58:50 2026 +0000 > > gdb: set frame_info_ptr::m_cached_id during invalidation > > Currently frame_info_ptr caches the frame_id at construction time, see > frame_info_ptr::frame_info_ptr in frame.c. The problem with this is > that a frame's frame-id might not be known at this point. > > Consider get_prev_frame_maybe_check_cycle, this calls > get_prev_frame_raw to create the previous frame, placing the result > into a frame_info_ptr PREV_FRAME. Then (for frames other than frame > 0) compute_frame_id is called, however, this only computes the > frame_id for the frame_info object pointed to by the frame_info_ptr, > the cached frame_id within the frame_info_ptr itself is not updated. > This is a long sentence. Consider splitting into two to improve readability. > What this means is that in get_prev_frame_maybe_check_cycle, the > PREV_FRAME local has no cached frame-id. > > If we consider the call stack: > > get_selected_frame > lookup_selected_frame > frame_find_by_id > get_prev_frame > get_prev_frame_always > get_prev_frame_always_1 > get_prev_frame_maybe_check_cycle > > Then what we see is that the frame_info_ptr created in > get_prev_frame_maybe_check_cycle, which lacks a cached frame_id, can > be passed all the way back to lookup_selected_frame, where it will be > stored in the SELECTED_FRAME global by a call to select_frame. The > outer get_selected_frame call (in the above backtrace) will then > return the SELECTED_FRAME global, which lacks a cached frame-id. > > If GDB ever tries to reinflate the SELECTED_FRAME frame_info_ptr (or a > copy of it), then we will trigger the assert: > `gdb_assert (frame_id_p (m_cached_id));` which can be found in > `frame_info_ptr::reinflate` in frame.c. > > An example of how this can be triggered is included in the updated > test case: > > - The 'up' command sets the selected frame to a frame with > level > 0. > - An inferior call invalidates the selected frame. > - The selected frame is rebuilt following the call-stack above. > The wrapping frame_info_ptr object doesn't cache the frame-id. > - The 'frame' command invokes another inferior call for the pretty > printer, which flushes the frame cache. > - The frame_info_ptr is reinflated, e.g., to print the next > argument, and this hits the assertion mentioned above. > > The problem is that frames don't always know their frame-id when they > are placed into a frame_info_ptr, but they always do (for frames other > than #0) after get_prev_frame_maybe_check_cycle has finished. This > commit defers caching the frame-id in the frame_info_ptr until the > frame cache is being flushed, at which point the frame-id is known. > I think we shouldn't focus on 'get_prev_frame_maybe_check_cycle' solely, though I didn't find any other location where the described behaviour could reproduce (which doesn't mean it doesn't exist). Maybe generalize a bit more, e.g.: The problem is that a frame_info_ptr relies on a computed frame-id upon construction time to cache the frame-id. This commit defers caching the frame-id in the frame_info_ptr until the frame cache is being flushed, at which point the frame-id is known. I'm not sure if 'defer' is the correct wording as the frame_info_ptr ctor still sets the frame-id. Maybe change to 'updates the frame-id'. > Co-Authored-By: Rohr, Stephan <stephan.rohr@intel.com> > > diff --git a/gdb/frame.c b/gdb/frame.c > index cefdde5ed1e..a0a982dc866 100644 > --- a/gdb/frame.c > +++ b/gdb/frame.c > @@ -2190,15 +2190,18 @@ reinit_frame_cache (void) > sentinel_frame = nullptr; > } > > + /* Invalidation copies the frame-id from the managed frame_info object > + into the frame_info_ptr, so this must run before the frame_info > + objects are freed (via obstack_free). */ > + for (frame_info_ptr &iter : frame_info_ptr::frame_list) > + iter.invalidate (); > + > frame_stash_invalidate (); > > /* Since we can't really be sure what the first object allocated was. */ > obstack_free (&frame_cache_obstack, 0); > obstack_init (&frame_cache_obstack); > > - for (frame_info_ptr &iter : frame_info_ptr::frame_list) > - iter.invalidate (); > - > frame_debug_printf ("generation=%d", frame_cache_generation); > } > > @@ -3436,9 +3439,24 @@ frame_info_ptr::frame_info_ptr (struct > frame_info *ptr) > return; > > m_cached_level = ptr->level; > +} > > +void > +frame_info_ptr::invalidate () > +{ > + if (m_ptr == nullptr) > + return; > + > + gdb_assert (m_cached_level == m_ptr->level); > + > + /* If a frame_info_ptr is invalidated multiple times (with a reinflate > + call between) then we will end up updating m_cached_id multiple times. > + This should be harmless as the underlying frame_id should never > + change. */ > if (m_cached_level != 0 || m_ptr->this_id.value.user_created_p) > m_cached_id = m_ptr->this_id.value; > + > + m_ptr = nullptr; > } > > /* See frame-info-ptr.h. */ > diff --git a/gdb/frame.h b/gdb/frame.h > index f6553fb7b6d..b386303d895 100644 > --- a/gdb/frame.h > +++ b/gdb/frame.h > @@ -327,10 +327,7 @@ class frame_info_ptr : public > intrusive_list_node<frame_info_ptr> > } > > /* Invalidate this pointer. */ > - void invalidate () > - { > - m_ptr = nullptr; > - } > + void invalidate (); > > private: > /* We sometimes need to construct frame_info_ptr objects around the > diff --git a/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp > b/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp > index 52162fc9952..a2a29c4d0f8 100644 > --- a/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp > +++ b/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp > @@ -108,6 +108,8 @@ with_test_prefix "frame movement down" { > with_test_prefix "frame movement up" { > if { [start_test "TAG: final frame"] == 0 } { > gdb_test "up" [multi_line "#1 .*in g \\(mt=mytype is .*\\, > depth=1\\).*" ".*first frame.*"] > + gdb_test "p f ()" " = 2" > + gdb_test "frame" [multi_line "#1 .*in g \\(mt=mytype is .*\\, > depth=1\\).*" ".*first frame.*"] > } > } > Intel Deutschland GmbH Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany Tel: +49 89 991 430, www.intel.de Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell Chairperson of the Supervisory Board: Nicole Lau Registered Seat: Munich Commercial Register: Amtsgericht Muenchen HRB 186928 ^ permalink raw reply [flat|nested] 17+ messages in thread
* RE: [PATCH 1/1] gdb: set the cache information in 'get_prev_frame_maybe_check_cycle' 2026-07-08 15:18 ` Rohr, Stephan @ 2026-07-14 15:14 ` Andrew Burgess 2026-07-15 7:33 ` Rohr, Stephan 0 siblings, 1 reply; 17+ messages in thread From: Andrew Burgess @ 2026-07-14 15:14 UTC (permalink / raw) To: Rohr, Stephan; +Cc: gdb-patches, Tom Tromey "Rohr, Stephan" <stephan.rohr@intel.com> writes: > Hi Andrew, > > Two more nits for the commit message, see my comment bellow. Hi Stephan, Sorry for the delay, I took some time to think about one of the issue you raised before responding. > >> -----Original Message----- >> From: Andrew Burgess <aburgess@redhat.com> >> Sent: Wednesday, 8 July 2026 15:59 >> To: Rohr, Stephan <stephan.rohr@intel.com> >> Cc: gdb-patches@sourceware.org; Tom Tromey <tom@tromey.com> >> Subject: RE: [PATCH 1/1] gdb: set the cache information in >> 'get_prev_frame_maybe_check_cycle' >> >> "Rohr, Stephan" <stephan.rohr@intel.com> writes: >> >> > HI Andrew, >> > >> > Thanks for sharing. The patch itself looks good. >> > >> > I have a few comments regarding the commit message, see below. >> >> Thanks Stephan. Below is an updated patch with an improved commit >> message. I also tweaked some of the comments in the actual code as, >> upon re-reading, I found some of them not ideal. >> >> Let me know what you think. >> >> Thanks, >> Andrew >> >> --- >> >> commit cec35caa5c173de5e98a9ac6cbc0b8ee8e15d912 >> Author: Andrew Burgess <aburgess@redhat.com> >> Date: Thu Jun 25 14:58:50 2026 +0000 >> >> gdb: set frame_info_ptr::m_cached_id during invalidation >> >> Currently frame_info_ptr caches the frame_id at construction time, see >> frame_info_ptr::frame_info_ptr in frame.c. The problem with this is >> that a frame's frame-id might not be known at this point. >> >> Consider get_prev_frame_maybe_check_cycle, this calls >> get_prev_frame_raw to create the previous frame, placing the result >> into a frame_info_ptr PREV_FRAME. Then (for frames other than frame >> 0) compute_frame_id is called, however, this only computes the >> frame_id for the frame_info object pointed to by the frame_info_ptr, >> the cached frame_id within the frame_info_ptr itself is not updated. >> > > This is a long sentence. Consider splitting into two to improve readability. > I've reworked this, see the updated commit message below. >> What this means is that in get_prev_frame_maybe_check_cycle, the >> PREV_FRAME local has no cached frame-id. >> >> If we consider the call stack: >> >> get_selected_frame >> lookup_selected_frame >> frame_find_by_id >> get_prev_frame >> get_prev_frame_always >> get_prev_frame_always_1 >> get_prev_frame_maybe_check_cycle >> >> Then what we see is that the frame_info_ptr created in >> get_prev_frame_maybe_check_cycle, which lacks a cached frame_id, can >> be passed all the way back to lookup_selected_frame, where it will be >> stored in the SELECTED_FRAME global by a call to select_frame. The >> outer get_selected_frame call (in the above backtrace) will then >> return the SELECTED_FRAME global, which lacks a cached frame-id. >> >> If GDB ever tries to reinflate the SELECTED_FRAME frame_info_ptr (or a >> copy of it), then we will trigger the assert: >> `gdb_assert (frame_id_p (m_cached_id));` which can be found in >> `frame_info_ptr::reinflate` in frame.c. >> >> An example of how this can be triggered is included in the updated >> test case: >> >> - The 'up' command sets the selected frame to a frame with >> level > 0. >> - An inferior call invalidates the selected frame. >> - The selected frame is rebuilt following the call-stack above. >> The wrapping frame_info_ptr object doesn't cache the frame-id. >> - The 'frame' command invokes another inferior call for the pretty >> printer, which flushes the frame cache. >> - The frame_info_ptr is reinflated, e.g., to print the next >> argument, and this hits the assertion mentioned above. >> >> The problem is that frames don't always know their frame-id when they >> are placed into a frame_info_ptr, but they always do (for frames other >> than #0) after get_prev_frame_maybe_check_cycle has finished. This >> commit defers caching the frame-id in the frame_info_ptr until the >> frame cache is being flushed, at which point the frame-id is known. >> > > I think we shouldn't focus on 'get_prev_frame_maybe_check_cycle' solely, though > I didn't find any other location where the described behaviour could reproduce > (which doesn't mean it doesn't exist). I think in GDB right now get_prev_frame_maybe_check_cycle is the only place this bug exists. There are 3 places where new frame_info objects are created: create_sentinel_frame, create_new_frame, and get_prev_frame_raw. In the first two of these the frame_info is assigned an ID before being placed into the frame_info_ptr, so these are not problems. Only in get_prev_frame_raw is the frame_info placed into a frame_info_ptr before having an ID assigned, and that is only called from get_prev_frame_maybe_check_cycle. As we discussed in another thread, an ideal solution would be to have get_prev_frame_raw not create the frame_info_ptr at all, and defer this until get_prev_frame_maybe_check_cycle has finished, but this would require changing the frame sniffer API to not expect a frame_info_ptr, which seems like a bigger change than I'd like to make right now. But as I wrote the above I'm finding it harder to justify the churn of moving the frame_id caching into the frame_info_ptr::invalidate method. So, sorry to pivot again, but how about the patch below? It's far simpler than the original suggestion and is targets just get_prev_frame_maybe_check_cycle, which is where the broken frame_info_ptr objects always come from. Let me know what you think. Thanks, Andrew -- commit b6dba04e0e21f1a83b43ca616ae5fcce1a66eb9b Author: Andrew Burgess <aburgess@redhat.com> Date: Thu Jun 25 14:58:50 2026 +0000 gdb: recreate the frame_info_ptr in get_prev_frame_maybe_check_cycle Currently frame_info_ptr caches the frame_id at construction time, see frame_info_ptr::frame_info_ptr in frame.c. The problem with this is that a frame's frame-id might not be known at this point. The function get_prev_frame_maybe_check_cycle calls get_prev_frame_raw to create the previous frame, placing the result into a frame_info_ptr PREV_FRAME. For frames other than frame 0, compute_frame_id is then called computing the frame-id. However, the call to compute_frame_id only updates the frame_info object itself, the frame_info_ptr PREV_FRAME is not updated with the new frame-id. What this means is that in get_prev_frame_maybe_check_cycle, the PREV_FRAME local has no cached frame-id. Consider the call stack: get_selected_frame lookup_selected_frame frame_find_by_id get_prev_frame get_prev_frame_always get_prev_frame_always_1 get_prev_frame_maybe_check_cycle What we see is that the frame_info_ptr created in get_prev_frame_maybe_check_cycle, which lacks a cached frame_id, can be passed all the way back to lookup_selected_frame, where it will be stored in the SELECTED_FRAME global by a call to select_frame. The outer get_selected_frame call (in the above backtrace) will then return the SELECTED_FRAME global, which lacks a cached frame-id. If GDB ever tries to reinflate the SELECTED_FRAME frame_info_ptr (or a copy of it), then we will trigger the assert: `gdb_assert (frame_id_p (m_cached_id));` which can be found in `frame_info_ptr::reinflate` in frame.c. An example of how this can be triggered is included in the updated test case: - The 'up' command sets the selected frame to a frame with level > 0. - An inferior call invalidates the selected frame. - The selected frame is rebuilt following the call-stack above. The wrapping frame_info_ptr object doesn't cache the frame-id. - The 'frame' command invokes another inferior call for the pretty printer, which flushes the frame cache. - The frame_info_ptr is reinflated, e.g., to print the next argument, and this hits the assertion mentioned above. There are only 3 places in GDB where new frame_info objects are created: create_sentinel_frame, create_new_frame, and get_prev_frame_raw. Of these, the first two always calculate the frame_id before placing the frame_info object into a frame_info_ptr. Only get_prev_frame_raw, which is only called from get_prev_frame_maybe_check_cycle, creates the frame_info_ptr before the frame_id is calculated. There are two places where PREV_FRAME is returned from get_prev_frame_maybe_check_cycle. The first is only for frame #0. The frame_info_ptr::reinflate method doesn't need a frame_id for frame #0, so the first return is not a problem. The second return from get_prev_frame_maybe_check_cycle is done after the frame_id has been calculated, and it is here that the problem can be fixed. If we create a new frame_info_ptr to replace PREV_FRAME then this new frame_info_ptr will have a cached frame_id and the problem described above will no longer occur. Co-Authored-By: Rohr, Stephan <stephan.rohr@intel.com> diff --git a/gdb/frame.c b/gdb/frame.c index cefdde5ed1e..b91e18fad99 100644 --- a/gdb/frame.c +++ b/gdb/frame.c @@ -2332,7 +2332,16 @@ get_prev_frame_maybe_check_cycle (const frame_info_ptr &this_frame) throw; } - return prev_frame; + /* When PREV_FRAME was initially created it had no cached frame_id as the + frame_id had not yet been computed. Without a frame_id however + PREV_FRAME will not be able to reinflate. Recreate the frame_info_ptr + now that the frame_id is known, this new frame_info_ptr will have a + cached frame_id. + + You might wonder about the earlier return of PREV_FRAME within the + function. That is fine as reinflating a frame_info_ptr at level 0 + doesn't require a cached frame_id. */ + return frame_info_ptr (prev_frame.get ()); } /* Helper function for get_prev_frame_always, this is called inside a diff --git a/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp b/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp index 52162fc9952..a2a29c4d0f8 100644 --- a/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp +++ b/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp @@ -108,6 +108,8 @@ with_test_prefix "frame movement down" { with_test_prefix "frame movement up" { if { [start_test "TAG: final frame"] == 0 } { gdb_test "up" [multi_line "#1 .*in g \\(mt=mytype is .*\\, depth=1\\).*" ".*first frame.*"] + gdb_test "p f ()" " = 2" + gdb_test "frame" [multi_line "#1 .*in g \\(mt=mytype is .*\\, depth=1\\).*" ".*first frame.*"] } } ^ permalink raw reply [flat|nested] 17+ messages in thread
* RE: [PATCH 1/1] gdb: set the cache information in 'get_prev_frame_maybe_check_cycle' 2026-07-14 15:14 ` Andrew Burgess @ 2026-07-15 7:33 ` Rohr, Stephan 2026-07-18 11:57 ` Andrew Burgess 0 siblings, 1 reply; 17+ messages in thread From: Rohr, Stephan @ 2026-07-15 7:33 UTC (permalink / raw) To: Andrew Burgess; +Cc: gdb-patches, Tom Tromey Hi Andrew, thanks for getting back with that. This was also my first approach to fix the issue. The only limitation I see is that we have a duplicate entry into the frame_list when we setup the return value. When the dtor of prev_frame is called, it is removed again. I see this the only side effect of the patch. I'm personally fine with the proposed change as this is a minimal fix for the bug. I agree that a perfect fix would defer the frame_info_ptr creation until the frame-id is computed, but I think this is out of scope for this patch. Thanks Stephan > -----Original Message----- > From: Andrew Burgess <aburgess@redhat.com> > Sent: Tuesday, 14 July 2026 17:14 > To: Rohr, Stephan <stephan.rohr@intel.com> > Cc: gdb-patches@sourceware.org; Tom Tromey <tom@tromey.com> > Subject: RE: [PATCH 1/1] gdb: set the cache information in > 'get_prev_frame_maybe_check_cycle' > > "Rohr, Stephan" <stephan.rohr@intel.com> writes: > > > Hi Andrew, > > > > Two more nits for the commit message, see my comment bellow. > > Hi Stephan, > > Sorry for the delay, I took some time to think about one of the issue > you raised before responding. > > > >> -----Original Message----- > >> From: Andrew Burgess <aburgess@redhat.com> > >> Sent: Wednesday, 8 July 2026 15:59 > >> To: Rohr, Stephan <stephan.rohr@intel.com> > >> Cc: gdb-patches@sourceware.org; Tom Tromey <tom@tromey.com> > >> Subject: RE: [PATCH 1/1] gdb: set the cache information in > >> 'get_prev_frame_maybe_check_cycle' > >> > >> "Rohr, Stephan" <stephan.rohr@intel.com> writes: > >> > >> > HI Andrew, > >> > > >> > Thanks for sharing. The patch itself looks good. > >> > > >> > I have a few comments regarding the commit message, see below. > >> > >> Thanks Stephan. Below is an updated patch with an improved commit > >> message. I also tweaked some of the comments in the actual code as, > >> upon re-reading, I found some of them not ideal. > >> > >> Let me know what you think. > >> > >> Thanks, > >> Andrew > >> > >> --- > >> > >> commit cec35caa5c173de5e98a9ac6cbc0b8ee8e15d912 > >> Author: Andrew Burgess <aburgess@redhat.com> > >> Date: Thu Jun 25 14:58:50 2026 +0000 > >> > >> gdb: set frame_info_ptr::m_cached_id during invalidation > >> > >> Currently frame_info_ptr caches the frame_id at construction time, see > >> frame_info_ptr::frame_info_ptr in frame.c. The problem with this is > >> that a frame's frame-id might not be known at this point. > >> > >> Consider get_prev_frame_maybe_check_cycle, this calls > >> get_prev_frame_raw to create the previous frame, placing the result > >> into a frame_info_ptr PREV_FRAME. Then (for frames other than frame > >> 0) compute_frame_id is called, however, this only computes the > >> frame_id for the frame_info object pointed to by the frame_info_ptr, > >> the cached frame_id within the frame_info_ptr itself is not updated. > >> > > > > This is a long sentence. Consider splitting into two to improve readability. > > > > I've reworked this, see the updated commit message below. > > > >> What this means is that in get_prev_frame_maybe_check_cycle, the > >> PREV_FRAME local has no cached frame-id. > >> > >> If we consider the call stack: > >> > >> get_selected_frame > >> lookup_selected_frame > >> frame_find_by_id > >> get_prev_frame > >> get_prev_frame_always > >> get_prev_frame_always_1 > >> get_prev_frame_maybe_check_cycle > >> > >> Then what we see is that the frame_info_ptr created in > >> get_prev_frame_maybe_check_cycle, which lacks a cached frame_id, can > >> be passed all the way back to lookup_selected_frame, where it will be > >> stored in the SELECTED_FRAME global by a call to select_frame. The > >> outer get_selected_frame call (in the above backtrace) will then > >> return the SELECTED_FRAME global, which lacks a cached frame-id. > >> > >> If GDB ever tries to reinflate the SELECTED_FRAME frame_info_ptr (or a > >> copy of it), then we will trigger the assert: > >> `gdb_assert (frame_id_p (m_cached_id));` which can be found in > >> `frame_info_ptr::reinflate` in frame.c. > >> > >> An example of how this can be triggered is included in the updated > >> test case: > >> > >> - The 'up' command sets the selected frame to a frame with > >> level > 0. > >> - An inferior call invalidates the selected frame. > >> - The selected frame is rebuilt following the call-stack above. > >> The wrapping frame_info_ptr object doesn't cache the frame-id. > >> - The 'frame' command invokes another inferior call for the pretty > >> printer, which flushes the frame cache. > >> - The frame_info_ptr is reinflated, e.g., to print the next > >> argument, and this hits the assertion mentioned above. > >> > >> The problem is that frames don't always know their frame-id when they > >> are placed into a frame_info_ptr, but they always do (for frames other > >> than #0) after get_prev_frame_maybe_check_cycle has finished. This > >> commit defers caching the frame-id in the frame_info_ptr until the > >> frame cache is being flushed, at which point the frame-id is known. > >> > > > > I think we shouldn't focus on 'get_prev_frame_maybe_check_cycle' solely, > though > > I didn't find any other location where the described behaviour could > reproduce > > (which doesn't mean it doesn't exist). > > I think in GDB right now get_prev_frame_maybe_check_cycle is the only > place this bug exists. > > There are 3 places where new frame_info objects are created: > create_sentinel_frame, create_new_frame, and get_prev_frame_raw. > > In the first two of these the frame_info is assigned an ID before being > placed into the frame_info_ptr, so these are not problems. > > Only in get_prev_frame_raw is the frame_info placed into a > frame_info_ptr before having an ID assigned, and that is only called > from get_prev_frame_maybe_check_cycle. > > As we discussed in another thread, an ideal solution would be to have > get_prev_frame_raw not create the frame_info_ptr at all, and defer this > until get_prev_frame_maybe_check_cycle has finished, but this would > require changing the frame sniffer API to not expect a frame_info_ptr, > which seems like a bigger change than I'd like to make right now. > > But as I wrote the above I'm finding it harder to justify the churn of > moving the frame_id caching into the frame_info_ptr::invalidate method. > > So, sorry to pivot again, but how about the patch below? It's far > simpler than the original suggestion and is targets just > get_prev_frame_maybe_check_cycle, which is where the broken > frame_info_ptr objects always come from. > > Let me know what you think. > > Thanks, > Andrew > > -- > > commit b6dba04e0e21f1a83b43ca616ae5fcce1a66eb9b > Author: Andrew Burgess <aburgess@redhat.com> > Date: Thu Jun 25 14:58:50 2026 +0000 > > gdb: recreate the frame_info_ptr in get_prev_frame_maybe_check_cycle > > Currently frame_info_ptr caches the frame_id at construction time, see > frame_info_ptr::frame_info_ptr in frame.c. The problem with this is > that a frame's frame-id might not be known at this point. > > The function get_prev_frame_maybe_check_cycle calls get_prev_frame_raw > to create the previous frame, placing the result into a frame_info_ptr > PREV_FRAME. For frames other than frame 0, compute_frame_id is then > called computing the frame-id. However, the call to compute_frame_id > only updates the frame_info object itself, the frame_info_ptr > PREV_FRAME is not updated with the new frame-id. > > What this means is that in get_prev_frame_maybe_check_cycle, the > PREV_FRAME local has no cached frame-id. > > Consider the call stack: > > get_selected_frame > lookup_selected_frame > frame_find_by_id > get_prev_frame > get_prev_frame_always > get_prev_frame_always_1 > get_prev_frame_maybe_check_cycle > > What we see is that the frame_info_ptr created in > get_prev_frame_maybe_check_cycle, which lacks a cached frame_id, can > be passed all the way back to lookup_selected_frame, where it will be > stored in the SELECTED_FRAME global by a call to select_frame. The > outer get_selected_frame call (in the above backtrace) will then > return the SELECTED_FRAME global, which lacks a cached frame-id. > > If GDB ever tries to reinflate the SELECTED_FRAME frame_info_ptr (or a > copy of it), then we will trigger the assert: > `gdb_assert (frame_id_p (m_cached_id));` which can be found in > `frame_info_ptr::reinflate` in frame.c. > > An example of how this can be triggered is included in the updated > test case: > > - The 'up' command sets the selected frame to a frame with > level > 0. > - An inferior call invalidates the selected frame. > - The selected frame is rebuilt following the call-stack above. > The wrapping frame_info_ptr object doesn't cache the frame-id. > - The 'frame' command invokes another inferior call for the pretty > printer, which flushes the frame cache. > - The frame_info_ptr is reinflated, e.g., to print the next > argument, and this hits the assertion mentioned above. > > There are only 3 places in GDB where new frame_info objects are > created: create_sentinel_frame, create_new_frame, and > get_prev_frame_raw. Of these, the first two always calculate the > frame_id before placing the frame_info object into a frame_info_ptr. > > Only get_prev_frame_raw, which is only called from > get_prev_frame_maybe_check_cycle, creates the frame_info_ptr before > the frame_id is calculated. > > There are two places where PREV_FRAME is returned from > get_prev_frame_maybe_check_cycle. The first is only for frame #0. > The frame_info_ptr::reinflate method doesn't need a frame_id for > frame #0, so the first return is not a problem. > > The second return from get_prev_frame_maybe_check_cycle is done after > the frame_id has been calculated, and it is here that the problem can > be fixed. If we create a new frame_info_ptr to replace PREV_FRAME > then this new frame_info_ptr will have a cached frame_id and the > problem described above will no longer occur. > > Co-Authored-By: Rohr, Stephan <stephan.rohr@intel.com> > > diff --git a/gdb/frame.c b/gdb/frame.c > index cefdde5ed1e..b91e18fad99 100644 > --- a/gdb/frame.c > +++ b/gdb/frame.c > @@ -2332,7 +2332,16 @@ get_prev_frame_maybe_check_cycle (const > frame_info_ptr &this_frame) > throw; > } > > - return prev_frame; > + /* When PREV_FRAME was initially created it had no cached frame_id as the > + frame_id had not yet been computed. Without a frame_id however > + PREV_FRAME will not be able to reinflate. Recreate the frame_info_ptr > + now that the frame_id is known, this new frame_info_ptr will have a > + cached frame_id. > + > + You might wonder about the earlier return of PREV_FRAME within the > + function. That is fine as reinflating a frame_info_ptr at level 0 > + doesn't require a cached frame_id. */ > + return frame_info_ptr (prev_frame.get ()); > } > > /* Helper function for get_prev_frame_always, this is called inside a > diff --git a/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp > b/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp > index 52162fc9952..a2a29c4d0f8 100644 > --- a/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp > +++ b/gdb/testsuite/gdb.python/pretty-print-call-by-hand.exp > @@ -108,6 +108,8 @@ with_test_prefix "frame movement down" { > with_test_prefix "frame movement up" { > if { [start_test "TAG: final frame"] == 0 } { > gdb_test "up" [multi_line "#1 .*in g \\(mt=mytype is .*\\, > depth=1\\).*" ".*first frame.*"] > + gdb_test "p f ()" " = 2" > + gdb_test "frame" [multi_line "#1 .*in g \\(mt=mytype is .*\\, > depth=1\\).*" ".*first frame.*"] > } > } > Intel Deutschland GmbH Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany Tel: +49 89 991 430, www.intel.de Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell Chairperson of the Supervisory Board: Nicole Lau Registered Seat: Munich Commercial Register: Amtsgericht Muenchen HRB 186928 ^ permalink raw reply [flat|nested] 17+ messages in thread
* RE: [PATCH 1/1] gdb: set the cache information in 'get_prev_frame_maybe_check_cycle' 2026-07-15 7:33 ` Rohr, Stephan @ 2026-07-18 11:57 ` Andrew Burgess 0 siblings, 0 replies; 17+ messages in thread From: Andrew Burgess @ 2026-07-18 11:57 UTC (permalink / raw) To: Rohr, Stephan; +Cc: gdb-patches, Tom Tromey "Rohr, Stephan" <stephan.rohr@intel.com> writes: > Hi Andrew, > > thanks for getting back with that. This was also my first approach > to fix the issue. The only limitation I see is that we have a duplicate > entry into the frame_list when we setup the return value. When the > dtor of prev_frame is called, it is removed again. I see this the only > side effect of the patch. Sorry about that, I guess I lost track of the original proposal somewhere along the way. I've gone ahead and pushed this patch for now. I ran into this issue again with another a second series that I'm working on, so I'd like to see this fix landed, and it looks like we're both OK with this approach. I agree there are some minor costs, but I don't see them as significant, and anything else will also have some cost associated anyway, so I don't think this is obviously that much worse than anything else. > > I'm personally fine with the proposed change as this is a minimal fix > for the bug. I agree that a perfect fix would defer the frame_info_ptr > creation until the frame-id is computed, but I think this is out of > scope for this patch. Agreed. I have this down as something to look at in the future. I have another series in progress that touches frame related stuff, so I need to see how that will go first, but after that I might revisit this. Thanks, Andrew ^ permalink raw reply [flat|nested] 17+ messages in thread
* Re: [PATCH 1/1] gdb: set the cache information in 'get_prev_frame_maybe_check_cycle' 2026-07-07 16:04 ` Andrew Burgess 2026-07-08 8:46 ` Rohr, Stephan @ 2026-07-20 17:39 ` Tom Tromey 1 sibling, 0 replies; 17+ messages in thread From: Tom Tromey @ 2026-07-20 17:39 UTC (permalink / raw) To: Andrew Burgess; +Cc: Rohr, Stephan, gdb-patches, Tom Tromey Andrew> Comparing to null_frame_id always returns false, see Andrew> frame_id::operator== for details. I wonder if this could be changed. This comparison behavior seems difficult to use, to me, whereas comparing against "null" seems more normal... in ordinary code it's really only NaNs that have this weird property and I tend to think that's a bad model. Tom ^ permalink raw reply [flat|nested] 17+ messages in thread
* RE: [PATCH 1/1] gdb: set the cache information in 'get_prev_frame_maybe_check_cycle' 2026-06-29 14:04 ` Rohr, Stephan 2026-07-06 15:06 ` Andrew Burgess @ 2026-07-07 10:33 ` Andrew Burgess 2026-07-07 12:25 ` Rohr, Stephan 1 sibling, 1 reply; 17+ messages in thread From: Andrew Burgess @ 2026-07-07 10:33 UTC (permalink / raw) To: Rohr, Stephan, Tom Tromey; +Cc: gdb-patches "Rohr, Stephan" <stephan.rohr@intel.com> writes: > Hi Tom, > > thank you for your feedback! I will fix the typos in v2 of the patch. > > I'll try to explain the problem in more detail now: > > 1. If you run an inferior call after the up command invalidates > the selected frame. > 2. The selected frame is rebuild following the chain > > get_selected_frame () -> lookup_selected_frame () > -> get_prev_frame_always_1 () > -> get_prev_frame_maybe_check_cycle () > > 3. At this point, the frame_info_ptr is constructed before the > frame-id is computed. The cache Information is not updated as > 'compute_frame_id' only updates the frame id of the internal > 'frame_info' pointer. > > 4. If we now run the "frame" command in combination with a > pretty-printer, this invokes another infcall to evaluate the > pretty-printer. > > 5. This clears the 'm_ptr' member of the frame_info_ptr. Since the cache > id is not set, a subsequent reinflate fails. > > A very simple fix for this could be: > > @@ -2332,7 +2361,7 @@ get_prev_frame_maybe_check_cycle (const frame_info_ptr &this_frame) > throw; > } > > - return prev_frame; > + return frame_info_ptr (prev_frame.get ()); > } > > It constructs a new frame_info_ptr using the updated raw pointer > of 'prev_frame'. This is somewhat redundant as another frame_info_ptr > is added to the frame list in the frame_info_ptr ctor. > > The frame list entry from > > frame_info_ptr prev_frame = get_prev_frame_raw (this_frame); > > is removed again by the frame_info_ptr's dtor. > > This would add some overhead. > > I didn't find a better solution. We could modify 'get_prev_frame_raw' to > return a raw pointer instead (it is the only location where is this called at > all). But we'd need a temporary copy of the frame_info_ptr anyways, > either to pass a frame_info_ptr to 'compute_frame_id' or inside of > 'compute_frame_id' (to forward the frame id to > 'frame_unwind_find_by_frame'). Changing these functions to > accept a raw frame_info pointer is not desired in my point of view. I'd like to dig into this a little more. Why do you feel changing these to take a raw pointer would be a bad thing? Looking at the code again, I don't think it makes much sense to have these take a frame_info_ptr. A frame_info_ptr is a mechanism to cache a frame-id and frame-level so that we can re-find a frame if the cache gets cleared. But having get_prev_frame_raw return a frame_info_ptr makes no sense (to me), we know at this point that the frame doesn't have a frame-id, so there is no way that the frame_info_ptr can ever do its job. Similarly for compute_frame_id, we know the frame we are passing in doesn't have a frame-id yet, so why pass it a frame_info_ptr? I wonder if we change these functions to take a raw frame_info* then we might (I haven't tried it yet) be able to have the frame_info_ptr constructor assert that the frame_info* it is holding has a valid frame_id, which we cannot right now, at least in part because of these two functions. Anyway, I just wondered if you'd seen some problem with changing these functions that I was missing? Thanks, Andrew ^ permalink raw reply [flat|nested] 17+ messages in thread
* RE: [PATCH 1/1] gdb: set the cache information in 'get_prev_frame_maybe_check_cycle' 2026-07-07 10:33 ` Andrew Burgess @ 2026-07-07 12:25 ` Rohr, Stephan 2026-07-07 15:04 ` Andrew Burgess 0 siblings, 1 reply; 17+ messages in thread From: Rohr, Stephan @ 2026-07-07 12:25 UTC (permalink / raw) To: Andrew Burgess, Tom Tromey; +Cc: gdb-patches Hello Andrew, I think changing 'get_prev_frame_raw' to return a raw pointer and changing 'compute_frame_id' to accept a raw pointer seems reasonable. I was hesitant to propose this change as calling 'frame_unwind_find_by_frame' in 'compute_frame_id' requires a frame_info_ptr. Same for ' fi->unwind->this_id (..)'. It feels wrong to create a temporary frame_info_ptr for these calls, though I observed similar patterns, e.g., as done in 'create_new_frame'. Changing 'get_prev_frame_maybe_check_cycle' to create a frame_info_ptr object after its ID is computed certainly fixes the bug. Thanks Stephan > -----Original Message----- > From: Andrew Burgess <aburgess@redhat.com> > Sent: Tuesday, 7 July 2026 12:34 > To: Rohr, Stephan <stephan.rohr@intel.com>; Tom Tromey > <tom@tromey.com> > Cc: gdb-patches@sourceware.org > Subject: RE: [PATCH 1/1] gdb: set the cache information in > 'get_prev_frame_maybe_check_cycle' > > "Rohr, Stephan" <stephan.rohr@intel.com> writes: > > > Hi Tom, > > > > thank you for your feedback! I will fix the typos in v2 of the patch. > > > > I'll try to explain the problem in more detail now: > > > > 1. If you run an inferior call after the up command invalidates > > the selected frame. > > 2. The selected frame is rebuild following the chain > > > > get_selected_frame () -> lookup_selected_frame () > > -> get_prev_frame_always_1 () > > -> get_prev_frame_maybe_check_cycle () > > > > 3. At this point, the frame_info_ptr is constructed before the > > frame-id is computed. The cache Information is not updated as > > 'compute_frame_id' only updates the frame id of the internal > > 'frame_info' pointer. > > > > 4. If we now run the "frame" command in combination with a > > pretty-printer, this invokes another infcall to evaluate the > > pretty-printer. > > > > 5. This clears the 'm_ptr' member of the frame_info_ptr. Since the cache > > id is not set, a subsequent reinflate fails. > > > > A very simple fix for this could be: > > > > @@ -2332,7 +2361,7 @@ get_prev_frame_maybe_check_cycle (const > frame_info_ptr &this_frame) > > throw; > > } > > > > - return prev_frame; > > + return frame_info_ptr (prev_frame.get ()); > > } > > > > It constructs a new frame_info_ptr using the updated raw pointer > > of 'prev_frame'. This is somewhat redundant as another frame_info_ptr > > is added to the frame list in the frame_info_ptr ctor. > > > > The frame list entry from > > > > frame_info_ptr prev_frame = get_prev_frame_raw (this_frame); > > > > is removed again by the frame_info_ptr's dtor. > > > > This would add some overhead. > > > > I didn't find a better solution. We could modify 'get_prev_frame_raw' to > > return a raw pointer instead (it is the only location where is this called at > > all). But we'd need a temporary copy of the frame_info_ptr anyways, > > either to pass a frame_info_ptr to 'compute_frame_id' or inside of > > 'compute_frame_id' (to forward the frame id to > > 'frame_unwind_find_by_frame'). Changing these functions to > > accept a raw frame_info pointer is not desired in my point of view. > > I'd like to dig into this a little more. Why do you feel changing these > to take a raw pointer would be a bad thing? > > Looking at the code again, I don't think it makes much sense to have > these take a frame_info_ptr. A frame_info_ptr is a mechanism to cache a > frame-id and frame-level so that we can re-find a frame if the cache > gets cleared. > > But having get_prev_frame_raw return a frame_info_ptr makes no sense (to > me), we know at this point that the frame doesn't have a frame-id, so > there is no way that the frame_info_ptr can ever do its job. > > Similarly for compute_frame_id, we know the frame we are passing in > doesn't have a frame-id yet, so why pass it a frame_info_ptr? > > I wonder if we change these functions to take a raw frame_info* then we > might (I haven't tried it yet) be able to have the frame_info_ptr > constructor assert that the frame_info* it is holding has a valid > frame_id, which we cannot right now, at least in part because of these > two functions. > > Anyway, I just wondered if you'd seen some problem with changing these > functions that I was missing? > > Thanks, > Andrew Intel Deutschland GmbH Registered Address: Dornacher Strasse 1, 85622 Feldkirchen, Germany Tel: +49 89 991 430, www.intel.de Managing Directors: Harry Demas, Jeffrey Schneiderman, Yin Chong Sorrell Chairperson of the Supervisory Board: Nicole Lau Registered Seat: Munich Commercial Register: Amtsgericht Muenchen HRB 186928 ^ permalink raw reply [flat|nested] 17+ messages in thread
* RE: [PATCH 1/1] gdb: set the cache information in 'get_prev_frame_maybe_check_cycle' 2026-07-07 12:25 ` Rohr, Stephan @ 2026-07-07 15:04 ` Andrew Burgess 0 siblings, 0 replies; 17+ messages in thread From: Andrew Burgess @ 2026-07-07 15:04 UTC (permalink / raw) To: Rohr, Stephan, Tom Tromey; +Cc: gdb-patches "Rohr, Stephan" <stephan.rohr@intel.com> writes: > Hello Andrew, > > I think changing 'get_prev_frame_raw' to return a raw pointer > and changing 'compute_frame_id' to accept a raw pointer seems > reasonable. > > I was hesitant to propose this change as calling > 'frame_unwind_find_by_frame' in 'compute_frame_id' requires > a frame_info_ptr. Same for ' fi->unwind->this_id (..)'. It feels wrong > to create a temporary frame_info_ptr for these calls, though I observed > similar patterns, e.g., as done in 'create_new_frame'. Yeah, after sending the email I started implementing this change and ran into this exact case. I agree that changing frame_unwind_find_by_frame probably isn't going to be ideal right now, so I think my original suggestion might be the way to go. I think long term it might be right to downgrade some part of the frame building API back to raw frame_info pointers, but I think this will need some wider changes to prevent those pointers becoming invalidated, e.g. frame_unwind_find_by_frame can invoke Python code, which can then invalidate the frame cache (yuck), and though that probably is never going to do anything useful, right now we run into problems reinflating (due to missing frame-ids), and if we switch to raw frame_info pointer we end up with use after free issues. I'll go through your feedback to my earlier patch, and write up a commit message and repost it shortly. Thanks, Andrew ^ permalink raw reply [flat|nested] 17+ messages in thread
end of thread, other threads:[~2026-07-20 17:39 UTC | newest] Thread overview: 17+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-06-25 14:58 [PATCH 0/1] gdb: set the cache information in 'get_prev_frame_maybe_check_cycle' Stephan Rohr 2026-06-25 14:58 ` [PATCH 1/1] " Stephan Rohr 2026-06-26 15:06 ` Tom Tromey 2026-06-29 14:04 ` Rohr, Stephan 2026-07-06 15:06 ` Andrew Burgess 2026-07-07 7:57 ` Rohr, Stephan 2026-07-07 16:04 ` Andrew Burgess 2026-07-08 8:46 ` Rohr, Stephan 2026-07-08 13:59 ` Andrew Burgess 2026-07-08 15:18 ` Rohr, Stephan 2026-07-14 15:14 ` Andrew Burgess 2026-07-15 7:33 ` Rohr, Stephan 2026-07-18 11:57 ` Andrew Burgess 2026-07-20 17:39 ` Tom Tromey 2026-07-07 10:33 ` Andrew Burgess 2026-07-07 12:25 ` Rohr, Stephan 2026-07-07 15:04 ` Andrew Burgess
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox