diff mbox series

[applied] Add primitives callable from the command line of the debugger

Message ID 87zgwj3yql.fsf@redhat.com
State Committed
Headers show
Series [applied] Add primitives callable from the command line of the debugger | expand

Commit Message

Dodji Seketeli May 25, 2021, 10:16 a.m. UTC
Hello,

During debugging it can be extremely useful to be able to visualize
the data members of a class type, instance of
abigail::ir::class_decl*.  It's actually useful to visualize the
pretty representation (type name and kind) of all types and decls that
inherit abigail::ir::type_or_decl_base, basically.

Today, in the debugger, if we have a variable defined as
"abigail::ir::type_or_decl_base* t", we can type:

    $ p t->get_pretty_representation(true, true);

This would display something like:

    $ typedef foo_t

However, if 't' is declared as:
"abigail::ir::class_decl* t", then if we type:

   (gdb) p t->get_pretty_representation(true, true);

We'll get something like:

   class foo_klass
   (gdb)

So we get the kind and the name of the ABI artifact; but in case of a
class, we don't get the details of its data members.

This patch introduces a function named "debug" which, would be invoked
on the 't' above like this:

   (gdb) p debug(t)

I would yield:

    struct tm
    {  // size in bits: 448
       // translation unit: test24-drop-fns.cc
       // @: 0x5387a0, @canonical: 0x5387a0

      int tm_sec; // uses canonical type '@0x538270'
      int tm_min; // uses canonical type '@0x538270'
      int tm_hour; // uses canonical type '@0x538270'
      int tm_mday; // uses canonical type '@0x538270'
      int tm_mon; // uses canonical type '@0x538270'
      int tm_year; // uses canonical type '@0x538270'
      int tm_wday; // uses canonical type '@0x538270'
      int tm_yday; // uses canonical type '@0x538270'
      int tm_isdst; // uses canonical type '@0x538270'
      long int tm_gmtoff; // uses canonical type '@0x461200'
      const char* tm_zone; // uses canonical type '@0x544528'
    };
    (gdb)

This gives much more information to understand what 't' designates.

The patch also provides functions to retrieve one data member from a
given type that happens to designate a class type.  For instance:

    (gdb) p get_data_member(t, "tm_sec")

This would yield:

    $19 = std::shared_ptr<abigail::ir::var_decl> (use count 4, weak count 0) = {get() = 0x9d9a80}

We could visualize that data member by doing:

    (gdb) p debug(get_data_member(t, "tm_sec")._M_ptr)
    int tm::tm_sec
    (gdb)

The patch also provides a new 'debug_equals' function that allow us to
easily perform an artifact comparison from the command line of the
debugger, as well as methods to the environment type to poke at the
canonical types available in the environment.

These new debugging primitives already proved priceless while
debugging issues that are fixed by subsequent patches to come.

	* include/abg-fwd.h (get_debug_representation, get_data_member)
	(debug, debug_equals): Declare new functions.
	* include/abg-ir.h (environment{get_canonical_types,
	get_canonical_type}): Declare new member functions.
	* src/abg-ir.cc (environment::{get_canonical_types,
	get_canonical_type}): Define new member functions.
	(get_debug_representation, get_data_member)
	(debug, debug_equals): Define new functions.

Signed-off-by: Dodji Seketeli <dodji@redhat.com>
---
 include/abg-fwd.h |  21 +++++
 include/abg-ir.h  |   4 +
 src/abg-ir.cc     | 224 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 249 insertions(+)
diff mbox series

Patch

diff --git a/include/abg-fwd.h b/include/abg-fwd.h
index cb03b54f..e7b621d4 100644
--- a/include/abg-fwd.h
+++ b/include/abg-fwd.h
@@ -999,6 +999,27 @@  get_class_or_union_flat_representation(const class_or_union_sptr& cou,
 				       bool internal,
 				       bool qualified_name = true);
 
+string
+get_debug_representation(const type_or_decl_base*);
+
+var_decl_sptr
+get_data_member(class_or_union *, const char*);
+
+var_decl_sptr
+get_data_member(type_base *clazz, const char* member_name);
+
+type_or_decl_base*
+debug(const type_or_decl_base* artifact);
+
+type_base*
+debug(const type_base* artifact);
+
+decl_base*
+debug(const decl_base* artifact);
+
+bool
+debug_equals(const type_or_decl_base *l, const type_or_decl_base *r);
+
 bool
 odr_is_relevant(const type_or_decl_base&);
 
diff --git a/include/abg-ir.h b/include/abg-ir.h
index 62c94405..2fbc12e9 100644
--- a/include/abg-ir.h
+++ b/include/abg-ir.h
@@ -200,6 +200,10 @@  public:
   const config&
   get_config() const;
 
+  vector<type_base_sptr>* get_canonical_types(const char* name);
+
+  type_base* get_canonical_type(const char* name, unsigned index);
+
   friend class class_or_union;
   friend class class_decl;
   friend class function_type;
diff --git a/src/abg-ir.cc b/src/abg-ir.cc
index d0f8c6b8..31abcc2e 100644
--- a/src/abg-ir.cc
+++ b/src/abg-ir.cc
@@ -3186,6 +3186,52 @@  const config&
 environment::get_config() const
 {return priv_->config_;}
 
+
+/// Get the vector of canonical types which have a given "string
+/// representation".
+///
+/// @param 'name', the textual representation of the type as returned
+/// by type_or_decl_base::get_pretty_representation(/*internal=*/true,
+///                                                 /*qualified=*/true)
+///
+/// This is useful to for debugging purposes as it's handy to use from
+/// inside a debugger like GDB.
+///
+/// @return a pointer to the vector of canonical types having the
+/// representation @p name, or nullptr if no type with that
+/// representation exists.
+vector<type_base_sptr>*
+environment::get_canonical_types(const char* name)
+{
+  auto ti = get_canonical_types_map().find(name);
+  if (ti == get_canonical_types_map().end())
+    return nullptr;
+  return &ti->second;
+}
+
+/// Get a given canonical type which has a given "string
+/// representation".
+///
+/// @param 'name', the textual representation of the type as returned
+/// by type_or_decl_base::get_pretty_representation(/*internal=*/true,
+///                                                 /*qualified=*/true).
+///
+/// @param index, the index of the type in the vector of types that
+/// all have the same textual representation @p 'name'.  That vector
+/// is returned by the function environment::get_canonical_types().
+///
+/// @return the canonical type which has the representation @p name,
+/// and which is at index @p index in the vector of canonical types
+/// having that same textual representation.
+type_base*
+environment::get_canonical_type(const char* name, unsigned index)
+{
+  vector<type_base_sptr> *types = get_canonical_types(name);
+  if (!types ||index >= types->size())
+    return nullptr;
+  return (*types)[index].get();
+}
+
 // </environment stuff>
 
 // <type_or_decl_base stuff>
@@ -8068,6 +8114,184 @@  get_class_or_union_flat_representation(const class_or_union_sptr& cou,
 					       internal,
 					       qualified_names);}
 
+/// Get the textual representation of a type for debugging purposes.
+///
+/// If the type is a class/union, this shows the data members, virtual
+/// member functions, size, pointer value of its canonical type, etc.
+/// Otherwise, this just shows the name of the artifact as returned by
+/// type_or_decl_base:get_pretty_representation().
+///
+/// @param artifact the artifact to show a debugging representation of.
+///
+/// @return a debugging string representation of @p artifact.
+string
+get_debug_representation(const type_or_decl_base* artifact)
+{
+  if (!artifact)
+    return string("");
+
+  class_or_union * c = is_class_or_union_type(artifact);
+  if (c)
+    {
+      class_decl *clazz = is_class_type(c);
+      string name = c->get_pretty_representation(/*internal=*/false, true);
+      std::ostringstream o;
+      o << name;
+
+      if (clazz)
+	{
+	  if (!clazz->get_base_specifiers().empty())
+	    o << " :" << std::endl;
+	  for (auto &b : clazz->get_base_specifiers())
+	    {
+	      o << "  ";
+	      if (b->get_is_virtual())
+		o << "virtual ";
+	      o << b->get_base_class()->get_pretty_representation(/*internal=*/false,
+								  /*qualified=*/true)
+		<< std::endl;
+	    }
+	}
+      o << std::endl
+	<< "{"
+	<< "  // size in bits: " << c->get_size_in_bits() << "\n"
+	<< "   // translation unit: " << c->get_translation_unit()->get_absolute_path() << std::endl
+	<< "   // @: " << std::hex << is_type(c)
+	<< ", @canonical: " << c->get_canonical_type().get() << std::dec
+	<< "\n\n";
+
+      for (auto m : c->get_data_members())
+	{
+	  type_base_sptr t = m->get_type();
+	  t = peel_typedef_pointer_or_reference_type(t);
+
+	  o << "  "
+	    << m->get_pretty_representation(/*internal=*/false,
+					    /*qualified=*/false)
+	    << ";";
+
+	  if (t && t->get_canonical_type())
+	    o << " // uses canonical type '@"
+	      << std::hex << t->get_canonical_type().get() << std::dec;
+
+	  o << "'" << std::endl;
+	}
+
+      if (clazz && clazz->has_vtable())
+	{
+	  o << "  // virtual member functions\n\n";
+	  for (auto f : clazz->get_virtual_mem_fns())
+	    o << "  " << f->get_pretty_representation(/*internal=*/false,
+						      /*qualified=*/false)
+	      << ";" << std::endl;
+	}
+
+      o << "};" << std::endl;
+
+      return o.str();
+    }
+  return artifact->get_pretty_representation(/*internal=*/true,
+					     /*qualified=*/true);
+}
+
+/// Get a given data member, referred to by its name, of a class type.
+///
+/// @param clazz the class to consider.
+///
+/// @param member_name name of the data member to get.
+///
+/// @return the resulting data member or nullptr if none was found.
+var_decl_sptr
+get_data_member(class_or_union *clazz, const char* member_name)
+{
+  if (!clazz)
+    return var_decl_sptr();
+  return clazz->find_data_member(member_name);
+}
+
+/// Get a given data member, referred to by its name, of a class type.
+///
+/// @param clazz the class to consider.
+///
+/// @param member_name name of the data member to get.
+///
+/// @return the resulting data member or nullptr if none was found.
+var_decl_sptr
+get_data_member(type_base *clazz, const char* member_name)
+{return get_data_member(is_class_or_union_type(clazz), member_name);}
+
+/// Emit a textual representation of an artifact to std error stream
+/// for debugging purposes.
+///
+/// This is useful to invoke from within a command line debugger like
+/// GDB to help make sense of a given ABI artifact.
+///
+/// @param artifact the ABI artifact to emit the debugging
+/// representation for.
+///
+/// @return the artifact @p artifact.
+type_or_decl_base*
+debug(const type_or_decl_base* artifact)
+{
+  std::cerr << get_debug_representation(artifact) << std::endl;
+  return const_cast<type_or_decl_base*>(artifact);
+}
+
+/// Emit a textual representation of an artifact to std error stream
+/// for debugging purposes.
+///
+/// This is useful to invoke from within a command line debugger like
+/// GDB to help make sense of a given ABI artifact.
+///
+/// @param artifact the ABI artifact to emit the debugging
+/// representation for.
+///
+/// @return the artifact @p artifact.
+type_base*
+debug(const type_base* artifact)
+{
+  debug(static_cast<const type_or_decl_base*>(artifact));
+  return const_cast<type_base*>(artifact);
+}
+
+/// Emit a textual representation of an artifact to std error stream
+/// for debugging purposes.
+///
+/// This is useful to invoke from within a command line debugger like
+/// GDB to help make sense of a given ABI artifact.
+///
+/// @param artifact the ABI artifact to emit the debugging
+/// representation for.
+///
+/// @return the artifact @p artifact.
+decl_base*
+debug(const decl_base* artifact)
+{
+  debug(static_cast<const type_or_decl_base*>(artifact));
+  return const_cast<decl_base*>(artifact);
+}
+
+/// Test if two ABI artifacts are equal.
+///
+/// This can be useful when used from the command line of a debugger
+/// like GDB.
+///
+/// @param l the first ABI artifact to consider in the comparison.
+///
+/// @param r the second ABI artifact to consider in the comparison.
+///
+/// @return true iff @p l equals @p r.
+bool
+debug_equals(const type_or_decl_base *l, const type_or_decl_base *r)
+{
+  if (!!l != !!r)
+    return false;
+  if (!l && !r)
+    return true;
+
+  return (*l == *r);
+}
+
 /// By looking at the language of the TU a given ABI artifact belongs
 /// to, test if the ONE Definition Rule should apply.
 ///