Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
From: Andrew Burgess <aburgess@redhat.com>
To: gdb-patches@sourceware.org
Cc: Andrew Burgess <aburgess@redhat.com>
Subject: [PATCH 3/3] gdb/python: add Corefile.mapped_files method
Date: Tue,  2 Sep 2025 17:03:51 +0100	[thread overview]
Message-ID: <5b014e8b969d0661adf41c803fdaf8e83a55a880.1756828929.git.aburgess@redhat.com> (raw)
In-Reply-To: <cover.1756828929.git.aburgess@redhat.com>

Add a new Corefile.mapped_files method which returns a list of
gdb.CorefileMappedFile objects.

Each gdb.CorefileMappedFile object represents a file that was mapped
into the process when the core file was created.

A gdb.CorefileMappedFile has attributes:

  + filename  -- A string, the name of the mapped file.
  + build_id -- A string or None, the build-id of the mapped file if
                GDB could find it (None if not).
  + is_main_executable -- A boolean, True if this mapping is the main
                          executable.
  + regions -- A list containing the regions of this file that were
               mapped into the process.

The 'regions' list is a list of gdb.CorefileMappedFileRegion objects,
each of these objects has the following attributes:

  + start -- the start address within the inferior.
  + end -- the end address within the inferior.
  + file_offset -- the offset within the mapped file for this mapping.

There are docs and tests.

Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=32844
---
 gdb/NEWS                                 |  13 +-
 gdb/doc/python.texi                      |  61 ++++
 gdb/python/py-corefile.c                 | 407 +++++++++++++++++++++++
 gdb/testsuite/gdb.python/py-corefile.exp |  41 +++
 gdb/testsuite/gdb.python/py-corefile.py  | 144 ++++++++
 5 files changed, 665 insertions(+), 1 deletion(-)
 create mode 100644 gdb/testsuite/gdb.python/py-corefile.py

diff --git a/gdb/NEWS b/gdb/NEWS
index 98e322d7be6..791ba694370 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -178,7 +178,18 @@ info threads [-gid] [-stopped] [-running] [ID]...
      has an attribute Corefile.filename, the file name of the loaded
      core file, and a method Corefile.is_valid(), which returns False
      when a Corefile object becomes invalid (e.g. when the core file
-     is unloaded).
+     is unloaded).  There is also Corefile.mapped_files() which
+     returns a list of CorefileMappedFile objects, representing files
+     that were mapped into the core file when it was created.
+
+  ** New gdb.CorefileMappedFile type representing a file that was
+     mapped when the core file was created.  Has read-only attributes
+     filename (string), build_id (string), is_main_executable
+     (boolean), and regions (list of CorefileMappedFileRegion objects).
+
+  ** New gdb.CorefileMappedFileRegion type, which represents a mapped
+     region of a file (see gdb.CorefileMappedFile above).  Has
+     read-only attributes start, end, and file_offset.
 
   ** New Inferior.corefile attribute.  This read only attribute
      contains the gdb.Corefile object if a core file is loaded into
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index 3a6022f22e7..558787ff3db 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -8648,12 +8648,73 @@ Core Files In Python
 accessed.
 @end defun
 
+@defun Corefile.mapped_files ()
+Return a list of @code{gdb.CorefileMappedFile} (see below) objects
+representing files that were mapped into the process when the core
+file was created.  This information is read from the @samp{NT_FILE}
+core file note on Linux.  Not every target supports accessing this
+information, for targets without support, an empty list will be
+returned.
+@end defun
+
 One may add arbitrary attributes to @code{gdb.Corefile} objects in the
 usual Python way.  This is useful if, for example, one needs to do
 some extra record keeping associated with the corefile.
 @xref{choosing attribute names}, for guidance on selecting a suitable
 name for new attributes.
 
+The @code{Corefile.mapped_files ()} method returns a list of
+@code{gdb.CorefileMappedFile} objects.  Each of these objects
+represents a file that was fully, or partially, mapped into the
+processes address space when the core file was created.
+
+A @code{gdb.CorefileMappedFile} object has the following attributes:
+
+@defvar CorefileMappedFile.filename
+This read only attribute contains a non-empty string, the file name of
+the mapped file.
+@end defvar
+
+@defvar CorefileMappedFile.build_id
+This read only attribute contains a non-empty string or @code{None}.
+This is the build-id of the mapped file extracted from the core file,
+or @code{None} if there was no build-id, or @value{GDBN} was unable to
+extract the build-id.
+@end defvar
+
+@defvar CorefileMappedFile.is_main_executable
+This read only attribute is @code{True} if @value{GDBN} believes this
+mapping represents the main executable for which this core file was
+created.  This will be @code{False} for all other mappings.
+@end defvar
+
+@defvar CorefileMappedFile.regions
+This read only attribute contains a list of
+@code{gdb.CorefileMappedFileRegion} objects.  Each of these objects
+describes a region of the file that was mapped into the process when
+the core file was created, further details are given below.
+@end defvar
+
+The @code{gdb.CorefileMappedFileRegion} object describes which part of
+a file that was mapped into a process when the core file was created.
+
+A @code{gdb.CorefileMappedFile} object has the following attributes:
+
+@defvar CorefileMappedFileRegion.start
+This read only attribute contains the start address of this mapping
+within the inferior.
+@end defvar
+
+@defvar CorefileMappedFileRegion.end
+This read only attribute contains end address of this mapping within
+the inferior.
+@end defvar
+
+@defvar CorefileMappedFileRegion.file_offset
+This read only attribute contains the offset within the mapped file
+for this mapping.
+@end defvar
+
 @node Python Auto-loading
 @subsection Python Auto-loading
 @cindex Python auto-loading
diff --git a/gdb/python/py-corefile.c b/gdb/python/py-corefile.c
index 25fdb31963c..8d941814d59 100644
--- a/gdb/python/py-corefile.c
+++ b/gdb/python/py-corefile.c
@@ -21,6 +21,8 @@
 #include "progspace.h"
 #include "observable.h"
 #include "inferior.h"
+#include "gdbcore.h"
+#include "gdbsupport/rsp-low.h"
 
 /* A gdb.Corefile object.  */
 
@@ -37,11 +39,61 @@ struct corefile_object
   /* Dictionary holding user-added attributes.  This is the __dict__
      attribute of the object.  This is an owning reference.  */
   PyObject *dict;
+
+  /* A List of gdb.CorefileMappedFile objects.  This list is only created
+     the first time the user calls gdb.Corefile.mapped_files(), the result
+     is cached here.  If this pointer is not NULL then this is an owning
+     pointer (i.e. this owns a reference to the List).  */
+  PyObject *mapped_files;
 };
 
 extern PyTypeObject corefile_object_type
     CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("corefile_object");
 
+/* A gdb.CorefileMapped object.  */
+
+struct corefile_mapped_file_object
+{
+  PyObject_HEAD
+
+  /* The name of a file that was mapped when the core file was created.
+     This is a 'str' object.  */
+  PyObject *filename;
+
+  /* The build-id of a file that was mapped when the core file was
+     created.  This is either a 'str' if the file had a build-id, or
+     'None' if there was no build-id for this file.  */
+  PyObject *build_id;
+
+  /* A List of gdb.CorefileMappedFileRegion objects.  */
+  PyObject *regions;
+
+  /* True if this represents the main executable from which the core file
+     was created.  */
+  bool is_main_exec_p;
+};
+
+extern PyTypeObject corefile_mapped_file_object_type
+    CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("corefile_mapped_file_object");
+
+/* A gdb.CorefileMappedFileRegion object.  */
+
+struct corefile_mapped_file_region_object
+{
+  PyObject_HEAD
+
+  /* The start and end addresses for this mapping, these are addresses
+     within the inferior's address space.  */
+  CORE_ADDR start;
+  CORE_ADDR end;
+
+  /* The offset within the mapped file for this mapping.  */
+  ULONGEST file_offset;
+};
+
+extern PyTypeObject corefile_mapped_file_region_object_type
+    CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("corefile_mapped_file_region_object");
+
 /* Clear the inferior pointer in a Corefile object OBJ when an inferior is
    deleted.  */
 
@@ -90,6 +142,7 @@ gdbpy_core_file_from_inferior (inferior *inf)
       if (object->dict == nullptr)
 	return nullptr;
       object->inferior = inf;
+      object->mapped_files = nullptr;
       cfpy_inferior_corefile_data_key.set (inf, object.get ());
       result = (PyObject *) object.release ();
     }
@@ -155,6 +208,121 @@ cfpy_is_valid (PyObject *self, PyObject *args)
   Py_RETURN_TRUE;
 }
 
+/* Implement gdb.Corefile.mapped_files ().  Return a List of
+   gdb.CorefileMappedFile objects.  The list is created the first time
+   this method is called, and then cached within the gdb.Corefile object,
+   future calls just return a reference to the same list.  */
+
+static PyObject *
+cfpy_mapped_files (PyObject *self, PyObject *args)
+{
+  corefile_object *obj = (corefile_object *) self;
+
+  CFPY_REQUIRE_VALID (obj);
+
+  /* If we have already created the List then just return another reference
+     to the existing list.  */
+  if (obj->mapped_files != nullptr)
+    {
+      Py_INCREF (obj->mapped_files);
+      return obj->mapped_files;
+    }
+
+  /* Get all the mapping data from GDB.  */
+  std::vector<core_mapped_file> mapped_files;
+  try
+    {
+      mapped_files
+	= gdb_read_core_file_mappings (obj->inferior->arch (),
+				       current_program_space->core_bfd ());
+    }
+  catch (const gdb_exception &except)
+    {
+      return gdbpy_handle_gdb_exception (nullptr, except);
+    }
+
+  /* Create a new list to hold the results.  */
+  gdbpy_ref<> list (PyList_New (0));
+  if (list == nullptr)
+    return nullptr;
+
+  /* Create each gdb.CorefileMappedFile object.  */
+  for (const core_mapped_file &file : mapped_files)
+    {
+      /* The filename 'str' object.  */
+      gdbpy_ref<> filename
+	= host_string_to_python_string (file.filename.c_str ());
+      if (filename == nullptr)
+	return nullptr;
+
+      /* The build-id object.  Either a 'str' or 'None'.  */
+      gdbpy_ref<> build_id;
+      if (file.build_id != nullptr)
+	{
+	  std::string hex_form = bin2hex (file.build_id->data,
+					  file.build_id->size);
+
+	  build_id
+	    = host_string_to_python_string (hex_form.c_str ());
+	  if (build_id == nullptr)
+	    return nullptr;
+	}
+      else
+	build_id = gdbpy_ref<>::new_reference (Py_None);
+
+      /* List to hold all the gdb.CorefileMappedFileRegion objects.  */
+      gdbpy_ref<> regions (PyList_New (0));
+      if (regions == nullptr)
+	return nullptr;
+
+      /* Create all the gdb.CorefileMappedFileRegion objects.  */
+      for (const core_mapped_file::region &r : file.regions)
+	{
+	  /* Actually create the object.  */
+	  gdbpy_ref<corefile_mapped_file_region_object> region_obj
+	    ((corefile_mapped_file_region_object *) PyObject_New
+	     (corefile_mapped_file_region_object,
+	      &corefile_mapped_file_region_object_type));
+	  if (region_obj == nullptr)
+	    return nullptr;
+
+	  /* Initialise the object.  */
+	  region_obj->start = r.start;
+	  region_obj->end = r.end;
+	  region_obj->file_offset = r.file_ofs;
+
+	  /* Add to the gdb.CorefileMappedFileRegion list.  */
+	  if (PyList_Append (regions.get (),
+			     (PyObject *) region_obj.get ()) < 0)
+	    return nullptr;
+	}
+
+      /* Actually create the gdb.CorefileMappedFile object.  */
+      gdbpy_ref<corefile_mapped_file_object> entry
+	((corefile_mapped_file_object *) PyObject_New
+	   (corefile_mapped_file_object, &corefile_mapped_file_object_type));
+      if (entry == nullptr)
+	return nullptr;
+
+      /* Initialise the object.  */
+      entry->filename = filename.release ();
+      entry->build_id = build_id.release ();
+      entry->regions = regions.release ();
+      entry->is_main_exec_p = file.is_main_exec;
+
+      /* Add to the gdb.CorefileMappedFile list.  */
+      if (PyList_Append (list.get (), (PyObject *) entry.get ()) < 0)
+	return nullptr;
+    }
+
+  /* No errors.  Move the reference currently in LIST into the Corefile
+     object itself.  Then create a new reference and hand this back to the
+     user.  */
+  obj->mapped_files = list.release ();
+  Py_INCREF (obj->mapped_files);
+  return obj->mapped_files;
+}
+
 /* Callback from gdb::observers::core_file_changed.  The core file in
    PSPACE has been changed.  */
 
@@ -195,6 +363,7 @@ cfpy_dealloc (PyObject *obj)
   gdb_assert (corefile->inferior == nullptr);
 
   Py_XDECREF (corefile->dict);
+  Py_XDECREF (corefile->mapped_files);
 
   Py_TYPE (obj)->tp_free (obj);
 }
@@ -219,6 +388,126 @@ cfpy_repr (PyObject *self)
 
 \f
 
+/* Called when a gdb.CorefileMappedFile is destroyed.  */
+
+static void
+cfmfpy_dealloc (PyObject *obj)
+{
+  corefile_mapped_file_object *mapped_file
+    = (corefile_mapped_file_object *) obj;
+
+  Py_XDECREF (mapped_file->filename);
+  Py_XDECREF (mapped_file->build_id);
+  Py_XDECREF (mapped_file->regions);
+
+  Py_TYPE (obj)->tp_free (obj);
+}
+
+/* Read the gdb.CorefileMappedFile.filename attribute.  */
+
+static PyObject *
+cfmfpy_get_filename (PyObject *self, void *closure)
+{
+  corefile_mapped_file_object *obj
+    = (corefile_mapped_file_object *) self;
+
+  gdb_assert (obj->filename != nullptr);
+
+  Py_INCREF (obj->filename);
+  return obj->filename;
+}
+
+/* Read the gdb.CorefileMappedFile.build_id attribute.  */
+
+static PyObject *
+cfmfpy_get_build_id (PyObject *self, void *closure)
+{
+  corefile_mapped_file_object *obj
+    = (corefile_mapped_file_object *) self;
+
+  gdb_assert (obj->build_id != nullptr);
+
+  Py_INCREF (obj->build_id);
+  return obj->build_id;
+}
+
+/* Read the gdb.CorefileMappedFile.regions attribute.  */
+
+static PyObject *
+cfmfpy_get_regions (PyObject *self, void *closure)
+{
+  corefile_mapped_file_object *obj
+    = (corefile_mapped_file_object *) self;
+
+  gdb_assert (obj->regions != nullptr);
+
+  Py_INCREF (obj->regions);
+  return obj->regions;
+}
+
+/* Read the gdb.CorefileMappedFile.is_main_executable attribute.  */
+
+static PyObject *
+cfmf_is_main_exec (PyObject *self, void *closure)
+{
+  corefile_mapped_file_object *obj
+    = (corefile_mapped_file_object *) self;
+
+  if (obj->is_main_exec_p)
+    Py_RETURN_TRUE;
+  else
+    Py_RETURN_FALSE;
+}
+
+\f
+
+/* Read the gdb.CorefileMappedFileRegion.start attribute.  */
+
+static PyObject *
+cfmfrpy_get_start (PyObject *self, void *closure)
+{
+  corefile_mapped_file_region_object *obj
+    = (corefile_mapped_file_region_object *) self;
+
+  gdbpy_ref<> start (gdb_py_object_from_ulongest (obj->start));
+  if (start == nullptr)
+    return nullptr;
+
+  return start.release ();
+}
+
+/* Read the gdb.CorefileMappedFileRegion.end attribute.  */
+
+static PyObject *
+cfmfrpy_get_end (PyObject *self, void *closure)
+{
+  corefile_mapped_file_region_object *obj
+    = (corefile_mapped_file_region_object *) self;
+
+  gdbpy_ref<> end (gdb_py_object_from_ulongest (obj->end));
+  if (end == nullptr)
+    return nullptr;
+
+  return end.release ();
+}
+
+/* Read the gdb.CorefileMappedFileRegion.file_offset attribute.  */
+
+static PyObject *
+cfmfrpy_get_file_offset (PyObject *self, void *closure)
+{
+  corefile_mapped_file_region_object *obj
+    = (corefile_mapped_file_region_object *) self;
+
+  gdbpy_ref<> file_offset (gdb_py_object_from_ulongest (obj->file_offset));
+  if (file_offset == nullptr)
+    return nullptr;
+
+  return file_offset.release ();
+}
+
+\f
+
 static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
 gdbpy_initialize_corefile (void)
 {
@@ -228,6 +517,12 @@ gdbpy_initialize_corefile (void)
   if (gdbpy_type_ready (&corefile_object_type) < 0)
     return -1;
 
+  if (gdbpy_type_ready (&corefile_mapped_file_object_type) < 0)
+    return -1;
+
+  if (gdbpy_type_ready (&corefile_mapped_file_region_object_type) < 0)
+    return -1;
+
   return 0;
 }
 
@@ -247,6 +542,10 @@ static PyMethodDef corefile_object_methods[] =
   { "is_valid", cfpy_is_valid, METH_NOARGS,
     "is_valid () -> Boolean.\n\
 Return true if this Corefile is valid, false if not." },
+  { "mapped_files", cfpy_mapped_files, METH_NOARGS,
+    "mapped_files () -> List of mapping tuples.\n\
+Return a list of tuples.  Each tuple represents a mapping from the\
+core file." },
   { nullptr }
 };
 
@@ -291,3 +590,111 @@ PyTypeObject corefile_object_type =
   0,				  /* tp_alloc */
   0,				  /* tp_new */
 };
+
+static gdb_PyGetSetDef corefile_mapped_file_object_getset[] =
+{
+  { "filename", cfmfpy_get_filename, nullptr,
+    "The filename of a CorefileMappedFile object.", nullptr },
+  { "build_id", cfmfpy_get_build_id, nullptr,
+    "The build-id of a CorefileMappedFile object or None.", nullptr },
+  { "regions", cfmfpy_get_regions, nullptr,
+    "The list of regions from a CorefileMappedFile object.", nullptr },
+  { "is_main_executable", cfmf_is_main_exec, nullptr,
+    "True for the main executable mapping, otherwise False.", nullptr },
+  { nullptr }
+};
+
+PyTypeObject corefile_mapped_file_object_type =
+{
+  PyVarObject_HEAD_INIT (NULL, 0)
+  "gdb.CorefileMappedFile",	  /*tp_name*/
+  sizeof (corefile_mapped_file_object),	/*tp_basicsize*/
+  0,				  /*tp_itemsize*/
+  cfmfpy_dealloc,		  /*tp_dealloc*/
+  0,				  /*tp_print*/
+  0,				  /*tp_getattr*/
+  0,				  /*tp_setattr*/
+  0,				  /*tp_compare*/
+  0,				  /*tp_repr*/
+  0,				  /*tp_as_number*/
+  0,				  /*tp_as_sequence*/
+  0,				  /*tp_as_mapping*/
+  0,				  /*tp_hash */
+  0,				  /*tp_call*/
+  0,				  /*tp_str*/
+  0,				  /*tp_getattro*/
+  0,				  /*tp_setattro*/
+  0,				  /*tp_as_buffer*/
+  Py_TPFLAGS_DEFAULT,		  /*tp_flags*/
+  "GDB corefile mapped file object",	/* tp_doc */
+  0,				  /* tp_traverse */
+  0,				  /* tp_clear */
+  0,				  /* tp_richcompare */
+  0,				  /* tp_weaklistoffset */
+  0,				  /* tp_iter */
+  0,				  /* tp_iternext */
+  0,				  /* tp_methods */
+  0,				  /* tp_members */
+  corefile_mapped_file_object_getset,	/* tp_getset */
+  0,				  /* tp_base */
+  0,				  /* tp_dict */
+  0,				  /* tp_descr_get */
+  0,				  /* tp_descr_set */
+  0,				  /* tp_dictoffset */
+  0,				  /* tp_init */
+  0,				  /* tp_alloc */
+  0,				  /* tp_new */
+};
+
+static gdb_PyGetSetDef corefile_mapped_file_region_object_getset[] =
+{
+  { "start", cfmfrpy_get_start, nullptr,
+    "The start address of a CorefileMappedFileRegion object.", nullptr },
+  { "end", cfmfrpy_get_end, nullptr,
+    "The end address of a CorefileMappedFileRegion object.", nullptr },
+  { "file_offset", cfmfrpy_get_file_offset, nullptr,
+    "The file offset of a CorefileMappedFileRegion object.", nullptr },
+  { nullptr }
+};
+
+PyTypeObject corefile_mapped_file_region_object_type =
+{
+  PyVarObject_HEAD_INIT (NULL, 0)
+  "gdb.CorefileMappedFileRegion",  /*tp_name*/
+  sizeof (corefile_mapped_file_region_object),	/*tp_basicsize*/
+  0,				  /*tp_itemsize*/
+  0,				  /*tp_dealloc*/
+  0,				  /*tp_print*/
+  0,				  /*tp_getattr*/
+  0,				  /*tp_setattr*/
+  0,				  /*tp_compare*/
+  0,				  /*tp_repr*/
+  0,				  /*tp_as_number*/
+  0,				  /*tp_as_sequence*/
+  0,				  /*tp_as_mapping*/
+  0,				  /*tp_hash */
+  0,				  /*tp_call*/
+  0,				  /*tp_str*/
+  0,				  /*tp_getattro*/
+  0,				  /*tp_setattro*/
+  0,				  /*tp_as_buffer*/
+  Py_TPFLAGS_DEFAULT,		  /*tp_flags*/
+  "GDB corefile mapped file region object",	/* tp_doc */
+  0,				  /* tp_traverse */
+  0,				  /* tp_clear */
+  0,				  /* tp_richcompare */
+  0,				  /* tp_weaklistoffset */
+  0,				  /* tp_iter */
+  0,				  /* tp_iternext */
+  0,				  /* tp_methods */
+  0,				  /* tp_members */
+  corefile_mapped_file_region_object_getset,	/* tp_getset */
+  0,				  /* tp_base */
+  0,				  /* tp_dict */
+  0,				  /* tp_descr_get */
+  0,				  /* tp_descr_set */
+  0,				  /* tp_dictoffset */
+  0,				  /* tp_init */
+  0,				  /* tp_alloc */
+  0,				  /* tp_new */
+};
diff --git a/gdb/testsuite/gdb.python/py-corefile.exp b/gdb/testsuite/gdb.python/py-corefile.exp
index a65c7c9b8b5..382843b7572 100644
--- a/gdb/testsuite/gdb.python/py-corefile.exp
+++ b/gdb/testsuite/gdb.python/py-corefile.exp
@@ -167,3 +167,44 @@ with_test_prefix "remove second inferior" {
     gdb_test "python print(core1.is_valid())" "^True" \
 	"check inferior 1 core file is still valid"
 }
+
+# Test the Corefile.mapped_files() API.  The Python script that is
+# sourced here implements 'info proc mappings' in Python using the
+# mapped_files API.  The output from the built-in command, and the
+# Python command should be identical.
+with_test_prefix "test mapped files data" {
+    clean_restart
+
+    set remote_python_file \
+	[gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py]
+
+    # Load the Python script into GDB.
+    gdb_test "source $remote_python_file" "^Success" \
+	"source python script"
+
+    # Load the core file.
+    gdb_test "core-file $corefile" ".*" \
+	"load core file"
+
+    # Two files to write the output to.
+    set out_1 [standard_output_file ${gdb_test_file_name}-out-1.txt]
+    set out_2 [standard_output_file ${gdb_test_file_name}-out-2.txt]
+
+    # Run the built-in command, then the new Python command, capture
+    # the output.
+    gdb_test "pipe info proc mappings | tee $out_1" ".*" \
+	"capture built-in mappings output"
+    gdb_test "pipe info proc py-mappings | tee $out_2" ".*" \
+	"capture Python based mappings data"
+
+    # Check the output is identical.
+    gdb_test "shell diff -s $out_1 $out_2" \
+	"Files \[^\r\n\]+-out-1.txt and \[^\r\n\]+-out-2.txt are identical" \
+	"diff input and output one"
+
+    # Check build-ids within the core file mapping data.
+    gdb_test "check-build-ids" "^PASS"
+
+    # Check the is_main_executable flag in the mapping data.
+    gdb_test "check-main-executable" "^PASS"
+}
diff --git a/gdb/testsuite/gdb.python/py-corefile.py b/gdb/testsuite/gdb.python/py-corefile.py
new file mode 100644
index 00000000000..cffd037a23b
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-corefile.py
@@ -0,0 +1,144 @@
+# Copyright (C) 2025 Free Software Foundation, Inc.
+
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import pathlib
+
+
+class Mapping:
+    def __init__(self, mapping, region):
+        self._mapping = mapping
+        self._region = region
+
+    @property
+    def start(self):
+        return self._region.start
+
+    @property
+    def end(self):
+        return self._region.end
+
+    @property
+    def offset(self):
+        return self._region.file_offset
+
+    @property
+    def filename(self):
+        return self._mapping.filename
+
+
+def info_proc_mappings():
+    print("Mapped address spaces:")
+    print("")
+    format_str = "%-18s %-18s %-18s %-18s %s "
+    print(format_str % ("Start Addr", "End Addr", "Size", "Offset", "File"))
+
+    core = gdb.selected_inferior().corefile
+    mappings = core.mapped_files()
+
+    result = []
+    for m in mappings:
+        for r in m.regions:
+            result.append(Mapping(m, r))
+
+    result.sort(key=lambda x: x.start)
+    for r in result:
+        sz = r.end - r.start
+        print(
+            format_str
+            % (
+                "0x%016x" % r.start,
+                "0x%016x" % r.end,
+                "0x%-16x" % sz,
+                "0x%-16x" % r.offset,
+                "%s" % r.filename,
+            )
+        )
+
+
+class InfoProcPyMappings(gdb.Command):
+    def __init__(self):
+        gdb.Command.__init__(self, "info proc py-mappings", gdb.COMMAND_DATA)
+
+    def invoke(self, args, from_tty):
+        info_proc_mappings()
+
+
+InfoProcPyMappings()
+
+
+class CheckBuildIds(gdb.Command):
+    def __init__(self):
+        gdb.Command.__init__(self, "check-build-ids", gdb.COMMAND_DATA)
+
+    def invoke(self, args, from_tty):
+        inf = gdb.selected_inferior()
+        objfiles = inf.progspace.objfiles()
+
+        path_to_build_id = {}
+
+        for o in objfiles:
+            if not o.is_file or o.build_id is None:
+                continue
+            p = pathlib.Path(o.filename).resolve()
+            b = o.build_id
+            path_to_build_id[p] = b
+
+        count = 0
+        core_mapped_files = inf.corefile.mapped_files()
+        for m in core_mapped_files:
+            p = pathlib.Path(m.filename).resolve()
+            b = m.build_id
+
+            if p in path_to_build_id:
+                count += 1
+                assert path_to_build_id[p] == b, "build-id mismatch for %s" % p
+
+        assert count > 0, "no mapped files checked"
+
+        print("PASS")
+
+
+CheckBuildIds()
+
+
+class CheckMainExec(gdb.Command):
+    def __init__(self):
+        gdb.Command.__init__(self, "check-main-executable", gdb.COMMAND_DATA)
+
+    def invoke(self, args, from_tty):
+        inf = gdb.selected_inferior()
+        pspace = inf.progspace
+        exec_filename = pathlib.Path(pspace.executable_filename).resolve()
+
+        count = 0
+        core_mapped_files = inf.corefile.mapped_files()
+        for m in core_mapped_files:
+            if not m.is_main_executable:
+                continue
+
+            p = pathlib.Path(m.filename).resolve()
+
+            count += 1
+            assert exec_filename == p, "main exec filename mismatch"
+
+        assert count == 1, "invalid main executable count"
+
+        print("PASS")
+
+
+CheckMainExec()
+
+
+print("Success")
-- 
2.47.1


  parent reply	other threads:[~2025-09-02 16:07 UTC|newest]

Thread overview: 38+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-09-02 16:03 [PATCH 0/3] Core file Python API Andrew Burgess
2025-09-02 16:03 ` [PATCH 1/3] gdb/python: introduce gdb.Corefile API Andrew Burgess
2025-09-02 16:26   ` Eli Zaretskii
2025-09-16 17:25   ` Tom Tromey
2025-09-23 13:50     ` Andrew Burgess
2025-09-02 16:03 ` [PATCH 2/3] gdb: make structured core file mappings processing global Andrew Burgess
2025-09-16 17:28   ` Tom Tromey
2025-09-02 16:03 ` Andrew Burgess [this message]
2025-09-16 17:54   ` [PATCH 3/3] gdb/python: add Corefile.mapped_files method Tom Tromey
2025-09-23 13:52     ` Andrew Burgess
2025-09-23 13:44 ` [PATCHv2 0/3] Core file Python API Andrew Burgess
2025-09-23 13:44   ` [PATCHv2 1/3] gdb/python: introduce gdb.Corefile API Andrew Burgess
2025-10-03 18:56     ` Tom Tromey
2025-10-06  8:54       ` Andrew Burgess
2025-10-06 15:39         ` Tom Tromey
2025-10-06 16:13           ` Andrew Burgess
2025-09-23 13:44   ` [PATCHv2 2/3] gdb: make structured core file mappings processing global Andrew Burgess
2025-10-13 13:57     ` Lancelot SIX
2025-10-13 14:37       ` Andrew Burgess
2025-10-13 15:16         ` Six, Lancelot
2025-10-14  9:12         ` Lancelot SIX
2025-09-23 13:44   ` [PATCHv2 3/3] gdb/python: add Corefile.mapped_files method Andrew Burgess
2025-10-03 19:15     ` Tom Tromey
2025-10-07  6:24       ` Tom de Vries
2025-10-07 12:21         ` Andrew Burgess
2025-10-07 13:08           ` Tom de Vries
2025-10-07 13:26             ` Andrew Burgess
2025-10-07 14:38               ` Andrew Burgess
2025-10-07 15:43                 ` Tom de Vries
2025-10-07 16:28                   ` Andrew Burgess
2025-10-08  9:29                     ` Andrew Burgess
2025-10-08 10:36                       ` Tom de Vries
2025-10-08 14:14                         ` Andrew Burgess
2025-10-08 15:43                           ` Tom de Vries
2025-10-08 16:03                             ` Andrew Burgess
2025-10-16 20:00           ` Tom Tromey
2025-10-17 10:02             ` Andrew Burgess
2025-10-17 13:32               ` Andrew Burgess

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=5b014e8b969d0661adf41c803fdaf8e83a55a880.1756828929.git.aburgess@redhat.com \
    --to=aburgess@redhat.com \
    --cc=gdb-patches@sourceware.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox