[1/3] gdb: add inferior_target_stack_changed observer, use it to clear auxv cache

Message ID 20221129025048.44490-1-simon.marchi@polymtl.ca
State New
Headers
Series [1/3] gdb: add inferior_target_stack_changed observer, use it to clear auxv cache |

Commit Message

Simon Marchi Nov. 29, 2022, 2:50 a.m. UTC
  A following patch expects to be able to read the core target's auxv
data right after pushing it to the inferior's target stack.  It is not
the case today, because the read requests would hit the auxv cache,
filled earlier using (empty) auxv data obtained from the exec target.
The auxv cache is only cleared a bit later in the core target
initialization, when the inferior_appeared observers are notified.

I think it would make sense to flush an inferiors auxv cache as soone as
its target stack is modified.  The auxv cache should reflect what
reading an inferior's auxv data would look like at any point in time,
and simply speed up that process.  In other words, it should work the
same with the cache as it would work without the cache.  It seems
obvious that pushing/popping a target from the stack may change what
reading auxv for that inferior would return, and that we should
therefore flush the cache at that point.

Add an inferior_target_stack_changed observable, and attach
invalidate_auxv_cache_inf to it.  Notify this observable in the
push_target and unpush_target methods of inferior.

Change-Id: I76b00cebe933e3b26620eda39f57425aaba928e5
---
 gdb/auxv.c       |  2 ++
 gdb/inferior.c   | 23 ++++++++++++++++++++++-
 gdb/inferior.h   |  9 ++-------
 gdb/observable.c |  1 +
 gdb/observable.h |  3 +++
 5 files changed, 30 insertions(+), 8 deletions(-)


base-commit: d0a2cfbd3141dae38498fa077b01ae6bb394462b
  

Comments

Tom Tromey Nov. 30, 2022, 3:44 p.m. UTC | #1
>>>>> "Simon" == Simon Marchi via Gdb-patches <gdb-patches@sourceware.org> writes:

Simon> Add an inferior_target_stack_changed observable, and attach
Simon> invalidate_auxv_cache_inf to it.  Notify this observable in the
Simon> push_target and unpush_target methods of inferior.

This looks good to me, thanks.

It seems like overall it would be better to have the auxv cache just be
some member of 'inferior'.  Then these indirections and observers
wouldn't be needed -- the inferior could just manage it directly.

Tom
  
Simon Marchi Dec. 2, 2022, 7:36 p.m. UTC | #2
On 11/30/22 10:44, Tom Tromey wrote:
>>>>>> "Simon" == Simon Marchi via Gdb-patches <gdb-patches@sourceware.org> writes:
> 
> Simon> Add an inferior_target_stack_changed observable, and attach
> Simon> invalidate_auxv_cache_inf to it.  Notify this observable in the
> Simon> push_target and unpush_target methods of inferior.
> 
> This looks good to me, thanks.

Thanks, will push once the rest of the series is approved.

> It seems like overall it would be better to have the auxv cache just be
> some member of 'inferior'.  Then these indirections and observers
> wouldn't be needed -- the inferior could just manage it directly.

I kind of like observers, it gives the impression that things are
decoupled... but yeah in practice it just adds unnecessary layers of
indirection.

Simon
  
Tom Tromey Dec. 2, 2022, 8:59 p.m. UTC | #3
>> It seems like overall it would be better to have the auxv cache just be
>> some member of 'inferior'.  Then these indirections and observers
>> wouldn't be needed -- the inferior could just manage it directly.

Simon> I kind of like observers, it gives the impression that things are
Simon> decoupled... but yeah in practice it just adds unnecessary layers of
Simon> indirection.

I like them depending on the situation.  For me it's sort of like
attaching a registry entry.

If there are "optional" modules that need to attach data to a core data
structure (e.g., something arch-specific attaching to an objfile) then a
registry entry makes sense.  But for things that are inherit attributes
of an object, a registry entry would be sort of absurd.

I see observers the same way.  If two modules "should be" decoupled,
like if one is a core bit of gdb and another is some thing that might be
not compiled in, or only useful in some situations, then an observer is
a good way to convey state changes.

For auxv, IIUC, it's intended to always cache this data, which is
somewhat intrinsic to the inferior and the module must track some
aspects of the inferior's lifetime.  To me this says, just make some
auxv cache object as a field of inferior and let the inferior tell it
directly.

That said I wouldn't expect a change here since it's already written.
Just wouldn't object; and I guess it's worthwhile to try to articulate
the principle for new code.

thanks,
Tom
  

Patch

diff --git a/gdb/auxv.c b/gdb/auxv.c
index 0ac74826aebb..73e84cf144db 100644
--- a/gdb/auxv.c
+++ b/gdb/auxv.c
@@ -614,4 +614,6 @@  This is information provided by the operating system at program startup."));
   gdb::observers::inferior_exit.attach (invalidate_auxv_cache_inf, "auxv");
   gdb::observers::inferior_appeared.attach (invalidate_auxv_cache_inf, "auxv");
   gdb::observers::executable_changed.attach (invalidate_auxv_cache, "auxv");
+  gdb::observers::inferior_target_stack_changed.attach
+    (invalidate_auxv_cache_inf, "auxv");
 }
diff --git a/gdb/inferior.c b/gdb/inferior.c
index 23cbfd63bde0..23a68a87c728 100644
--- a/gdb/inferior.c
+++ b/gdb/inferior.c
@@ -84,6 +84,25 @@  inferior::inferior (int pid_)
 
 /* See inferior.h.  */
 
+void
+inferior::push_target (target_ops *t)
+{
+  m_target_stack.push (t);
+  gdb::observers::inferior_target_stack_changed.notify (this);
+}
+
+/* See inferior.h.  */
+
+void
+inferior::push_target (target_ops_up &&t)
+{
+  m_target_stack.push (t.get ());
+  t.release ();
+  gdb::observers::inferior_target_stack_changed.notify (this);
+}
+
+/* See inferior.h.  */
+
 int
 inferior::unpush_target (struct target_ops *t)
 {
@@ -100,7 +119,9 @@  inferior::unpush_target (struct target_ops *t)
 	proc_target->maybe_remove_resumed_with_pending_wait_status (thread);
     }
 
-  return m_target_stack.unpush (t);
+  bool ret = m_target_stack.unpush (t);
+  gdb::observers::inferior_target_stack_changed.notify (this);
+  return ret;
 }
 
 void
diff --git a/gdb/inferior.h b/gdb/inferior.h
index 69525a2e053f..909e1819922d 100644
--- a/gdb/inferior.h
+++ b/gdb/inferior.h
@@ -352,15 +352,10 @@  class inferior : public refcounted_object,
   bool deletable () const { return refcount () == 0; }
 
   /* Push T in this inferior's target stack.  */
-  void push_target (struct target_ops *t)
-  { m_target_stack.push (t); }
+  void push_target (target_ops *t);
 
   /* An overload that deletes the target on failure.  */
-  void push_target (target_ops_up &&t)
-  {
-    m_target_stack.push (t.get ());
-    t.release ();
-  }
+  void push_target (target_ops_up &&t);
 
   /* Unpush T from this inferior's target stack.  */
   int unpush_target (struct target_ops *t);
diff --git a/gdb/observable.c b/gdb/observable.c
index 0bc8697f137a..dd17ec9ab769 100644
--- a/gdb/observable.c
+++ b/gdb/observable.c
@@ -44,6 +44,7 @@  DEFINE_OBSERVABLE (target_changed);
 DEFINE_OBSERVABLE (executable_changed);
 DEFINE_OBSERVABLE (inferior_created);
 DEFINE_OBSERVABLE (inferior_execd);
+DEFINE_OBSERVABLE (inferior_target_stack_changed);
 DEFINE_OBSERVABLE (record_changed);
 DEFINE_OBSERVABLE (solib_loaded);
 DEFINE_OBSERVABLE (solib_unloaded);
diff --git a/gdb/observable.h b/gdb/observable.h
index 1103c5c98a6b..23ea6d5f6a30 100644
--- a/gdb/observable.h
+++ b/gdb/observable.h
@@ -93,6 +93,9 @@  extern observable<inferior */* inferior */> inferior_created;
 /* The inferior INF has exec'ed a new executable file.  */
 extern observable<struct inferior */* inf */> inferior_execd;
 
+/* Inferior INF's target stack changed (a target was pushed or popped).  */
+extern observable<inferior */* inf */> inferior_target_stack_changed;
+
 /* The status of process record for inferior inferior in gdb has
    changed.  The process record is started if STARTED is true, and
    the process record is stopped if STARTED is false.