* [PATCH 0/3] Core file Python API
@ 2025-09-02 16:03 Andrew Burgess
2025-09-02 16:03 ` [PATCH 1/3] gdb/python: introduce gdb.Corefile API Andrew Burgess
` (3 more replies)
0 siblings, 4 replies; 38+ messages in thread
From: Andrew Burgess @ 2025-09-02 16:03 UTC (permalink / raw)
To: gdb-patches; +Cc: Andrew Burgess
This series extends the Python API to allow users to query the
currently loaded core file, and to find out things about the core
file.
There's probably more features we could offer, but I think this is a
reasonable starting point.
All feedback welcome.
Thanks,
Andrew
---
Andrew Burgess (3):
gdb/python: introduce gdb.Corefile API
gdb: make structured core file mappings processing global
gdb/python: add Corefile.mapped_files method
gdb/Makefile.in | 1 +
gdb/NEWS | 21 +
gdb/corelow.c | 225 ++++----
gdb/doc/python.texi | 110 ++++
gdb/gdbcore.h | 43 ++
gdb/observable.c | 1 +
gdb/observable.h | 6 +
gdb/python/py-corefile.c | 700 +++++++++++++++++++++++
gdb/python/py-inferior.c | 18 +
gdb/python/python-internal.h | 8 +
gdb/testsuite/gdb.python/py-corefile.c | 25 +
gdb/testsuite/gdb.python/py-corefile.exp | 210 +++++++
gdb/testsuite/gdb.python/py-corefile.py | 144 +++++
13 files changed, 1411 insertions(+), 101 deletions(-)
create mode 100644 gdb/python/py-corefile.c
create mode 100644 gdb/testsuite/gdb.python/py-corefile.c
create mode 100644 gdb/testsuite/gdb.python/py-corefile.exp
create mode 100644 gdb/testsuite/gdb.python/py-corefile.py
base-commit: 7bdcd19cc6d8137ecdca83571946d6ffb7b4be26
--
2.47.1
^ permalink raw reply [flat|nested] 38+ messages in thread
* [PATCH 1/3] gdb/python: introduce gdb.Corefile API
2025-09-02 16:03 [PATCH 0/3] Core file Python API Andrew Burgess
@ 2025-09-02 16:03 ` Andrew Burgess
2025-09-02 16:26 ` Eli Zaretskii
2025-09-16 17:25 ` Tom Tromey
2025-09-02 16:03 ` [PATCH 2/3] gdb: make structured core file mappings processing global Andrew Burgess
` (2 subsequent siblings)
3 siblings, 2 replies; 38+ messages in thread
From: Andrew Burgess @ 2025-09-02 16:03 UTC (permalink / raw)
To: gdb-patches; +Cc: Andrew Burgess
This commit starts adding some core file related features to the
Python API.
In this initial commit I've tried to keep the changes as small as
possible for easy review.
There's a new Python class gdb.Corefile, which represents a loaded
core file. This API doesn't allow the user to create their own
gdb.Corefile objects, a core file must be loaded using the 'core-file'
command, then a gdb.Corefile object can be obtained by querying the
inferior in which the core file was loaded.
There's a new attribute gdb.Inferior.corefile, this is None when no
core file is loaded, or contains a gdb.Corefile object if a core file
has been loaded.
Currently, the gdb.Corefile object has one attribute, and one method,
these are:
gdb.Corefile.filename -- the file name of the loaded core file.
gdb.Corefile.is_valid() -- indicates if a gdb.Corefile object is
valid or not. See notes below.
A gdb.Corefile object is only valid while the corresponding core file
is loaded into an inferior. Unloading the core file, or loading a
different one will cause a gdb.Corefile object to become invalid. For
example:
(gdb) core-file /tmp/core.54313
... snip ...
(gdb) python core=gdb.selected_inferior().corefile
(gdb) python print(core)
<gdb.Corefile inferior=1 filename='/tmp/core.54313'>
(gdb) python print(core.is_valid())
True
(gdb) core-file
No core file now.
(gdb) python print(core)
<gdb.Corefile (invalid)>
(gdb) python print(core.is_valid())
False
(gdb)
In order to track changes to the core file, there is a new observable
'core_file_changed', which accounts for the changes in corelow.c,
observable,c, and observable.h. Currently, this observable is not
visible as a Python event.
I chose to access the core file via the inferior even though the core
file BFD object is actually stored within the program_space. As such,
it might seem that the natural choice would be to add the attribute as
gdb.Progspace.corefile.
For background reading on my choice, please see:
https://inbox.sourceware.org/gdb-patches/577f2c47793acb501c2611c0e6c7ea379f774830.1668789658.git.aburgess@redhat.com
This patch was never merged, it is still on my backlog, but the
observation in that work is that some targets are not really
shareable. For example, the core_target (corelow.c) stores
information about the loaded core file within the target instance. As
such, each target instance represents a single loaded core file.
Except that the BFD part of the core file is stored in the
program_space, which is a little weird.
During review, Tom made the observation, that maybe we should
investigate moving the core file BFD into the core_target. I'm
inclined to agree with this as a direction of travel.
All this leaves us with two observations:
1. Currently, loading a core file into an inferior, then using
'add-inferior' will try to share the core_target between
inferiors. This is broken, and can trigger GDB crashes. The
obvious fix, without reworking core_target, is just to prevent
this sharing, making core_target per-inferior.
2. Having the core file information split between the core_target
instance, and the BFD stored in the program_space is a little
weird, and is really just historical. Planning for a future
where the BFD is also stored in the core_target might be wise.
So, if we imagine that the BFD is (one day) moved into the
core_target, and that the core_target really becomes non-shareable,
then it is, I think, clearer that the corefile attribute should live
on the gdb.Inferior object, not the gdb.Progspace object.
There's testing for all the functionality added in this commit.
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=32844
---
gdb/Makefile.in | 1 +
gdb/NEWS | 10 +
gdb/corelow.c | 7 +
gdb/doc/python.texi | 49 ++++
gdb/observable.c | 1 +
gdb/observable.h | 6 +
gdb/python/py-corefile.c | 293 +++++++++++++++++++++++
gdb/python/py-inferior.c | 18 ++
gdb/python/python-internal.h | 8 +
gdb/testsuite/gdb.python/py-corefile.c | 25 ++
gdb/testsuite/gdb.python/py-corefile.exp | 169 +++++++++++++
11 files changed, 587 insertions(+)
create mode 100644 gdb/python/py-corefile.c
create mode 100644 gdb/testsuite/gdb.python/py-corefile.c
create mode 100644 gdb/testsuite/gdb.python/py-corefile.exp
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 7654fb1aa04..7a38bd6bed1 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -399,6 +399,7 @@ SUBDIR_PYTHON_SRCS = \
python/py-color.c \
python/py-connection.c \
python/py-continueevent.c \
+ python/py-corefile.c \
python/py-dap.c \
python/py-disasm.c \
python/py-event.c \
diff --git a/gdb/NEWS b/gdb/NEWS
index 2d410a75265..98e322d7be6 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -174,6 +174,16 @@ info threads [-gid] [-stopped] [-running] [ID]...
** New gdb.warning() function that takes a string and prints it as a
warning, with GDB's standard 'warning' prefix.
+ ** New gdb.Corefile class which represents a loaded core file. This
+ 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).
+
+ ** New Inferior.corefile attribute. This read only attribute
+ contains the gdb.Corefile object if a core file is loaded into
+ the inferior, otherwise, this contains None.
+
* Guile API
** New type <gdb:color> for dealing with colors.
diff --git a/gdb/corelow.c b/gdb/corelow.c
index af534bfb26f..111999c202c 100644
--- a/gdb/corelow.c
+++ b/gdb/corelow.c
@@ -53,6 +53,7 @@
#include "xml-tdesc.h"
#include "memtag.h"
#include "cli/cli-style.h"
+#include "observable.h"
#ifndef O_LARGEFILE
#define O_LARGEFILE 0
@@ -678,6 +679,9 @@ core_target::clear_core ()
clear_solib (current_program_space);
current_program_space->cbfd.reset (nullptr);
+
+ /* Notify that the core file has changed. */
+ gdb::observers::core_file_changed.notify (current_inferior ());
}
}
@@ -1277,6 +1281,9 @@ core_target_open (const char *arg, int from_tty)
exception_print (gdb_stderr, except);
}
}
+
+ /* Notify that the core file has changed. */
+ gdb::observers::core_file_changed.notify (current_inferior ());
}
void
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index 6fa22851f6a..3a6022f22e7 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -233,6 +233,7 @@ Python API
* Disassembly In Python:: Instruction Disassembly In Python
* Missing Debug Info In Python:: Handle missing debug info from Python.
* Missing Objfiles In Python:: Handle objfiles from Python.
+* Core Files In Python:: Python representation of core files.
@end menu
@node Basic Python
@@ -3607,6 +3608,15 @@ Inferiors In Python
quoting is applied by @value{GDBN}.
@end defvar
+@defvar Inferior.corefile
+If a core file has been loaded into this inferior (@pxref{core-file
+command}), then this contains a @code{gdb.Corefile} object that
+represents the loaded core file (@pxref{Core Files In Python}).
+
+If no core file has been loaded into this inferior, then this
+attribute contains @code{None}.
+@end defvar
+
A @code{gdb.Inferior} object has the following methods:
@defun Inferior.is_valid ()
@@ -8605,6 +8615,45 @@ Missing Objfiles In Python
@code{enabled} field of each matching handler is set to @code{True}.
@end table
+@node Core Files In Python
+@subsubsection Core Files In Python
+@cindex python, core files
+
+When a core file is loaded into an inferior (@pxref{Inferiors In
+Python}) for examination (@pxref{core-file command}), information
+about the core file is contained in a @code{gdb.Corefile} object.
+
+The @code{gdb.Corefile} for an inferior can be accessed using the
+@code{Inferior.corefile} attribute. This will be @code{None} if
+no core file is loaded.
+
+A @code{gdb.Corefile} object has the following attributes:
+
+@defvar Corefile.filename
+This read only attribute contains a non-empty string, the file name of
+the core file. Attempting to access this attribute on an invalid
+@code{gdb.Corefile} object will raise a @code{RuntimeError} exception.
+@end defvar
+
+A @code{gdb.Corefile} object has the following methods:
+
+@defun Corefile.is_valid ()
+Returns @code{True} if the @code{gdb.Corefile} object is valid,
+@code{False} if not. A @code{gdb.Corefile} object will become invalid
+when the core file is unloaded from the inferior using the
+@kbd{core-file} command (@pxref{core-file command}), or if the
+inferior in which the core file is loaded is deleted. All other
+@code{gdb.Corefile} methods and attributes will throw an exception if
+it is invalid at the time the method is called, or the attribute
+accessed.
+@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.
+
@node Python Auto-loading
@subsection Python Auto-loading
@cindex Python auto-loading
diff --git a/gdb/observable.c b/gdb/observable.c
index 1233a1943a6..8439f11c15d 100644
--- a/gdb/observable.c
+++ b/gdb/observable.c
@@ -76,6 +76,7 @@ DEFINE_OBSERVABLE (target_post_wait);
DEFINE_OBSERVABLE (new_program_space);
DEFINE_OBSERVABLE (free_program_space);
DEFINE_OBSERVABLE (tui_enabled);
+DEFINE_OBSERVABLE (core_file_changed);
} /* namespace observers */
} /* namespace gdb */
diff --git a/gdb/observable.h b/gdb/observable.h
index 4d913010c56..5f064cf1fc8 100644
--- a/gdb/observable.h
+++ b/gdb/observable.h
@@ -260,6 +260,12 @@ extern observable <program_space */* pspace */> free_program_space;
extern observable<bool /* enabled */> tui_enabled;
+/* The core file loaded into the program space inferior INF has changed.
+ The process of changing has completed, i.e. when unloading, the unload
+ is now complete. When loading a new core file, the load is complete,
+ shared libraries have been loaded, registers and threads read in, etc. */
+extern observable<inferior */* inf */> core_file_changed;
+
} /* namespace observers */
} /* namespace gdb */
diff --git a/gdb/python/py-corefile.c b/gdb/python/py-corefile.c
new file mode 100644
index 00000000000..25fdb31963c
--- /dev/null
+++ b/gdb/python/py-corefile.c
@@ -0,0 +1,293 @@
+/* Python interface to core files.
+
+ Copyright (C) 2010-2025 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ 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/>. */
+
+#include "python-internal.h"
+#include "progspace.h"
+#include "observable.h"
+#include "inferior.h"
+
+/* A gdb.Corefile object. */
+
+struct corefile_object
+{
+ PyObject_HEAD
+
+ /* The inferior this core file is attached to. This will be set to NULL
+ when the inferior is deleted, or if a different core file is loaded
+ for the inferior. When this is NULL the gdb.Corefile object is
+ considered invalid.*/
+ struct inferior *inferior;
+
+ /* Dictionary holding user-added attributes. This is the __dict__
+ attribute of the object. This is an owning reference. */
+ PyObject *dict;
+};
+
+extern PyTypeObject corefile_object_type
+ CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("corefile_object");
+
+/* Clear the inferior pointer in a Corefile object OBJ when an inferior is
+ deleted. */
+
+struct inferior_corefile_deleter
+{
+ void operator() (corefile_object *obj)
+ {
+ if (!gdb_python_initialized)
+ return;
+
+ gdbpy_enter enter_py;
+ gdbpy_ref<corefile_object> object (obj);
+ object->inferior = nullptr;
+ }
+};
+
+/* Store a gdb.Corefile object in an inferior's registry. */
+
+static const registry<inferior>::key<corefile_object,
+ inferior_corefile_deleter>
+ cfpy_inferior_corefile_data_key;
+
+/* See python-internal.h. */
+
+gdbpy_ref<>
+gdbpy_core_file_from_inferior (inferior *inf)
+{
+ gdb_assert (inf != nullptr);
+ gdb_assert (inf->pspace != nullptr);
+
+ program_space *pspace = inf->pspace;
+
+ if (pspace->core_bfd () == nullptr)
+ return gdbpy_ref<>::new_reference (Py_None);
+
+ PyObject *result = (PyObject *) cfpy_inferior_corefile_data_key.get (inf);
+ if (result == nullptr)
+ {
+ gdbpy_ref<corefile_object> object
+ ((corefile_object *) PyObject_New (corefile_object,
+ &corefile_object_type));
+ if (object == nullptr)
+ return nullptr;
+
+ object->dict = PyDict_New ();
+ if (object->dict == nullptr)
+ return nullptr;
+ object->inferior = inf;
+ cfpy_inferior_corefile_data_key.set (inf, object.get ());
+ result = (PyObject *) object.release ();
+ }
+
+ return gdbpy_ref<>::new_reference (result);
+}
+
+/* Return true if OBJ is valid. */
+
+static bool
+cfpy_corefile_object_is_valid (const corefile_object *obj)
+{
+ if (obj->inferior == nullptr)
+ return false;
+
+ gdb_assert (obj->inferior->pspace != nullptr);
+
+ return obj->inferior->pspace->core_bfd () != nullptr;
+}
+
+/* Require that COREFILE_OBJ be a valid core file. A valid core file
+ object has a valid program space, and the program space has a core file
+ loaded into it. */
+#define CFPY_REQUIRE_VALID(corefile_obj) \
+ do { \
+ if (!cfpy_corefile_object_is_valid (corefile_obj)) \
+ { \
+ PyErr_SetString (PyExc_RuntimeError, \
+ _("Corefile no longer exists.")); \
+ return NULL; \
+ } \
+ } while (0)
+
+/* Read the gdb.Corefile.filename attribute. */
+
+static PyObject *
+cfpy_get_filename (PyObject *self, void *closure)
+{
+ corefile_object *obj = (corefile_object *) self;
+
+ CFPY_REQUIRE_VALID (obj);
+
+ /* If the program space's core file had been cleared, then this Corefile
+ object would have been invalidated. */
+ bfd *abfd= obj->inferior->pspace->core_bfd ();
+ gdb_assert (abfd != nullptr);
+
+ return host_string_to_python_string (bfd_get_filename (abfd)).release ();
+}
+
+/* Implementation of gdb.Corefile.is_valid (self) -> Boolean.
+ Returns True if this core file object is associated with a program space
+ that still exists, an the program space still has a core file loaded. */
+
+static PyObject *
+cfpy_is_valid (PyObject *self, PyObject *args)
+{
+ corefile_object *obj = (corefile_object *) self;
+
+ if (!cfpy_corefile_object_is_valid (obj))
+ Py_RETURN_FALSE;
+
+ Py_RETURN_TRUE;
+}
+
+/* Callback from gdb::observers::core_file_changed. The core file in
+ PSPACE has been changed. */
+
+static void
+cfpy_corefile_changed (inferior *inf)
+{
+ if (!gdb_python_initialized)
+ return;
+
+ gdbpy_enter enter_py;
+
+ /* Get any existing corefile_object for PSPACE. */
+ corefile_object *object = cfpy_inferior_corefile_data_key.get (inf);
+
+ /* If we have an object, then... */
+ if (object != nullptr)
+ {
+ /* Clearing the inferior pointer marks the gdb.Corefile as invalid. */
+ object->inferior = nullptr;
+
+ /* We're discarding our reference to the gdb.Corefile. */
+ Py_XDECREF ((PyObject *) object);
+ cfpy_inferior_corefile_data_key.set (inf, nullptr);
+ }
+}
+
+/* Called when a gdb.Corefile is destroyed. */
+
+static void
+cfpy_dealloc (PyObject *obj)
+{
+ corefile_object *corefile = (corefile_object *) obj;
+
+ /* Every gdb.Corefile is cached in an inferior's registry. The only way
+ for a gdb.Corefile to be deallocated is to remove the object reference
+ from the registry (and dec its ref count), but before we do that, we
+ set the object's inferior pointer to NULL. */
+ gdb_assert (corefile->inferior == nullptr);
+
+ Py_XDECREF (corefile->dict);
+
+ Py_TYPE (obj)->tp_free (obj);
+}
+
+/* __repr__ implementation for gdb.Corefile. */
+
+static PyObject *
+cfpy_repr (PyObject *self)
+{
+ corefile_object *obj = (corefile_object *) self;
+
+ if (!cfpy_corefile_object_is_valid (obj))
+ return gdb_py_invalid_object_repr (self);
+
+ program_space *pspace = obj->inferior->pspace;
+ gdb_assert (pspace != nullptr);
+ return PyUnicode_FromFormat ("<%s inferior=%d filename='%s'>",
+ Py_TYPE (self)->tp_name,
+ obj->inferior->num,
+ bfd_get_filename (pspace->core_bfd ()));
+}
+
+\f
+
+static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
+gdbpy_initialize_corefile (void)
+{
+ gdb::observers::core_file_changed.attach (cfpy_corefile_changed,
+ "py-corefile");
+
+ if (gdbpy_type_ready (&corefile_object_type) < 0)
+ return -1;
+
+ return 0;
+}
+
+GDBPY_INITIALIZE_FILE (gdbpy_initialize_corefile);
+
+\f
+
+static gdb_PyGetSetDef corefile_getset[] =
+{
+ { "filename", cfpy_get_filename, nullptr,
+ "The filename of a valid Corefile object.", nullptr },
+ { nullptr }
+};
+
+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." },
+ { nullptr }
+};
+
+PyTypeObject corefile_object_type =
+{
+ PyVarObject_HEAD_INIT (NULL, 0)
+ "gdb.Corefile", /*tp_name*/
+ sizeof (corefile_object), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ cfpy_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ cfpy_repr, /*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 object", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ corefile_object_methods, /* tp_methods */
+ 0, /* tp_members */
+ corefile_getset, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ offsetof (corefile_object, dict), /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
diff --git a/gdb/python/py-inferior.c b/gdb/python/py-inferior.c
index 2aa11d3160d..d926923915a 100644
--- a/gdb/python/py-inferior.c
+++ b/gdb/python/py-inferior.c
@@ -973,6 +973,22 @@ infpy_get_main_name (PyObject *self, void *closure)
return host_string_to_python_string (name).release ();
}
+/* Implement the Inferior.corefile getter. Returns a gdb.Corefile
+ object, or None. */
+
+static PyObject *
+infpy_get_core_file (PyObject *self, void *closure)
+{
+ inferior_object *inf = (inferior_object *) self;
+
+ INFPY_REQUIRE_VALID (inf);
+
+ inferior *inferior = inf->inferior;
+ gdb_assert (inferior != nullptr);
+
+ return gdbpy_core_file_from_inferior (inferior).release ();
+}
+
static void
infpy_dealloc (PyObject *obj)
{
@@ -1062,6 +1078,8 @@ static gdb_PyGetSetDef inferior_object_getset[] =
{ "progspace", infpy_get_progspace, NULL, "Program space of this inferior" },
{ "main_name", infpy_get_main_name, nullptr,
"Name of 'main' function, if known.", nullptr },
+ { "corefile", infpy_get_core_file, nullptr,
+ "The corefile loaded in to this inferior, or None.", nullptr },
{ NULL }
};
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index f61a1753ac4..1649bb83812 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -1113,6 +1113,14 @@ extern std::optional<int> gdbpy_print_insn (struct gdbarch *gdbarch,
CORE_ADDR address,
disassemble_info *info);
+/* Return the gdb.Corefile object representing the core file loaded into
+ the program space of INF, or None if there is no core file loaded. INF
+ must not be NULL. If an error occurs then NULL is returned, and a
+ suitable Python error will be set. */
+
+extern gdbpy_ref<> gdbpy_core_file_from_inferior (inferior *inf);
+
+
/* A wrapper for PyType_Ready that also automatically registers the
type in the appropriate module. Returns 0 on success, -1 on error.
If MOD is supplied, then the type is added to that module. If MOD
diff --git a/gdb/testsuite/gdb.python/py-corefile.c b/gdb/testsuite/gdb.python/py-corefile.c
new file mode 100644
index 00000000000..1334ff65143
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-corefile.c
@@ -0,0 +1,25 @@
+/* Copyright 2025 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ 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/>. */
+
+#include <stdlib.h>
+
+int
+main (void)
+{
+ /* With correct ulimit, etc. this should cause a core dump. */
+ abort ();
+}
diff --git a/gdb/testsuite/gdb.python/py-corefile.exp b/gdb/testsuite/gdb.python/py-corefile.exp
new file mode 100644
index 00000000000..a65c7c9b8b5
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-corefile.exp
@@ -0,0 +1,169 @@
+# 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/>.
+
+# This file is part of the GDB testsuite. It tests the core file
+# support in Python.
+
+require isnative
+
+load_lib gdb-python.exp
+
+require allow_python_tests
+
+standard_testfile
+
+if {[build_executable "build executable" $testfile $srcfile] == -1} {
+ return
+}
+
+set corefile [core_find $binfile]
+if {$corefile == ""} {
+ unsupported "couldn't create or find corefile"
+ return
+}
+
+# Create a copy of the corefile.
+set other_corefile [standard_output_file ${testfile}-other.core]
+remote_exec build "cp $corefile $other_corefile"
+
+clean_restart
+
+gdb_test_no_output "python inf = gdb.selected_inferior()" \
+ "capture current inferior"
+
+gdb_test "python print(inf.corefile)" "^None" \
+ "Inferior.corefile is None before loading a core file"
+
+gdb_test "core-file $corefile" ".*" \
+ "load core file"
+
+set file_re [string_to_regexp $corefile]
+gdb_test "python print(inf.corefile)" "^<gdb\\.Corefile inferior=1 filename='$file_re'>" \
+ "Inferior.corefile is a valid object after loading a core file"
+
+gdb_test_no_output "python core1=inf.corefile" "capture gdb.Corefile object"
+
+gdb_test_no_output "python core1._my_attribute = \"Hello\"" \
+ "write new attribute into Corefile object"
+
+gdb_test "python print(core1._my_attribute)" "^Hello" \
+ "immediately read new attribute"
+
+gdb_test "python print(core1.filename)" "^$file_re" \
+ "Corefile.filename attribute works as expected"
+
+gdb_test "python print(core1.is_valid())" "^True" \
+ "Corefile.is_valid() is True while corefile is loaded"
+
+gdb_test "core-file" "^No core file now\\." "unload current core file"
+
+gdb_test "python print(core1.is_valid())" "^False" \
+ "Corefile.is_valid() is False after corefile is unloaded"
+
+gdb_test "python print(core1)" "^<gdb\\.Corefile \\(invalid\\)>" \
+ "print an invalid gdb.Corefile object"
+
+gdb_test "python print(core1.filename)" \
+ [multi_line \
+ "Python Exception <class 'RuntimeError'>: Corefile no longer exists\\." \
+ "Error occurred in Python: Corefile no longer exists\\."] \
+ "error when reading filename from invalid Corefile"
+
+gdb_test "python print(inf.corefile)" "^None" \
+ "Inferior.corefile is None again after corefile unload"
+
+gdb_test "python print(core1._my_attribute)" "^Hello" \
+ "read new attribute from invalid core file"
+
+# Create a second inferior.
+gdb_test "add-inferior"
+gdb_test "inferior 2"
+
+with_test_prefix "in second inferior" {
+ gdb_test "core-file $corefile" ".*" \
+ "load core file"
+
+ gdb_test "python print(inf.corefile)" "^None" \
+ "first inferior still has no core file"
+
+ gdb_test_no_output "python core2=gdb.selected_inferior().corefile" \
+ "capture gdb.Corefile object"
+
+ # The _my_attribute was added to CORE1, not CORE2. Check it
+ # doesn't somehow appear on CORE2.
+ gdb_test "python print(core2._my_attribute)" \
+ "AttributeError.*: 'gdb\\.Corefile' object has no attribute '_my_attribute'" \
+ "try to read attribute that doesn't exist"
+
+ gdb_test "python print(core2.filename)" "^$file_re" \
+ "Corefile.filename attribute works as expected"
+
+ gdb_test "inferior 1"
+}
+
+# Read the name of the core file from the second program space while
+# the current program space is the first one.
+gdb_test "python print(core2.filename)" "^$file_re" \
+ "Corefile.filename attribute works from different progspace"
+
+# Load the other corefile into the first inferior.
+gdb_test "core $other_corefile" ".*" \
+ "load other corefile into inferior 1"
+
+# Delete the second inferior. We need to switch to the second
+# inferior and unload its corefile before we can do that. Then,
+# switch back to the first inferior, delete the second, and try to
+# read the filename of the core file from the (now deleted) second
+# inferior. We should get an error about the gdb.Corefile being
+# invalid.
+with_test_prefix "remove second inferior" {
+ gdb_test "inferior 2"
+
+ gdb_test "python print(inf.corefile.filename)" \
+ "^[string_to_regexp $other_corefile]" \
+ "read inferior 1 corefile when in inferior 2"
+
+ gdb_test_no_output "python core1=inf.corefile" \
+ "capture inferior 1 gdb.Corefile while in inferior 2"
+
+ # This is a new CORE1 object, check that _my_attribute is gone.
+ gdb_test "python print(core1._my_attribute)" \
+ "AttributeError.*: 'gdb\\.Corefile' object has no attribute '_my_attribute'" \
+ "try to read attribute that doesn't exist"
+
+ gdb_test "core-file"
+
+ gdb_test "python print(core2.filename)" \
+ [multi_line \
+ "Python Exception <class 'RuntimeError'>: Corefile no longer exists\\." \
+ "Error occurred in Python: Corefile no longer exists\\."] \
+ "error when reading filename from invalid Corefile"
+
+ gdb_test "inferior 1"
+
+ gdb_test "remove-inferiors 2"
+
+ gdb_test "python print(core2.is_valid())" "^False" \
+ "Corefile.is_valid() is False after corefile is unloaded, and Progspace is deleted"
+
+ gdb_test "python print(core2.filename)" \
+ [multi_line \
+ "Python Exception <class 'RuntimeError'>: Corefile no longer exists\\." \
+ "Error occurred in Python: Corefile no longer exists\\."] \
+ "error when reading filename of an invalid Corefile, from deleted program space"
+
+ gdb_test "python print(core1.is_valid())" "^True" \
+ "check inferior 1 core file is still valid"
+}
--
2.47.1
^ permalink raw reply [flat|nested] 38+ messages in thread
* [PATCH 2/3] gdb: make structured core file mappings processing global
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:03 ` Andrew Burgess
2025-09-16 17:28 ` Tom Tromey
2025-09-02 16:03 ` [PATCH 3/3] gdb/python: add Corefile.mapped_files method Andrew Burgess
2025-09-23 13:44 ` [PATCHv2 0/3] Core file Python API Andrew Burgess
3 siblings, 1 reply; 38+ messages in thread
From: Andrew Burgess @ 2025-09-02 16:03 UTC (permalink / raw)
To: gdb-patches; +Cc: Andrew Burgess
In corelow.c, within core_target::build_file_mappings, we have code
that wraps around a call to gdbarch_read_core_file_mappings and
provides more structure to the results.
Specifically, gdbarch_read_core_file_mappings calls a callback once
for every region of every mapped file. The wrapper code groups all of
the mappings for one file into an instance of 'struct mapped_file',
this allows all of the mapped regions to be associated with the
build-id and filename of a file.
In the next commit I plan to make this information available via the
Python API, and so I need to allow access to this structured wrapping
outside of corelow.c.
This commit renames 'struct mapped_file' to 'struct core_mapped_file'
and moves the struct into gdbcore.h. Then a new global function
gdb_read_core_file_mappings is created into which I move the code to
build the structured data.
Then corelow.c is updated to call gdb_read_core_file_mappings.
This commit does not extend the Python API, that is for the next
commit.
There should be no user visible changes after this commit.
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=32844
---
gdb/corelow.c | 218 +++++++++++++++++++++++++++-----------------------
gdb/gdbcore.h | 43 ++++++++++
2 files changed, 160 insertions(+), 101 deletions(-)
diff --git a/gdb/corelow.c b/gdb/corelow.c
index 111999c202c..e7a129c2784 100644
--- a/gdb/corelow.c
+++ b/gdb/corelow.c
@@ -366,108 +366,27 @@ core_target::core_target ()
void
core_target::build_file_mappings ()
{
- /* Type holding information about a single file mapped into the inferior
- at the point when the core file was created. Associates a build-id
- with the list of regions the file is mapped into. */
- struct mapped_file
- {
- /* Type for a region of a file that was mapped into the inferior when
- the core file was generated. */
- struct region
- {
- /* Constructor. See member variables for argument descriptions. */
- region (CORE_ADDR start_, CORE_ADDR end_, CORE_ADDR file_ofs_)
- : start (start_),
- end (end_),
- file_ofs (file_ofs_)
- { /* Nothing. */ }
-
- /* The inferior address for the start of the mapped region. */
- CORE_ADDR start;
-
- /* The inferior address immediately after the mapped region. */
- CORE_ADDR end;
-
- /* The offset within the mapped file for this content. */
- CORE_ADDR file_ofs;
- };
-
- /* If not nullptr, then this is the build-id associated with this
- file. */
- const bfd_build_id *build_id = nullptr;
-
- /* If true then we have seen multiple different build-ids associated
- with the same filename. The build_id field will have been set back
- to nullptr, and we should not set build_id in future. */
- bool ignore_build_id_p = false;
-
- /* All the mapped regions of this file. */
- std::vector<region> regions;
- };
-
gdb::unordered_map<std::string, struct bfd *> bfd_map;
gdb::unordered_set<std::string> unavailable_paths;
/* All files mapped into the core file. The key is the filename. */
- gdb::unordered_map<std::string, mapped_file> mapped_files;
+ std::vector<core_mapped_file> mapped_files
+ = gdb_read_core_file_mappings (m_core_gdbarch,
+ current_program_space->core_bfd ());
- /* See linux_read_core_file_mappings() in linux-tdep.c for an example
- read_core_file_mappings method. */
- gdbarch_read_core_file_mappings (m_core_gdbarch,
- current_program_space->core_bfd (),
-
- /* After determining the number of mappings, read_core_file_mappings
- will invoke this lambda. */
- [&] (ULONGEST)
- {
- },
-
- /* read_core_file_mappings will invoke this lambda for each mapping
- that it finds. */
- [&] (int num, ULONGEST start, ULONGEST end, ULONGEST file_ofs,
- const char *filename, const bfd_build_id *build_id)
- {
- /* Architecture-specific read_core_mapping methods are expected to
- weed out non-file-backed mappings. */
- gdb_assert (filename != nullptr);
-
- /* Add this mapped region to the data for FILENAME. */
- mapped_file &file_data = mapped_files[filename];
- file_data.regions.emplace_back (start, end, file_ofs);
- if (build_id != nullptr && !file_data.ignore_build_id_p)
- {
- if (file_data.build_id == nullptr)
- file_data.build_id = build_id;
- else if (!build_id_equal (build_id, file_data.build_id))
- {
- warning (_("Multiple build-ids found for %ps"),
- styled_string (file_name_style.style (), filename));
- file_data.build_id = nullptr;
- file_data.ignore_build_id_p = true;
- }
- }
- });
-
- /* Get the build-id of the core file. */
- const bfd_build_id *core_build_id
- = build_id_bfd_get (current_program_space->core_bfd ());
-
- for (const auto &[filename, file_data] : mapped_files)
+ for (const core_mapped_file &file_data : mapped_files)
{
- /* If this mapped file has the same build-id as was discovered for
- the core-file itself, then we assume this is the main
- executable. Record the filename as we can use this later. */
- if (file_data.build_id != nullptr
- && m_expected_exec_filename.empty ()
- && build_id_equal (file_data.build_id, core_build_id))
- m_expected_exec_filename = filename;
+ /* If this mapped file is marked as the main executable then record
+ the filename as we can use this later. */
+ if (file_data.is_main_exec && m_expected_exec_filename.empty ())
+ m_expected_exec_filename = file_data.filename;
/* Use exec_file_find() to do sysroot expansion. It'll
also strip the potential sysroot "target:" prefix. If
there is no sysroot, an equivalent (possibly more
canonical) pathname will be provided. */
gdb::unique_xmalloc_ptr<char> expanded_fname
- = exec_file_find (filename.c_str (), nullptr);
+ = exec_file_find (file_data.filename.c_str (), nullptr);
bool build_id_mismatch = false;
if (expanded_fname != nullptr && file_data.build_id != nullptr)
@@ -509,7 +428,7 @@ core_target::build_file_mappings ()
{
abfd = find_objfile_by_build_id (current_program_space,
file_data.build_id,
- filename.c_str ());
+ file_data.filename.c_str ());
if (abfd != nullptr)
{
@@ -527,7 +446,7 @@ core_target::build_file_mappings ()
}
std::vector<mem_range> ranges;
- for (const mapped_file::region ®ion : file_data.regions)
+ for (const core_mapped_file::region ®ion : file_data.regions)
ranges.emplace_back (region.start, region.end - region.start);
if (expanded_fname == nullptr
@@ -545,7 +464,7 @@ core_target::build_file_mappings ()
bool content_is_in_core_file_p = true;
/* Record all regions for this file as unavailable. */
- for (const mapped_file::region ®ion : file_data.regions)
+ for (const core_mapped_file::region ®ion : file_data.regions)
{
/* Check to see if the region is available within the core
file. */
@@ -577,33 +496,33 @@ core_target::build_file_mappings ()
if (build_id_mismatch)
{
if (expanded_fname == nullptr
- || filename == expanded_fname.get ())
+ || file_data.filename == expanded_fname.get ())
warning (_("File %ps doesn't match build-id from core-file "
"during file-backed mapping processing"),
styled_string (file_name_style.style (),
- filename.c_str ()));
+ file_data.filename.c_str ()));
else
warning (_("File %ps which was expanded to %ps, doesn't match "
"build-id from core-file during file-backed "
"mapping processing"),
styled_string (file_name_style.style (),
- filename.c_str ()),
+ file_data.filename.c_str ()),
styled_string (file_name_style.style (),
expanded_fname.get ()));
}
else if (!content_is_in_core_file_p)
{
if (expanded_fname == nullptr
- || filename == expanded_fname.get ())
+ || file_data.filename == expanded_fname.get ())
warning (_("Can't open file %ps during file-backed mapping "
"note processing"),
styled_string (file_name_style.style (),
- filename.c_str ()));
+ file_data.filename.c_str ()));
else
warning (_("Can't open file %ps which was expanded to %ps "
"during file-backed mapping note processing"),
styled_string (file_name_style.style (),
- filename.c_str ()),
+ file_data.filename.c_str ()),
styled_string (file_name_style.style (),
expanded_fname.get ()));
}
@@ -617,7 +536,7 @@ core_target::build_file_mappings ()
abfd.get ());
/* Create sections for each mapped region. */
- for (const mapped_file::region ®ion : file_data.regions)
+ for (const core_mapped_file::region ®ion : file_data.regions)
{
/* Make new BFD section. All sections have the same name,
which is permitted by bfd_make_section_anyway(). */
@@ -653,7 +572,7 @@ core_target::build_file_mappings ()
soname = gdb_bfd_read_elf_soname (actual_filename);
}
- m_mapped_file_info.add (soname.get (), filename.c_str (),
+ m_mapped_file_info.add (soname.get (), file_data.filename.c_str (),
actual_filename, std::move (ranges),
file_data.build_id);
}
@@ -2154,6 +2073,103 @@ mapped_file_info::lookup (const char *filename,
/* See gdbcore.h. */
+std::vector<core_mapped_file>
+gdb_read_core_file_mappings (struct gdbarch *gdbarch, struct bfd *cbfd)
+{
+ std::vector<core_mapped_file> results;
+
+ /* A map entry used while building RESULTS. */
+ struct map_entry
+ {
+ explicit map_entry (core_mapped_file *ptr)
+ : file_data (ptr)
+ { /* Nothing. */ }
+
+ /* Points to an entry in RESULTS, this allows entries to be quickly
+ looked up and updated as new mappings are read. */
+ core_mapped_file *file_data = nullptr;
+
+ /* If true then we have seen multiple different build-ids associated
+ with the filename of FILE_DATA. The FILE_DATA->build_id field will
+ have been set to nullptr, and we should not set FILE_DATA->build_id
+ in future. */
+ bool ignore_build_id_p = false;
+ };
+
+ /* All files mapped into the core file. The key is the filename. */
+ gdb::unordered_map<std::string, map_entry> mapped_files;
+
+ /* Get the build-id of the core file. At least on Linux, this will be
+ the build-id for the main executable. If other targets add the
+ gdbarch_read_core_file_mappings method, then it might turn out that
+ this logic is no longer true, in which case this might need to move
+ into the gdbarch_read_core_file_mappings method. */
+ const bfd_build_id *core_build_id = build_id_bfd_get (cbfd);
+
+ /* See linux_read_core_file_mappings() in linux-tdep.c for an example
+ read_core_file_mappings method. */
+ gdbarch_read_core_file_mappings (gdbarch, cbfd,
+ /* After determining the number of mappings, read_core_file_mappings
+ will invoke this lambda. */
+ [&] (ULONGEST)
+ {
+ },
+
+ /* read_core_file_mappings will invoke this lambda for each mapping
+ that it finds. */
+ [&] (int num, ULONGEST start, ULONGEST end, ULONGEST file_ofs,
+ const char *filename, const bfd_build_id *build_id)
+ {
+ /* Architecture-specific read_core_mapping methods are expected to
+ weed out non-file-backed mappings. */
+ gdb_assert (filename != nullptr);
+
+ /* Add this mapped region to the data for FILENAME. */
+ auto iter = mapped_files.find (filename);
+ if (iter == mapped_files.end ())
+ {
+ /* Create entry in results list. */
+ results.emplace_back ();
+
+ /* The entry to be added to the lookup map. */
+ map_entry entry (&results.back ());
+ entry.file_data->filename = filename;
+
+ /* Add entry to the quick lookup map and update ITER. */
+ auto inserted_result
+ = mapped_files.insert ({filename, std::move (entry)});
+ gdb_assert (inserted_result.second);
+ iter = inserted_result.first;
+ }
+
+ core_mapped_file &file_data = *iter->second.file_data;
+ bool &ignore_build_id_p = iter->second.ignore_build_id_p;
+
+ file_data.regions.emplace_back (start, end, file_ofs);
+ if (build_id != nullptr && !ignore_build_id_p)
+ {
+ if (file_data.build_id == nullptr)
+ file_data.build_id = build_id;
+ else if (!build_id_equal (build_id, file_data.build_id))
+ {
+ warning (_("Multiple build-ids found for %ps"),
+ styled_string (file_name_style.style (), filename));
+ file_data.build_id = nullptr;
+ ignore_build_id_p = true;
+ }
+ }
+
+ if (build_id != nullptr
+ && core_build_id != nullptr
+ && build_id_equal (build_id, core_build_id))
+ file_data.is_main_exec = true;
+ });
+
+ return results;
+}
+
+/* See gdbcore.h. */
+
std::optional <core_target_mapped_file_info>
core_target_find_mapped_file (const char *filename,
std::optional<CORE_ADDR> addr)
diff --git a/gdb/gdbcore.h b/gdb/gdbcore.h
index e0e3843c97b..23432ecbd07 100644
--- a/gdb/gdbcore.h
+++ b/gdb/gdbcore.h
@@ -258,4 +258,47 @@ std::optional<core_target_mapped_file_info>
core_target_find_mapped_file (const char *filename,
std::optional<CORE_ADDR> addr);
+/* Type holding information about a single file mapped into the inferior
+ at the point when the core file was created. Associates a build-id
+ with the list of regions the file is mapped into. */
+struct core_mapped_file
+{
+ /* Type for a region of a file that was mapped into the inferior when
+ the core file was generated. */
+ struct region
+ {
+ /* Constructor. See member variables for argument descriptions. */
+ region (CORE_ADDR start_, CORE_ADDR end_, CORE_ADDR file_ofs_)
+ : start (start_),
+ end (end_),
+ file_ofs (file_ofs_)
+ { /* Nothing. */ }
+
+ /* The inferior address for the start of the mapped region. */
+ CORE_ADDR start;
+
+ /* The inferior address immediately after the mapped region. */
+ CORE_ADDR end;
+
+ /* The offset within the mapped file for this content. */
+ CORE_ADDR file_ofs;
+ };
+
+ /* The filename as recorded in the core file. */
+ std::string filename;
+
+ /* If not nullptr, then this is the build-id associated with this
+ file. */
+ const bfd_build_id *build_id = nullptr;
+
+ /* All the mapped regions of this file. */
+ std::vector<region> regions;
+
+ /* True if this is the main executable. */
+ bool is_main_exec = false;
+};
+
+extern std::vector<core_mapped_file> gdb_read_core_file_mappings
+ (struct gdbarch *gdbarch, struct bfd *cbfd);
+
#endif /* GDB_GDBCORE_H */
--
2.47.1
^ permalink raw reply [flat|nested] 38+ messages in thread
* [PATCH 3/3] gdb/python: add Corefile.mapped_files method
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:03 ` [PATCH 2/3] gdb: make structured core file mappings processing global Andrew Burgess
@ 2025-09-02 16:03 ` Andrew Burgess
2025-09-16 17:54 ` Tom Tromey
2025-09-23 13:44 ` [PATCHv2 0/3] Core file Python API Andrew Burgess
3 siblings, 1 reply; 38+ messages in thread
From: Andrew Burgess @ 2025-09-02 16:03 UTC (permalink / raw)
To: gdb-patches; +Cc: Andrew Burgess
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
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 1/3] gdb/python: introduce gdb.Corefile API
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
1 sibling, 0 replies; 38+ messages in thread
From: Eli Zaretskii @ 2025-09-02 16:26 UTC (permalink / raw)
To: Andrew Burgess; +Cc: gdb-patches
> From: Andrew Burgess <aburgess@redhat.com>
> Cc: Andrew Burgess <aburgess@redhat.com>
> Date: Tue, 2 Sep 2025 17:03:49 +0100
>
> This commit starts adding some core file related features to the
> Python API.
>
> In this initial commit I've tried to keep the changes as small as
> possible for easy review.
>
> There's a new Python class gdb.Corefile, which represents a loaded
> core file. This API doesn't allow the user to create their own
> gdb.Corefile objects, a core file must be loaded using the 'core-file'
> command, then a gdb.Corefile object can be obtained by querying the
> inferior in which the core file was loaded.
>
> There's a new attribute gdb.Inferior.corefile, this is None when no
> core file is loaded, or contains a gdb.Corefile object if a core file
> has been loaded.
>
> Currently, the gdb.Corefile object has one attribute, and one method,
> these are:
>
> gdb.Corefile.filename -- the file name of the loaded core file.
>
> gdb.Corefile.is_valid() -- indicates if a gdb.Corefile object is
> valid or not. See notes below.
>
> A gdb.Corefile object is only valid while the corresponding core file
> is loaded into an inferior. Unloading the core file, or loading a
> different one will cause a gdb.Corefile object to become invalid. For
> example:
>
> (gdb) core-file /tmp/core.54313
> ... snip ...
> (gdb) python core=gdb.selected_inferior().corefile
> (gdb) python print(core)
> <gdb.Corefile inferior=1 filename='/tmp/core.54313'>
> (gdb) python print(core.is_valid())
> True
> (gdb) core-file
> No core file now.
> (gdb) python print(core)
> <gdb.Corefile (invalid)>
> (gdb) python print(core.is_valid())
> False
> (gdb)
>
> In order to track changes to the core file, there is a new observable
> 'core_file_changed', which accounts for the changes in corelow.c,
> observable,c, and observable.h. Currently, this observable is not
> visible as a Python event.
>
> I chose to access the core file via the inferior even though the core
> file BFD object is actually stored within the program_space. As such,
> it might seem that the natural choice would be to add the attribute as
> gdb.Progspace.corefile.
>
> For background reading on my choice, please see:
>
> https://inbox.sourceware.org/gdb-patches/577f2c47793acb501c2611c0e6c7ea379f774830.1668789658.git.aburgess@redhat.com
>
> This patch was never merged, it is still on my backlog, but the
> observation in that work is that some targets are not really
> shareable. For example, the core_target (corelow.c) stores
> information about the loaded core file within the target instance. As
> such, each target instance represents a single loaded core file.
>
> Except that the BFD part of the core file is stored in the
> program_space, which is a little weird.
>
> During review, Tom made the observation, that maybe we should
> investigate moving the core file BFD into the core_target. I'm
> inclined to agree with this as a direction of travel.
>
> All this leaves us with two observations:
>
> 1. Currently, loading a core file into an inferior, then using
> 'add-inferior' will try to share the core_target between
> inferiors. This is broken, and can trigger GDB crashes. The
> obvious fix, without reworking core_target, is just to prevent
> this sharing, making core_target per-inferior.
>
> 2. Having the core file information split between the core_target
> instance, and the BFD stored in the program_space is a little
> weird, and is really just historical. Planning for a future
> where the BFD is also stored in the core_target might be wise.
>
> So, if we imagine that the BFD is (one day) moved into the
> core_target, and that the core_target really becomes non-shareable,
> then it is, I think, clearer that the corefile attribute should live
> on the gdb.Inferior object, not the gdb.Progspace object.
>
> There's testing for all the functionality added in this commit.
>
> Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=32844
> ---
> gdb/Makefile.in | 1 +
> gdb/NEWS | 10 +
> gdb/corelow.c | 7 +
> gdb/doc/python.texi | 49 ++++
> gdb/observable.c | 1 +
> gdb/observable.h | 6 +
> gdb/python/py-corefile.c | 293 +++++++++++++++++++++++
> gdb/python/py-inferior.c | 18 ++
> gdb/python/python-internal.h | 8 +
> gdb/testsuite/gdb.python/py-corefile.c | 25 ++
> gdb/testsuite/gdb.python/py-corefile.exp | 169 +++++++++++++
> 11 files changed, 587 insertions(+)
> create mode 100644 gdb/python/py-corefile.c
> create mode 100644 gdb/testsuite/gdb.python/py-corefile.c
> create mode 100644 gdb/testsuite/gdb.python/py-corefile.exp
Thanks, the documentation parts are okay.
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 1/3] gdb/python: introduce gdb.Corefile API
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
1 sibling, 1 reply; 38+ messages in thread
From: Tom Tromey @ 2025-09-16 17:25 UTC (permalink / raw)
To: Andrew Burgess; +Cc: gdb-patches
>>>>> "Andrew" == Andrew Burgess <aburgess@redhat.com> writes:
Andrew> This commit starts adding some core file related features to the
Andrew> Python API.
Andrew> In this initial commit I've tried to keep the changes as small as
Andrew> possible for easy review.
Thanks for doing this.
Andrew> +++ b/gdb/python/py-corefile.c
Andrew> @@ -0,0 +1,293 @@
Andrew> +/* Python interface to core files.
Andrew> +
Andrew> + Copyright (C) 2010-2025 Free Software Foundation, Inc.
Date seems like copy-paste error.
Andrew> + if (result == nullptr)
Andrew> + {
Andrew> + gdbpy_ref<corefile_object> object
Andrew> + ((corefile_object *) PyObject_New (corefile_object,
Andrew> + &corefile_object_type));
Andrew> + if (object == nullptr)
Andrew> + return nullptr;
Andrew> +
Andrew> + object->dict = PyDict_New ();
Andrew> + if (object->dict == nullptr)
Andrew> + return nullptr;
Andrew> + object->inferior = inf;
I don't recall how Python objects are initialized, so I wonder if we
could end up in a situation where PyDict_New fails, and then
object->inferior is uninitialized.
cfpy_dealloc checks corefile->inferior so I guess it could matter.
So if Python doesn't memset the object to zero, I guess object->inferior
should be initialized before the dict.
Andrew> +/* Callback from gdb::observers::core_file_changed. The core file in
Andrew> + PSPACE has been changed. */
Andrew> +
Andrew> +static void
Andrew> +cfpy_corefile_changed (inferior *inf)
Andrew> +{
Andrew> + if (!gdb_python_initialized)
Andrew> + return;
Andrew> +
Andrew> + gdbpy_enter enter_py;
Andrew> +
Andrew> + /* Get any existing corefile_object for PSPACE. */
Andrew> + corefile_object *object = cfpy_inferior_corefile_data_key.get (inf);
Andrew> +
Andrew> + /* If we have an object, then... */
Andrew> + if (object != nullptr)
Andrew> + {
Andrew> + /* Clearing the inferior pointer marks the gdb.Corefile as invalid. */
Andrew> + object->inferior = nullptr;
Andrew> +
Andrew> + /* We're discarding our reference to the gdb.Corefile. */
I wouldn't mind seeing this comment moved to
inferior_corefile_deleter::operator().
Andrew> + Py_XDECREF ((PyObject *) object);
Andrew> + cfpy_inferior_corefile_data_key.set (inf, nullptr);
I think the entire body of this function could be replaced with:
cfpy_inferior_corefile_data_key.clear (inf);
... and this would remove duplicate code.
Andrew> +static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
Andrew> +gdbpy_initialize_corefile (void)
No more (void)
I don't think the checker works for us any more -- at least, IIRC it
didn't work for C++ stuff very well (I guess we could ask David Malcolm)
and I haven't run it in years. So maybe those annotations aren't
worthwhile any more.
Andrew> + corefile_getset, /* tp_getset */
Andrew> + 0, /* tp_base */
Andrew> + 0, /* tp_dict */
Andrew> + 0, /* tp_descr_get */
Andrew> + 0, /* tp_descr_set */
Andrew> + offsetof (corefile_object, dict), /* tp_dictoffset */
I think if you want __dict__ to work you have to implement it by hand,
see gdb_py_generic_dict and its various users. I.e., I think the getset
object needs another entry.
Tom
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 2/3] gdb: make structured core file mappings processing global
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
0 siblings, 0 replies; 38+ messages in thread
From: Tom Tromey @ 2025-09-16 17:28 UTC (permalink / raw)
To: Andrew Burgess; +Cc: gdb-patches
>>>>> "Andrew" == Andrew Burgess <aburgess@redhat.com> writes:
Andrew> This commit renames 'struct mapped_file' to 'struct core_mapped_file'
Andrew> and moves the struct into gdbcore.h. Then a new global function
Andrew> gdb_read_core_file_mappings is created into which I move the code to
Andrew> build the structured data.
Andrew> Then corelow.c is updated to call gdb_read_core_file_mappings.
Andrew> This commit does not extend the Python API, that is for the next
Andrew> commit.
Andrew> There should be no user visible changes after this commit.
Looks good to me.
Approved-By: Tom Tromey <tom@tromey.com>
Tom
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 3/3] gdb/python: add Corefile.mapped_files method
2025-09-02 16:03 ` [PATCH 3/3] gdb/python: add Corefile.mapped_files method Andrew Burgess
@ 2025-09-16 17:54 ` Tom Tromey
2025-09-23 13:52 ` Andrew Burgess
0 siblings, 1 reply; 38+ messages in thread
From: Tom Tromey @ 2025-09-16 17:54 UTC (permalink / raw)
To: Andrew Burgess; +Cc: gdb-patches
>>>>> "Andrew" == Andrew Burgess <aburgess@redhat.com> writes:
Andrew> Add a new Corefile.mapped_files method which returns a list of
Andrew> gdb.CorefileMappedFile objects.
Andrew> Each gdb.CorefileMappedFile object represents a file that was mapped
Andrew> into the process when the core file was created.
Thanks.
Andrew> + /* A List of gdb.CorefileMappedFile objects. This list is only created
Andrew> + the first time the user calls gdb.Corefile.mapped_files(), the result
Andrew> + is cached here. If this pointer is not NULL then this is an owning
Andrew> + pointer (i.e. this owns a reference to the List). */
Andrew> + PyObject *mapped_files;
A Python list is a mutable object, so I think user code could mess
around with this data.
IMO it would be better to make this a tuple instead.
Andrew> + /* A List of gdb.CorefileMappedFileRegion objects. */
Andrew> + PyObject *regions;
Same here. Though I wonder if that's overly paranoid of me.
Andrew> + object->mapped_files = nullptr;
Perhaps this has to be hoisted if the initialization of object->inferior
does (see earlier review).
Andrew> + gdbpy_ref<corefile_mapped_file_region_object> region_obj
Andrew> + ((corefile_mapped_file_region_object *) PyObject_New
I think the cast of the return value of PyObject_New shouldn't be
needed. PyObject_New is a macro that supplies the cast.
There were a few cases of this.
Andrew> + gdbpy_ref<> start (gdb_py_object_from_ulongest (obj->start));
Andrew> + if (start == nullptr)
Andrew> + return nullptr;
Andrew> +
Andrew> + return start.release ();
The null check isn't really needed since the release does the same.
I think it's normal to just write
return gdb_py_object_from_ulongest (obj->start).release ();
There are a few of these.
Though if you prefer the wordier approach that is fine too I suppose.
Tom
^ permalink raw reply [flat|nested] 38+ messages in thread
* [PATCHv2 0/3] Core file Python API
2025-09-02 16:03 [PATCH 0/3] Core file Python API Andrew Burgess
` (2 preceding siblings ...)
2025-09-02 16:03 ` [PATCH 3/3] gdb/python: add Corefile.mapped_files method Andrew Burgess
@ 2025-09-23 13:44 ` Andrew Burgess
2025-09-23 13:44 ` [PATCHv2 1/3] gdb/python: introduce gdb.Corefile API Andrew Burgess
` (2 more replies)
3 siblings, 3 replies; 38+ messages in thread
From: Andrew Burgess @ 2025-09-23 13:44 UTC (permalink / raw)
To: gdb-patches; +Cc: Andrew Burgess
In v2:
+ Patches 1 and 3 updated inline with Tom's feedback.
+ Rebased to current HEAD.
+ Retested.
This series extends the Python API to allow users to query the
currently loaded core file, and to find out things about the core
file.
There's probably more features we could offer, but I think this is a
reasonable starting point.
All feedback welcome.
Thanks,
Andrew
---
Andrew Burgess (3):
gdb/python: introduce gdb.Corefile API
gdb: make structured core file mappings processing global
gdb/python: add Corefile.mapped_files method
gdb/Makefile.in | 1 +
gdb/NEWS | 33 ++
gdb/corelow.c | 225 ++++----
gdb/doc/python.texi | 110 ++++
gdb/gdbcore.h | 43 ++
gdb/observable.c | 1 +
gdb/observable.h | 6 +
gdb/python/py-corefile.c | 688 +++++++++++++++++++++++
gdb/python/py-inferior.c | 18 +
gdb/python/python-internal.h | 8 +
gdb/testsuite/gdb.python/py-corefile.c | 25 +
gdb/testsuite/gdb.python/py-corefile.exp | 236 ++++++++
gdb/testsuite/gdb.python/py-corefile.py | 144 +++++
13 files changed, 1437 insertions(+), 101 deletions(-)
create mode 100644 gdb/python/py-corefile.c
create mode 100644 gdb/testsuite/gdb.python/py-corefile.c
create mode 100644 gdb/testsuite/gdb.python/py-corefile.exp
create mode 100644 gdb/testsuite/gdb.python/py-corefile.py
base-commit: 84dd63f3279dc0d1e13351dbc7b11261800e4758
--
2.47.1
^ permalink raw reply [flat|nested] 38+ messages in thread
* [PATCHv2 1/3] gdb/python: introduce gdb.Corefile API
2025-09-23 13:44 ` [PATCHv2 0/3] Core file Python API Andrew Burgess
@ 2025-09-23 13:44 ` Andrew Burgess
2025-10-03 18:56 ` Tom Tromey
2025-09-23 13:44 ` [PATCHv2 2/3] gdb: make structured core file mappings processing global Andrew Burgess
2025-09-23 13:44 ` [PATCHv2 3/3] gdb/python: add Corefile.mapped_files method Andrew Burgess
2 siblings, 1 reply; 38+ messages in thread
From: Andrew Burgess @ 2025-09-23 13:44 UTC (permalink / raw)
To: gdb-patches; +Cc: Andrew Burgess, Eli Zaretskii
This commit starts adding some core file related features to the
Python API.
In this initial commit I've tried to keep the changes as small as
possible for easy review.
There's a new Python class gdb.Corefile, which represents a loaded
core file. This API doesn't allow the user to create their own
gdb.Corefile objects, a core file must be loaded using the 'core-file'
command, then a gdb.Corefile object can be obtained by querying the
inferior in which the core file was loaded.
There's a new attribute gdb.Inferior.corefile, this is None when no
core file is loaded, or contains a gdb.Corefile object if a core file
has been loaded.
Currently, the gdb.Corefile object has one attribute, and one method,
these are:
gdb.Corefile.filename -- the file name of the loaded core file.
gdb.Corefile.is_valid() -- indicates if a gdb.Corefile object is
valid or not. See notes below.
A gdb.Corefile object is only valid while the corresponding core file
is loaded into an inferior. Unloading the core file, or loading a
different one will cause a gdb.Corefile object to become invalid. For
example:
(gdb) core-file /tmp/core.54313
... snip ...
(gdb) python core=gdb.selected_inferior().corefile
(gdb) python print(core)
<gdb.Corefile inferior=1 filename='/tmp/core.54313'>
(gdb) python print(core.is_valid())
True
(gdb) core-file
No core file now.
(gdb) python print(core)
<gdb.Corefile (invalid)>
(gdb) python print(core.is_valid())
False
(gdb)
In order to track changes to the core file, there is a new observable
'core_file_changed', which accounts for the changes in corelow.c,
observable,c, and observable.h. Currently, this observable is not
visible as a Python event.
I chose to access the core file via the inferior even though the core
file BFD object is actually stored within the program_space. As such,
it might seem that the natural choice would be to add the attribute as
gdb.Progspace.corefile.
For background reading on my choice, please see:
https://inbox.sourceware.org/gdb-patches/577f2c47793acb501c2611c0e6c7ea379f774830.1668789658.git.aburgess@redhat.com
This patch was never merged, it is still on my backlog, but the
observation in that work is that some targets are not really
shareable. For example, the core_target (corelow.c) stores
information about the loaded core file within the target instance. As
such, each target instance represents a single loaded core file.
Except that the BFD part of the core file is stored in the
program_space, which is a little weird.
During review, Tom made the observation, that maybe we should
investigate moving the core file BFD into the core_target. I'm
inclined to agree with this as a direction of travel.
All this leaves us with two observations:
1. Currently, loading a core file into an inferior, then using
'add-inferior' will try to share the core_target between
inferiors. This is broken, and can trigger GDB crashes. The
obvious fix, without reworking core_target, is just to prevent
this sharing, making core_target per-inferior.
2. Having the core file information split between the core_target
instance, and the BFD stored in the program_space is a little
weird, and is really just historical. Planning for a future
where the BFD is also stored in the core_target might be wise.
So, if we imagine that the BFD is (one day) moved into the
core_target, and that the core_target really becomes non-shareable,
then it is, I think, clearer that the corefile attribute should live
on the gdb.Inferior object, not the gdb.Progspace object.
There's testing for all the functionality added in this commit.
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=32844
Reviewed-By: Eli Zaretskii <eliz@gnu.org>
---
gdb/Makefile.in | 1 +
gdb/NEWS | 12 +
gdb/corelow.c | 7 +
gdb/doc/python.texi | 49 ++++
gdb/observable.c | 1 +
gdb/observable.h | 6 +
gdb/python/py-corefile.c | 291 +++++++++++++++++++++++
gdb/python/py-inferior.c | 18 ++
gdb/python/python-internal.h | 8 +
gdb/testsuite/gdb.python/py-corefile.c | 25 ++
gdb/testsuite/gdb.python/py-corefile.exp | 178 ++++++++++++++
11 files changed, 596 insertions(+)
create mode 100644 gdb/python/py-corefile.c
create mode 100644 gdb/testsuite/gdb.python/py-corefile.c
create mode 100644 gdb/testsuite/gdb.python/py-corefile.exp
diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 689f37f479c..ef15575f978 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -399,6 +399,7 @@ SUBDIR_PYTHON_SRCS = \
python/py-color.c \
python/py-connection.c \
python/py-continueevent.c \
+ python/py-corefile.c \
python/py-dap.c \
python/py-disasm.c \
python/py-event.c \
diff --git a/gdb/NEWS b/gdb/NEWS
index 8be367d2424..07838e41bd2 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -33,6 +33,18 @@ single-inf-arg in qSupported
reply with the single-inf-arg feature to indicate that it is able to
accept arguments as a single string.
+* Python API
+
+ ** New gdb.Corefile class which represents a loaded core file. This
+ 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).
+
+ ** New Inferior.corefile attribute. This read only attribute
+ contains the gdb.Corefile object if a core file is loaded into
+ the inferior, otherwise, this contains None.
+
*** Changes in GDB 17
* Debugging Linux programs that use x86-64 or x86-64 with 32-bit pointer
diff --git a/gdb/corelow.c b/gdb/corelow.c
index 29eafe8bdfd..2f202dc1fbf 100644
--- a/gdb/corelow.c
+++ b/gdb/corelow.c
@@ -53,6 +53,7 @@
#include "xml-tdesc.h"
#include "memtag.h"
#include "cli/cli-style.h"
+#include "observable.h"
#ifndef O_LARGEFILE
#define O_LARGEFILE 0
@@ -678,6 +679,9 @@ core_target::clear_core ()
clear_solib (current_program_space);
current_program_space->cbfd.reset (nullptr);
+
+ /* Notify that the core file has changed. */
+ gdb::observers::core_file_changed.notify (current_inferior ());
}
}
@@ -1278,6 +1282,9 @@ core_target_open (const char *arg, int from_tty)
exception_print (gdb_stderr, except);
}
}
+
+ /* Notify that the core file has changed. */
+ gdb::observers::core_file_changed.notify (current_inferior ());
}
void
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index 3763eee9d63..d25567e8943 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -233,6 +233,7 @@ Python API
* Disassembly In Python:: Instruction Disassembly In Python
* Missing Debug Info In Python:: Handle missing debug info from Python.
* Missing Objfiles In Python:: Handle objfiles from Python.
+* Core Files In Python:: Python representation of core files.
@end menu
@node Basic Python
@@ -3626,6 +3627,15 @@ Inferiors In Python
quoting is applied by @value{GDBN}.
@end defvar
+@defvar Inferior.corefile
+If a core file has been loaded into this inferior (@pxref{core-file
+command}), then this contains a @code{gdb.Corefile} object that
+represents the loaded core file (@pxref{Core Files In Python}).
+
+If no core file has been loaded into this inferior, then this
+attribute contains @code{None}.
+@end defvar
+
A @code{gdb.Inferior} object has the following methods:
@defun Inferior.is_valid ()
@@ -8624,6 +8634,45 @@ Missing Objfiles In Python
@code{enabled} field of each matching handler is set to @code{True}.
@end table
+@node Core Files In Python
+@subsubsection Core Files In Python
+@cindex python, core files
+
+When a core file is loaded into an inferior (@pxref{Inferiors In
+Python}) for examination (@pxref{core-file command}), information
+about the core file is contained in a @code{gdb.Corefile} object.
+
+The @code{gdb.Corefile} for an inferior can be accessed using the
+@code{Inferior.corefile} attribute. This will be @code{None} if
+no core file is loaded.
+
+A @code{gdb.Corefile} object has the following attributes:
+
+@defvar Corefile.filename
+This read only attribute contains a non-empty string, the file name of
+the core file. Attempting to access this attribute on an invalid
+@code{gdb.Corefile} object will raise a @code{RuntimeError} exception.
+@end defvar
+
+A @code{gdb.Corefile} object has the following methods:
+
+@defun Corefile.is_valid ()
+Returns @code{True} if the @code{gdb.Corefile} object is valid,
+@code{False} if not. A @code{gdb.Corefile} object will become invalid
+when the core file is unloaded from the inferior using the
+@kbd{core-file} command (@pxref{core-file command}), or if the
+inferior in which the core file is loaded is deleted. All other
+@code{gdb.Corefile} methods and attributes will throw an exception if
+it is invalid at the time the method is called, or the attribute
+accessed.
+@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.
+
@node Python Auto-loading
@subsection Python Auto-loading
@cindex Python auto-loading
diff --git a/gdb/observable.c b/gdb/observable.c
index 1233a1943a6..8439f11c15d 100644
--- a/gdb/observable.c
+++ b/gdb/observable.c
@@ -76,6 +76,7 @@ DEFINE_OBSERVABLE (target_post_wait);
DEFINE_OBSERVABLE (new_program_space);
DEFINE_OBSERVABLE (free_program_space);
DEFINE_OBSERVABLE (tui_enabled);
+DEFINE_OBSERVABLE (core_file_changed);
} /* namespace observers */
} /* namespace gdb */
diff --git a/gdb/observable.h b/gdb/observable.h
index 4d913010c56..5f064cf1fc8 100644
--- a/gdb/observable.h
+++ b/gdb/observable.h
@@ -260,6 +260,12 @@ extern observable <program_space */* pspace */> free_program_space;
extern observable<bool /* enabled */> tui_enabled;
+/* The core file loaded into the program space inferior INF has changed.
+ The process of changing has completed, i.e. when unloading, the unload
+ is now complete. When loading a new core file, the load is complete,
+ shared libraries have been loaded, registers and threads read in, etc. */
+extern observable<inferior */* inf */> core_file_changed;
+
} /* namespace observers */
} /* namespace gdb */
diff --git a/gdb/python/py-corefile.c b/gdb/python/py-corefile.c
new file mode 100644
index 00000000000..f199a2c82be
--- /dev/null
+++ b/gdb/python/py-corefile.c
@@ -0,0 +1,291 @@
+/* Python interface to core files.
+
+ Copyright (C) 2025 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ 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/>. */
+
+#include "python-internal.h"
+#include "progspace.h"
+#include "observable.h"
+#include "inferior.h"
+
+/* A gdb.Corefile object. */
+
+struct corefile_object
+{
+ PyObject_HEAD
+
+ /* The inferior this core file is attached to. This will be set to NULL
+ when the inferior is deleted, or if a different core file is loaded
+ for the inferior. When this is NULL the gdb.Corefile object is
+ considered invalid.*/
+ struct inferior *inferior;
+
+ /* Dictionary holding user-added attributes. This is the __dict__
+ attribute of the object. This is an owning reference. */
+ PyObject *dict;
+};
+
+extern PyTypeObject corefile_object_type
+ CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("corefile_object");
+
+/* Clear the inferior pointer in a Corefile object OBJ when an inferior is
+ deleted. */
+
+struct inferior_corefile_deleter
+{
+ void operator() (corefile_object *obj)
+ {
+ if (!gdb_python_initialized)
+ return;
+
+ gdbpy_enter enter_py;
+
+ /* When OBJECT goes out of scope this will Py_DECREF on OBJ. */
+ gdbpy_ref<corefile_object> object (obj);
+
+ /* Clearing the inferior pointer marks the gdb.Corefile as invalid. */
+ object->inferior = nullptr;
+ }
+};
+
+/* Store a gdb.Corefile object in an inferior's registry. */
+
+static const registry<inferior>::key<corefile_object,
+ inferior_corefile_deleter>
+ cfpy_inferior_corefile_data_key;
+
+/* See python-internal.h. */
+
+gdbpy_ref<>
+gdbpy_core_file_from_inferior (inferior *inf)
+{
+ gdb_assert (inf != nullptr);
+ gdb_assert (inf->pspace != nullptr);
+
+ program_space *pspace = inf->pspace;
+
+ if (pspace->core_bfd () == nullptr)
+ return gdbpy_ref<>::new_reference (Py_None);
+
+ PyObject *result = (PyObject *) cfpy_inferior_corefile_data_key.get (inf);
+ if (result == nullptr)
+ {
+ gdbpy_ref<corefile_object> object
+ (PyObject_New (corefile_object, &corefile_object_type));
+ if (object == nullptr)
+ return nullptr;
+
+ /* Ensure the 'inferior' field is set to NULL. If the PyDict_New
+ call fails then the gdb.Corefile will be discarded and
+ cfpy_dealloc will be called, which requires that the 'inferior' be
+ set to NULL. */
+ object->inferior = nullptr;
+ object->dict = PyDict_New ();
+ if (object->dict == nullptr)
+ return nullptr;
+
+ /* Now that the gdb.Corefile has been successfully initialised and we
+ know that it is going to be passed back to the user, move it out
+ of the invalid state by setting the 'inferior' field to a non NULL
+ value. */
+ object->inferior = inf;
+ cfpy_inferior_corefile_data_key.set (inf, object.get ());
+ result = (PyObject *) object.release ();
+ }
+
+ return gdbpy_ref<>::new_reference (result);
+}
+
+/* Return true if OBJ is valid. */
+
+static bool
+cfpy_corefile_object_is_valid (const corefile_object *obj)
+{
+ if (obj->inferior == nullptr)
+ return false;
+
+ gdb_assert (obj->inferior->pspace != nullptr);
+
+ return obj->inferior->pspace->core_bfd () != nullptr;
+}
+
+/* Require that COREFILE_OBJ be a valid core file. A valid core file
+ object has a valid program space, and the program space has a core file
+ loaded into it. */
+#define CFPY_REQUIRE_VALID(corefile_obj) \
+ do { \
+ if (!cfpy_corefile_object_is_valid (corefile_obj)) \
+ { \
+ PyErr_SetString (PyExc_RuntimeError, \
+ _("Corefile no longer exists.")); \
+ return nullptr; \
+ } \
+ } while (0)
+
+/* Read the gdb.Corefile.filename attribute. */
+
+static PyObject *
+cfpy_get_filename (PyObject *self, void *closure)
+{
+ corefile_object *obj = (corefile_object *) self;
+
+ CFPY_REQUIRE_VALID (obj);
+
+ /* If the program space's core file had been cleared, then this Corefile
+ object would have been invalidated. */
+ bfd *abfd= obj->inferior->pspace->core_bfd ();
+ gdb_assert (abfd != nullptr);
+
+ return host_string_to_python_string (bfd_get_filename (abfd)).release ();
+}
+
+/* Implementation of gdb.Corefile.is_valid (self) -> Boolean.
+ Returns True if this core file object is associated with a program space
+ that still exists, an the program space still has a core file loaded. */
+
+static PyObject *
+cfpy_is_valid (PyObject *self, PyObject *args)
+{
+ corefile_object *obj = (corefile_object *) self;
+
+ if (!cfpy_corefile_object_is_valid (obj))
+ Py_RETURN_FALSE;
+
+ Py_RETURN_TRUE;
+}
+
+/* Callback from gdb::observers::core_file_changed. The core file in
+ PSPACE has been changed. */
+
+static void
+cfpy_corefile_changed (inferior *inf)
+{
+ cfpy_inferior_corefile_data_key.clear (inf);
+}
+
+/* Called when a gdb.Corefile is destroyed. */
+
+static void
+cfpy_dealloc (PyObject *obj)
+{
+ corefile_object *corefile = (corefile_object *) obj;
+
+ /* Every gdb.Corefile is cached in an inferior's registry. The only way
+ for a gdb.Corefile to be deallocated is to remove the object reference
+ from the registry (and dec its ref count), but before we do that, we
+ set the object's inferior pointer to NULL. */
+ gdb_assert (corefile->inferior == nullptr);
+
+ Py_XDECREF (corefile->dict);
+
+ Py_TYPE (obj)->tp_free (obj);
+}
+
+/* __repr__ implementation for gdb.Corefile. */
+
+static PyObject *
+cfpy_repr (PyObject *self)
+{
+ corefile_object *obj = (corefile_object *) self;
+
+ if (!cfpy_corefile_object_is_valid (obj))
+ return gdb_py_invalid_object_repr (self);
+
+ program_space *pspace = obj->inferior->pspace;
+ gdb_assert (pspace != nullptr);
+ return PyUnicode_FromFormat ("<%s inferior=%d filename='%s'>",
+ Py_TYPE (self)->tp_name,
+ obj->inferior->num,
+ bfd_get_filename (pspace->core_bfd ()));
+}
+
+\f
+
+static int
+gdbpy_initialize_corefile ()
+{
+ gdb::observers::core_file_changed.attach (cfpy_corefile_changed,
+ "py-corefile");
+
+ if (gdbpy_type_ready (&corefile_object_type) < 0)
+ return -1;
+
+ return 0;
+}
+
+GDBPY_INITIALIZE_FILE (gdbpy_initialize_corefile);
+
+\f
+
+static gdb_PyGetSetDef corefile_getset[] =
+{
+ { "__dict__", gdb_py_generic_dict, nullptr,
+ "The __dict__ for the gdb.Corefile.", &corefile_object_type },
+ { "filename", cfpy_get_filename, nullptr,
+ "The filename of a valid Corefile object.", nullptr },
+ { nullptr }
+};
+
+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." },
+ { nullptr }
+};
+
+PyTypeObject corefile_object_type =
+{
+ PyVarObject_HEAD_INIT (nullptr, 0)
+ "gdb.Corefile", /*tp_name*/
+ sizeof (corefile_object), /*tp_basicsize*/
+ 0, /*tp_itemsize*/
+ cfpy_dealloc, /*tp_dealloc*/
+ 0, /*tp_print*/
+ 0, /*tp_getattr*/
+ 0, /*tp_setattr*/
+ 0, /*tp_compare*/
+ cfpy_repr, /*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 object", /* tp_doc */
+ 0, /* tp_traverse */
+ 0, /* tp_clear */
+ 0, /* tp_richcompare */
+ 0, /* tp_weaklistoffset */
+ 0, /* tp_iter */
+ 0, /* tp_iternext */
+ corefile_object_methods, /* tp_methods */
+ 0, /* tp_members */
+ corefile_getset, /* tp_getset */
+ 0, /* tp_base */
+ 0, /* tp_dict */
+ 0, /* tp_descr_get */
+ 0, /* tp_descr_set */
+ offsetof (corefile_object, dict), /* tp_dictoffset */
+ 0, /* tp_init */
+ 0, /* tp_alloc */
+ 0, /* tp_new */
+};
diff --git a/gdb/python/py-inferior.c b/gdb/python/py-inferior.c
index 2aa11d3160d..d926923915a 100644
--- a/gdb/python/py-inferior.c
+++ b/gdb/python/py-inferior.c
@@ -973,6 +973,22 @@ infpy_get_main_name (PyObject *self, void *closure)
return host_string_to_python_string (name).release ();
}
+/* Implement the Inferior.corefile getter. Returns a gdb.Corefile
+ object, or None. */
+
+static PyObject *
+infpy_get_core_file (PyObject *self, void *closure)
+{
+ inferior_object *inf = (inferior_object *) self;
+
+ INFPY_REQUIRE_VALID (inf);
+
+ inferior *inferior = inf->inferior;
+ gdb_assert (inferior != nullptr);
+
+ return gdbpy_core_file_from_inferior (inferior).release ();
+}
+
static void
infpy_dealloc (PyObject *obj)
{
@@ -1062,6 +1078,8 @@ static gdb_PyGetSetDef inferior_object_getset[] =
{ "progspace", infpy_get_progspace, NULL, "Program space of this inferior" },
{ "main_name", infpy_get_main_name, nullptr,
"Name of 'main' function, if known.", nullptr },
+ { "corefile", infpy_get_core_file, nullptr,
+ "The corefile loaded in to this inferior, or None.", nullptr },
{ NULL }
};
diff --git a/gdb/python/python-internal.h b/gdb/python/python-internal.h
index f61a1753ac4..1649bb83812 100644
--- a/gdb/python/python-internal.h
+++ b/gdb/python/python-internal.h
@@ -1113,6 +1113,14 @@ extern std::optional<int> gdbpy_print_insn (struct gdbarch *gdbarch,
CORE_ADDR address,
disassemble_info *info);
+/* Return the gdb.Corefile object representing the core file loaded into
+ the program space of INF, or None if there is no core file loaded. INF
+ must not be NULL. If an error occurs then NULL is returned, and a
+ suitable Python error will be set. */
+
+extern gdbpy_ref<> gdbpy_core_file_from_inferior (inferior *inf);
+
+
/* A wrapper for PyType_Ready that also automatically registers the
type in the appropriate module. Returns 0 on success, -1 on error.
If MOD is supplied, then the type is added to that module. If MOD
diff --git a/gdb/testsuite/gdb.python/py-corefile.c b/gdb/testsuite/gdb.python/py-corefile.c
new file mode 100644
index 00000000000..1334ff65143
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-corefile.c
@@ -0,0 +1,25 @@
+/* Copyright 2025 Free Software Foundation, Inc.
+
+ This file is part of GDB.
+
+ 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/>. */
+
+#include <stdlib.h>
+
+int
+main (void)
+{
+ /* With correct ulimit, etc. this should cause a core dump. */
+ abort ();
+}
diff --git a/gdb/testsuite/gdb.python/py-corefile.exp b/gdb/testsuite/gdb.python/py-corefile.exp
new file mode 100644
index 00000000000..e9254bd9e78
--- /dev/null
+++ b/gdb/testsuite/gdb.python/py-corefile.exp
@@ -0,0 +1,178 @@
+# 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/>.
+
+# This file is part of the GDB testsuite. It tests the core file
+# support in Python.
+
+require isnative
+
+load_lib gdb-python.exp
+
+require allow_python_tests
+
+standard_testfile
+
+if {[build_executable "build executable" $testfile $srcfile] == -1} {
+ return
+}
+
+set corefile [core_find $binfile]
+if {$corefile == ""} {
+ unsupported "couldn't create or find corefile"
+ return
+}
+
+# Create a copy of the corefile.
+set other_corefile [standard_output_file ${testfile}-other.core]
+remote_exec build "cp $corefile $other_corefile"
+
+clean_restart
+
+gdb_test_no_output "python inf = gdb.selected_inferior()" \
+ "capture current inferior"
+
+gdb_test "python print(inf.corefile)" "^None" \
+ "Inferior.corefile is None before loading a core file"
+
+gdb_test "core-file $corefile" ".*" \
+ "load core file"
+
+set file_re [string_to_regexp $corefile]
+gdb_test "python print(inf.corefile)" "^<gdb\\.Corefile inferior=1 filename='$file_re'>" \
+ "Inferior.corefile is a valid object after loading a core file"
+
+gdb_test_no_output "python core1=inf.corefile" "capture gdb.Corefile object"
+
+gdb_test "python print(core1.__dict__)" "^\\{\\}" \
+ "print Corefile.__dict__ when empty"
+
+gdb_test_no_output "python core1._my_attribute = \"Hello\"" \
+ "write new attribute into Corefile object"
+
+gdb_test "python print(core1._my_attribute)" "^Hello" \
+ "immediately read new attribute"
+
+gdb_test "python print(core1.__dict__)" "^\\{'_my_attribute': 'Hello'\\}" \
+ "print Corefile.__dict__ after adding an attribute"
+
+gdb_test "python print(core1.filename)" "^$file_re" \
+ "Corefile.filename attribute works as expected"
+
+gdb_test "python print(core1.is_valid())" "^True" \
+ "Corefile.is_valid() is True while corefile is loaded"
+
+gdb_test "core-file" "^No core file now\\." "unload current core file"
+
+gdb_test "python print(core1.is_valid())" "^False" \
+ "Corefile.is_valid() is False after corefile is unloaded"
+
+gdb_test "python print(core1.__dict__)" "^\\{'_my_attribute': 'Hello'\\}" \
+ "print Corefile.__dict__ with attribute when invalid"
+
+gdb_test "python print(core1)" "^<gdb\\.Corefile \\(invalid\\)>" \
+ "print an invalid gdb.Corefile object"
+
+gdb_test "python print(core1.filename)" \
+ [multi_line \
+ "Python Exception <class 'RuntimeError'>: Corefile no longer exists\\." \
+ "Error occurred in Python: Corefile no longer exists\\."] \
+ "error when reading filename from invalid Corefile"
+
+gdb_test "python print(inf.corefile)" "^None" \
+ "Inferior.corefile is None again after corefile unload"
+
+gdb_test "python print(core1._my_attribute)" "^Hello" \
+ "read new attribute from invalid core file"
+
+# Create a second inferior.
+gdb_test "add-inferior"
+gdb_test "inferior 2"
+
+with_test_prefix "in second inferior" {
+ gdb_test "core-file $corefile" ".*" \
+ "load core file"
+
+ gdb_test "python print(inf.corefile)" "^None" \
+ "first inferior still has no core file"
+
+ gdb_test_no_output "python core2=gdb.selected_inferior().corefile" \
+ "capture gdb.Corefile object"
+
+ # The _my_attribute was added to CORE1, not CORE2. Check it
+ # doesn't somehow appear on CORE2.
+ gdb_test "python print(core2._my_attribute)" \
+ "AttributeError.*: 'gdb\\.Corefile' object has no attribute '_my_attribute'" \
+ "try to read attribute that doesn't exist"
+
+ gdb_test "python print(core2.filename)" "^$file_re" \
+ "Corefile.filename attribute works as expected"
+
+ gdb_test "inferior 1"
+}
+
+# Read the name of the core file from the second program space while
+# the current program space is the first one.
+gdb_test "python print(core2.filename)" "^$file_re" \
+ "Corefile.filename attribute works from different progspace"
+
+# Load the other corefile into the first inferior.
+gdb_test "core $other_corefile" ".*" \
+ "load other corefile into inferior 1"
+
+# Delete the second inferior. We need to switch to the second
+# inferior and unload its corefile before we can do that. Then,
+# switch back to the first inferior, delete the second, and try to
+# read the filename of the core file from the (now deleted) second
+# inferior. We should get an error about the gdb.Corefile being
+# invalid.
+with_test_prefix "remove second inferior" {
+ gdb_test "inferior 2"
+
+ gdb_test "python print(inf.corefile.filename)" \
+ "^[string_to_regexp $other_corefile]" \
+ "read inferior 1 corefile when in inferior 2"
+
+ gdb_test_no_output "python core1=inf.corefile" \
+ "capture inferior 1 gdb.Corefile while in inferior 2"
+
+ # This is a new CORE1 object, check that _my_attribute is gone.
+ gdb_test "python print(core1._my_attribute)" \
+ "AttributeError.*: 'gdb\\.Corefile' object has no attribute '_my_attribute'" \
+ "try to read attribute that doesn't exist"
+
+ gdb_test "core-file"
+
+ gdb_test "python print(core2.filename)" \
+ [multi_line \
+ "Python Exception <class 'RuntimeError'>: Corefile no longer exists\\." \
+ "Error occurred in Python: Corefile no longer exists\\."] \
+ "error when reading filename from invalid Corefile"
+
+ gdb_test "inferior 1"
+
+ gdb_test "remove-inferiors 2"
+
+ gdb_test "python print(core2.is_valid())" "^False" \
+ "Corefile.is_valid() is False after corefile is unloaded, and Progspace is deleted"
+
+ gdb_test "python print(core2.filename)" \
+ [multi_line \
+ "Python Exception <class 'RuntimeError'>: Corefile no longer exists\\." \
+ "Error occurred in Python: Corefile no longer exists\\."] \
+ "error when reading filename of an invalid Corefile, from deleted program space"
+
+ gdb_test "python print(core1.is_valid())" "^True" \
+ "check inferior 1 core file is still valid"
+}
--
2.47.1
^ permalink raw reply [flat|nested] 38+ messages in thread
* [PATCHv2 2/3] gdb: make structured core file mappings processing global
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-09-23 13:44 ` Andrew Burgess
2025-10-13 13:57 ` Lancelot SIX
2025-09-23 13:44 ` [PATCHv2 3/3] gdb/python: add Corefile.mapped_files method Andrew Burgess
2 siblings, 1 reply; 38+ messages in thread
From: Andrew Burgess @ 2025-09-23 13:44 UTC (permalink / raw)
To: gdb-patches; +Cc: Andrew Burgess, Tom Tromey
In corelow.c, within core_target::build_file_mappings, we have code
that wraps around a call to gdbarch_read_core_file_mappings and
provides more structure to the results.
Specifically, gdbarch_read_core_file_mappings calls a callback once
for every region of every mapped file. The wrapper code groups all of
the mappings for one file into an instance of 'struct mapped_file',
this allows all of the mapped regions to be associated with the
build-id and filename of a file.
In the next commit I plan to make this information available via the
Python API, and so I need to allow access to this structured wrapping
outside of corelow.c.
This commit renames 'struct mapped_file' to 'struct core_mapped_file'
and moves the struct into gdbcore.h. Then a new global function
gdb_read_core_file_mappings is created into which I move the code to
build the structured data.
Then corelow.c is updated to call gdb_read_core_file_mappings.
This commit does not extend the Python API, that is for the next
commit.
There should be no user visible changes after this commit.
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=32844
Approved-By: Tom Tromey <tom@tromey.com>
---
gdb/corelow.c | 218 +++++++++++++++++++++++++++-----------------------
gdb/gdbcore.h | 43 ++++++++++
2 files changed, 160 insertions(+), 101 deletions(-)
diff --git a/gdb/corelow.c b/gdb/corelow.c
index 2f202dc1fbf..ee97e29556e 100644
--- a/gdb/corelow.c
+++ b/gdb/corelow.c
@@ -366,108 +366,27 @@ core_target::core_target ()
void
core_target::build_file_mappings ()
{
- /* Type holding information about a single file mapped into the inferior
- at the point when the core file was created. Associates a build-id
- with the list of regions the file is mapped into. */
- struct mapped_file
- {
- /* Type for a region of a file that was mapped into the inferior when
- the core file was generated. */
- struct region
- {
- /* Constructor. See member variables for argument descriptions. */
- region (CORE_ADDR start_, CORE_ADDR end_, CORE_ADDR file_ofs_)
- : start (start_),
- end (end_),
- file_ofs (file_ofs_)
- { /* Nothing. */ }
-
- /* The inferior address for the start of the mapped region. */
- CORE_ADDR start;
-
- /* The inferior address immediately after the mapped region. */
- CORE_ADDR end;
-
- /* The offset within the mapped file for this content. */
- CORE_ADDR file_ofs;
- };
-
- /* If not nullptr, then this is the build-id associated with this
- file. */
- const bfd_build_id *build_id = nullptr;
-
- /* If true then we have seen multiple different build-ids associated
- with the same filename. The build_id field will have been set back
- to nullptr, and we should not set build_id in future. */
- bool ignore_build_id_p = false;
-
- /* All the mapped regions of this file. */
- std::vector<region> regions;
- };
-
gdb::unordered_map<std::string, struct bfd *> bfd_map;
gdb::unordered_set<std::string> unavailable_paths;
/* All files mapped into the core file. The key is the filename. */
- gdb::unordered_map<std::string, mapped_file> mapped_files;
+ std::vector<core_mapped_file> mapped_files
+ = gdb_read_core_file_mappings (m_core_gdbarch,
+ current_program_space->core_bfd ());
- /* See linux_read_core_file_mappings() in linux-tdep.c for an example
- read_core_file_mappings method. */
- gdbarch_read_core_file_mappings (m_core_gdbarch,
- current_program_space->core_bfd (),
-
- /* After determining the number of mappings, read_core_file_mappings
- will invoke this lambda. */
- [&] (ULONGEST)
- {
- },
-
- /* read_core_file_mappings will invoke this lambda for each mapping
- that it finds. */
- [&] (int num, ULONGEST start, ULONGEST end, ULONGEST file_ofs,
- const char *filename, const bfd_build_id *build_id)
- {
- /* Architecture-specific read_core_mapping methods are expected to
- weed out non-file-backed mappings. */
- gdb_assert (filename != nullptr);
-
- /* Add this mapped region to the data for FILENAME. */
- mapped_file &file_data = mapped_files[filename];
- file_data.regions.emplace_back (start, end, file_ofs);
- if (build_id != nullptr && !file_data.ignore_build_id_p)
- {
- if (file_data.build_id == nullptr)
- file_data.build_id = build_id;
- else if (!build_id_equal (build_id, file_data.build_id))
- {
- warning (_("Multiple build-ids found for %ps"),
- styled_string (file_name_style.style (), filename));
- file_data.build_id = nullptr;
- file_data.ignore_build_id_p = true;
- }
- }
- });
-
- /* Get the build-id of the core file. */
- const bfd_build_id *core_build_id
- = build_id_bfd_get (current_program_space->core_bfd ());
-
- for (const auto &[filename, file_data] : mapped_files)
+ for (const core_mapped_file &file_data : mapped_files)
{
- /* If this mapped file has the same build-id as was discovered for
- the core-file itself, then we assume this is the main
- executable. Record the filename as we can use this later. */
- if (file_data.build_id != nullptr
- && m_expected_exec_filename.empty ()
- && build_id_equal (file_data.build_id, core_build_id))
- m_expected_exec_filename = filename;
+ /* If this mapped file is marked as the main executable then record
+ the filename as we can use this later. */
+ if (file_data.is_main_exec && m_expected_exec_filename.empty ())
+ m_expected_exec_filename = file_data.filename;
/* Use exec_file_find() to do sysroot expansion. It'll
also strip the potential sysroot "target:" prefix. If
there is no sysroot, an equivalent (possibly more
canonical) pathname will be provided. */
gdb::unique_xmalloc_ptr<char> expanded_fname
- = exec_file_find (filename.c_str (), nullptr);
+ = exec_file_find (file_data.filename.c_str (), nullptr);
bool build_id_mismatch = false;
if (expanded_fname != nullptr && file_data.build_id != nullptr)
@@ -509,7 +428,7 @@ core_target::build_file_mappings ()
{
abfd = find_objfile_by_build_id (current_program_space,
file_data.build_id,
- filename.c_str ());
+ file_data.filename.c_str ());
if (abfd != nullptr)
{
@@ -527,7 +446,7 @@ core_target::build_file_mappings ()
}
std::vector<mem_range> ranges;
- for (const mapped_file::region ®ion : file_data.regions)
+ for (const core_mapped_file::region ®ion : file_data.regions)
ranges.emplace_back (region.start, region.end - region.start);
if (expanded_fname == nullptr
@@ -545,7 +464,7 @@ core_target::build_file_mappings ()
bool content_is_in_core_file_p = true;
/* Record all regions for this file as unavailable. */
- for (const mapped_file::region ®ion : file_data.regions)
+ for (const core_mapped_file::region ®ion : file_data.regions)
{
/* Check to see if the region is available within the core
file. */
@@ -577,33 +496,33 @@ core_target::build_file_mappings ()
if (build_id_mismatch)
{
if (expanded_fname == nullptr
- || filename == expanded_fname.get ())
+ || file_data.filename == expanded_fname.get ())
warning (_("File %ps doesn't match build-id from core-file "
"during file-backed mapping processing"),
styled_string (file_name_style.style (),
- filename.c_str ()));
+ file_data.filename.c_str ()));
else
warning (_("File %ps which was expanded to %ps, doesn't match "
"build-id from core-file during file-backed "
"mapping processing"),
styled_string (file_name_style.style (),
- filename.c_str ()),
+ file_data.filename.c_str ()),
styled_string (file_name_style.style (),
expanded_fname.get ()));
}
else if (!content_is_in_core_file_p)
{
if (expanded_fname == nullptr
- || filename == expanded_fname.get ())
+ || file_data.filename == expanded_fname.get ())
warning (_("Can't open file %ps during file-backed mapping "
"note processing"),
styled_string (file_name_style.style (),
- filename.c_str ()));
+ file_data.filename.c_str ()));
else
warning (_("Can't open file %ps which was expanded to %ps "
"during file-backed mapping note processing"),
styled_string (file_name_style.style (),
- filename.c_str ()),
+ file_data.filename.c_str ()),
styled_string (file_name_style.style (),
expanded_fname.get ()));
}
@@ -617,7 +536,7 @@ core_target::build_file_mappings ()
abfd.get ());
/* Create sections for each mapped region. */
- for (const mapped_file::region ®ion : file_data.regions)
+ for (const core_mapped_file::region ®ion : file_data.regions)
{
/* Make new BFD section. All sections have the same name,
which is permitted by bfd_make_section_anyway(). */
@@ -653,7 +572,7 @@ core_target::build_file_mappings ()
soname = gdb_bfd_read_elf_soname (actual_filename);
}
- m_mapped_file_info.add (soname.get (), filename.c_str (),
+ m_mapped_file_info.add (soname.get (), file_data.filename.c_str (),
actual_filename, std::move (ranges),
file_data.build_id);
}
@@ -2163,6 +2082,103 @@ mapped_file_info::lookup (const char *filename,
/* See gdbcore.h. */
+std::vector<core_mapped_file>
+gdb_read_core_file_mappings (struct gdbarch *gdbarch, struct bfd *cbfd)
+{
+ std::vector<core_mapped_file> results;
+
+ /* A map entry used while building RESULTS. */
+ struct map_entry
+ {
+ explicit map_entry (core_mapped_file *ptr)
+ : file_data (ptr)
+ { /* Nothing. */ }
+
+ /* Points to an entry in RESULTS, this allows entries to be quickly
+ looked up and updated as new mappings are read. */
+ core_mapped_file *file_data = nullptr;
+
+ /* If true then we have seen multiple different build-ids associated
+ with the filename of FILE_DATA. The FILE_DATA->build_id field will
+ have been set to nullptr, and we should not set FILE_DATA->build_id
+ in future. */
+ bool ignore_build_id_p = false;
+ };
+
+ /* All files mapped into the core file. The key is the filename. */
+ gdb::unordered_map<std::string, map_entry> mapped_files;
+
+ /* Get the build-id of the core file. At least on Linux, this will be
+ the build-id for the main executable. If other targets add the
+ gdbarch_read_core_file_mappings method, then it might turn out that
+ this logic is no longer true, in which case this might need to move
+ into the gdbarch_read_core_file_mappings method. */
+ const bfd_build_id *core_build_id = build_id_bfd_get (cbfd);
+
+ /* See linux_read_core_file_mappings() in linux-tdep.c for an example
+ read_core_file_mappings method. */
+ gdbarch_read_core_file_mappings (gdbarch, cbfd,
+ /* After determining the number of mappings, read_core_file_mappings
+ will invoke this lambda. */
+ [&] (ULONGEST)
+ {
+ },
+
+ /* read_core_file_mappings will invoke this lambda for each mapping
+ that it finds. */
+ [&] (int num, ULONGEST start, ULONGEST end, ULONGEST file_ofs,
+ const char *filename, const bfd_build_id *build_id)
+ {
+ /* Architecture-specific read_core_mapping methods are expected to
+ weed out non-file-backed mappings. */
+ gdb_assert (filename != nullptr);
+
+ /* Add this mapped region to the data for FILENAME. */
+ auto iter = mapped_files.find (filename);
+ if (iter == mapped_files.end ())
+ {
+ /* Create entry in results list. */
+ results.emplace_back ();
+
+ /* The entry to be added to the lookup map. */
+ map_entry entry (&results.back ());
+ entry.file_data->filename = filename;
+
+ /* Add entry to the quick lookup map and update ITER. */
+ auto inserted_result
+ = mapped_files.insert ({filename, std::move (entry)});
+ gdb_assert (inserted_result.second);
+ iter = inserted_result.first;
+ }
+
+ core_mapped_file &file_data = *iter->second.file_data;
+ bool &ignore_build_id_p = iter->second.ignore_build_id_p;
+
+ file_data.regions.emplace_back (start, end, file_ofs);
+ if (build_id != nullptr && !ignore_build_id_p)
+ {
+ if (file_data.build_id == nullptr)
+ file_data.build_id = build_id;
+ else if (!build_id_equal (build_id, file_data.build_id))
+ {
+ warning (_("Multiple build-ids found for %ps"),
+ styled_string (file_name_style.style (), filename));
+ file_data.build_id = nullptr;
+ ignore_build_id_p = true;
+ }
+ }
+
+ if (build_id != nullptr
+ && core_build_id != nullptr
+ && build_id_equal (build_id, core_build_id))
+ file_data.is_main_exec = true;
+ });
+
+ return results;
+}
+
+/* See gdbcore.h. */
+
std::optional <core_target_mapped_file_info>
core_target_find_mapped_file (const char *filename,
std::optional<CORE_ADDR> addr)
diff --git a/gdb/gdbcore.h b/gdb/gdbcore.h
index e0e3843c97b..23432ecbd07 100644
--- a/gdb/gdbcore.h
+++ b/gdb/gdbcore.h
@@ -258,4 +258,47 @@ std::optional<core_target_mapped_file_info>
core_target_find_mapped_file (const char *filename,
std::optional<CORE_ADDR> addr);
+/* Type holding information about a single file mapped into the inferior
+ at the point when the core file was created. Associates a build-id
+ with the list of regions the file is mapped into. */
+struct core_mapped_file
+{
+ /* Type for a region of a file that was mapped into the inferior when
+ the core file was generated. */
+ struct region
+ {
+ /* Constructor. See member variables for argument descriptions. */
+ region (CORE_ADDR start_, CORE_ADDR end_, CORE_ADDR file_ofs_)
+ : start (start_),
+ end (end_),
+ file_ofs (file_ofs_)
+ { /* Nothing. */ }
+
+ /* The inferior address for the start of the mapped region. */
+ CORE_ADDR start;
+
+ /* The inferior address immediately after the mapped region. */
+ CORE_ADDR end;
+
+ /* The offset within the mapped file for this content. */
+ CORE_ADDR file_ofs;
+ };
+
+ /* The filename as recorded in the core file. */
+ std::string filename;
+
+ /* If not nullptr, then this is the build-id associated with this
+ file. */
+ const bfd_build_id *build_id = nullptr;
+
+ /* All the mapped regions of this file. */
+ std::vector<region> regions;
+
+ /* True if this is the main executable. */
+ bool is_main_exec = false;
+};
+
+extern std::vector<core_mapped_file> gdb_read_core_file_mappings
+ (struct gdbarch *gdbarch, struct bfd *cbfd);
+
#endif /* GDB_GDBCORE_H */
--
2.47.1
^ permalink raw reply [flat|nested] 38+ messages in thread
* [PATCHv2 3/3] gdb/python: add Corefile.mapped_files method
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-09-23 13:44 ` [PATCHv2 2/3] gdb: make structured core file mappings processing global Andrew Burgess
@ 2025-09-23 13:44 ` Andrew Burgess
2025-10-03 19:15 ` Tom Tromey
2 siblings, 1 reply; 38+ messages in thread
From: Andrew Burgess @ 2025-09-23 13:44 UTC (permalink / raw)
To: gdb-patches; +Cc: Andrew Burgess
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 | 21 ++
gdb/doc/python.texi | 61 ++++
gdb/python/py-corefile.c | 397 +++++++++++++++++++++++
gdb/testsuite/gdb.python/py-corefile.exp | 58 ++++
gdb/testsuite/gdb.python/py-corefile.py | 144 ++++++++
5 files changed, 681 insertions(+)
create mode 100644 gdb/testsuite/gdb.python/py-corefile.py
diff --git a/gdb/NEWS b/gdb/NEWS
index 07838e41bd2..5cdd81a6ff3 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -45,6 +45,27 @@ single-inf-arg in qSupported
contains the gdb.Corefile object if a core file is loaded into
the inferior, otherwise, this contains None.
+ ** New gdb.Corefile class which represents a loaded core file. This
+ 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). 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
+ the inferior, otherwise, this contains None.
+
*** Changes in GDB 17
* Debugging Linux programs that use x86-64 or x86-64 with 32-bit pointer
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
index d25567e8943..b37d84a989e 100644
--- a/gdb/doc/python.texi
+++ b/gdb/doc/python.texi
@@ -8667,12 +8667,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 f199a2c82be..757369fa0a3 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 Tuple of gdb.CorefileMappedFile objects. This tuple 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 Tuple). */
+ 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. */
@@ -94,6 +146,7 @@ gdbpy_core_file_from_inferior (inferior *inf)
cfpy_dealloc will be called, which requires that the 'inferior' be
set to NULL. */
object->inferior = nullptr;
+ object->mapped_files = nullptr;
object->dict = PyDict_New ();
if (object->dict == nullptr)
return nullptr;
@@ -168,6 +221,123 @@ 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<> tuple (PyTuple_New (mapped_files.size ()));
+ if (tuple == nullptr)
+ return nullptr;
+
+ /* Create each gdb.CorefileMappedFile object. */
+ Py_ssize_t tuple_idx = 0;
+ 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 (PyTuple_New (file.regions.size ()));
+ if (regions == nullptr)
+ return nullptr;
+
+ /* Create all the gdb.CorefileMappedFileRegion objects. */
+ Py_ssize_t regions_idx = 0;
+ for (const core_mapped_file::region &r : file.regions)
+ {
+ /* Actually create the object. */
+ gdbpy_ref<corefile_mapped_file_region_object> region_obj
+ (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 (PyTuple_SetItem (regions.get (), regions_idx++,
+ (PyObject *) region_obj.release ()) < 0)
+ return nullptr;
+ }
+
+ /* Actually create the gdb.CorefileMappedFile object. */
+ gdbpy_ref<corefile_mapped_file_object> entry
+ (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 (PyTuple_SetItem (tuple.get (), tuple_idx++,
+ (PyObject *) entry.release ()) < 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 = tuple.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. */
@@ -191,6 +361,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);
}
@@ -215,6 +386,114 @@ 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;
+
+ return gdb_py_object_from_ulongest (obj->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;
+
+ return gdb_py_object_from_ulongest (obj->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;
+
+ return gdb_py_object_from_ulongest (obj->file_offset).release ();
+}
+
+\f
+
static int
gdbpy_initialize_corefile ()
{
@@ -224,6 +503,12 @@ gdbpy_initialize_corefile ()
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;
}
@@ -245,6 +530,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 }
};
@@ -289,3 +578,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 e9254bd9e78..3b57cc0e250 100644
--- a/gdb/testsuite/gdb.python/py-corefile.exp
+++ b/gdb/testsuite/gdb.python/py-corefile.exp
@@ -176,3 +176,61 @@ 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"
+
+ # Check that the mapped files "list" is actually an immutable
+ # tuple.
+ gdb_test_no_output "python core = gdb.selected_inferior().corefile"
+ gdb_test_no_output "python mapped_files = core.mapped_files()"
+ gdb_test "python print(type(mapped_files))" \
+ "^<class 'tuple'>"
+ gdb_test "python mapped_files\[0\] = None" \
+ "'tuple' object does not support item assignment"
+ gdb_test "python print(mapped_files\[0\] is None)" "^False"
+
+ # And same for the list of regions for a mapped file.
+ gdb_test_no_output "python regions = mapped_files\[0\].regions"
+ gdb_test "python print(type(regions))" \
+ "^<class 'tuple'>"
+ gdb_test "python regions\[0\] = None" \
+ "'tuple' object does not support item assignment"
+}
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
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 1/3] gdb/python: introduce gdb.Corefile API
2025-09-16 17:25 ` Tom Tromey
@ 2025-09-23 13:50 ` Andrew Burgess
0 siblings, 0 replies; 38+ messages in thread
From: Andrew Burgess @ 2025-09-23 13:50 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
Tom Tromey <tom@tromey.com> writes:
>>>>>> "Andrew" == Andrew Burgess <aburgess@redhat.com> writes:
>
> Andrew> This commit starts adding some core file related features to the
> Andrew> Python API.
>
> Andrew> In this initial commit I've tried to keep the changes as small as
> Andrew> possible for easy review.
>
> Thanks for doing this.
>
> Andrew> +++ b/gdb/python/py-corefile.c
> Andrew> @@ -0,0 +1,293 @@
> Andrew> +/* Python interface to core files.
> Andrew> +
> Andrew> + Copyright (C) 2010-2025 Free Software Foundation, Inc.
>
> Date seems like copy-paste error.
Fixed.
>
> Andrew> + if (result == nullptr)
> Andrew> + {
> Andrew> + gdbpy_ref<corefile_object> object
> Andrew> + ((corefile_object *) PyObject_New (corefile_object,
> Andrew> + &corefile_object_type));
> Andrew> + if (object == nullptr)
> Andrew> + return nullptr;
> Andrew> +
> Andrew> + object->dict = PyDict_New ();
> Andrew> + if (object->dict == nullptr)
> Andrew> + return nullptr;
> Andrew> + object->inferior = inf;
>
> I don't recall how Python objects are initialized, so I wonder if we
> could end up in a situation where PyDict_New fails, and then
> object->inferior is uninitialized.
You are correct to worry. PyObject_New only initialises the Python
object header, not any of the other fields, i.e. 'inferior' will indeed
be uninitialised.
> cfpy_dealloc checks corefile->inferior so I guess it could matter.
> So if Python doesn't memset the object to zero, I guess object->inferior
> should be initialized before the dict.
This is fixed in V2. I claim we need to first initialise the 'inferior'
field to NULL. if the PyDict_New call fails then we'll end up in
cfpy_dealloc as you say, and that only expects to see invalidated
(inferior == NULL) core file objects.
>
> Andrew> +/* Callback from gdb::observers::core_file_changed. The core file in
> Andrew> + PSPACE has been changed. */
> Andrew> +
> Andrew> +static void
> Andrew> +cfpy_corefile_changed (inferior *inf)
> Andrew> +{
> Andrew> + if (!gdb_python_initialized)
> Andrew> + return;
> Andrew> +
> Andrew> + gdbpy_enter enter_py;
> Andrew> +
> Andrew> + /* Get any existing corefile_object for PSPACE. */
> Andrew> + corefile_object *object = cfpy_inferior_corefile_data_key.get (inf);
> Andrew> +
> Andrew> + /* If we have an object, then... */
> Andrew> + if (object != nullptr)
> Andrew> + {
> Andrew> + /* Clearing the inferior pointer marks the gdb.Corefile as invalid. */
> Andrew> + object->inferior = nullptr;
> Andrew> +
> Andrew> + /* We're discarding our reference to the gdb.Corefile. */
>
> I wouldn't mind seeing this comment moved to
> inferior_corefile_deleter::operator().
>
> Andrew> + Py_XDECREF ((PyObject *) object);
> Andrew> + cfpy_inferior_corefile_data_key.set (inf, nullptr);
>
> I think the entire body of this function could be replaced with:
>
> cfpy_inferior_corefile_data_key.clear (inf);
>
> ... and this would remove duplicate code.
Done.
>
> Andrew> +static int CPYCHECKER_NEGATIVE_RESULT_SETS_EXCEPTION
> Andrew> +gdbpy_initialize_corefile (void)
>
> No more (void)
Fixed.
>
> I don't think the checker works for us any more -- at least, IIRC it
> didn't work for C++ stuff very well (I guess we could ask David Malcolm)
> and I haven't run it in years. So maybe those annotations aren't
> worthwhile any more.
I dropped the annotation.
>
> Andrew> + corefile_getset, /* tp_getset */
> Andrew> + 0, /* tp_base */
> Andrew> + 0, /* tp_dict */
> Andrew> + 0, /* tp_descr_get */
> Andrew> + 0, /* tp_descr_set */
> Andrew> + offsetof (corefile_object, dict), /* tp_dictoffset */
>
> I think if you want __dict__ to work you have to implement it by hand,
> see gdb_py_generic_dict and its various users. I.e., I think the getset
> object needs another entry.
I hadn't even realised this was a thing. I've added this in now. The
code as it was already supported doing things like:
core = gdb.selected_inferior().corefile
core._my_attribute = "blah blah blah"
which is how I've always used this feature. But users can now:
core.__dict__['_my_attribute'] = "blah blah blah"
too. It doesn't hurt to be consistent with other objects that implement
this feature.
I posted a V2 series with these fixes included.
Thanks,
Andrew
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCH 3/3] gdb/python: add Corefile.mapped_files method
2025-09-16 17:54 ` Tom Tromey
@ 2025-09-23 13:52 ` Andrew Burgess
0 siblings, 0 replies; 38+ messages in thread
From: Andrew Burgess @ 2025-09-23 13:52 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches
Tom Tromey <tom@tromey.com> writes:
>>>>>> "Andrew" == Andrew Burgess <aburgess@redhat.com> writes:
>
> Andrew> Add a new Corefile.mapped_files method which returns a list of
> Andrew> gdb.CorefileMappedFile objects.
>
> Andrew> Each gdb.CorefileMappedFile object represents a file that was mapped
> Andrew> into the process when the core file was created.
>
> Thanks.
>
> Andrew> + /* A List of gdb.CorefileMappedFile objects. This list is only created
> Andrew> + the first time the user calls gdb.Corefile.mapped_files(), the result
> Andrew> + is cached here. If this pointer is not NULL then this is an owning
> Andrew> + pointer (i.e. this owns a reference to the List). */
> Andrew> + PyObject *mapped_files;
>
> A Python list is a mutable object, so I think user code could mess
> around with this data.
>
> IMO it would be better to make this a tuple instead.
>
> Andrew> + /* A List of gdb.CorefileMappedFileRegion objects. */
> Andrew> + PyObject *regions;
>
> Same here. Though I wonder if that's overly paranoid of me.
No, I think this is the right amount of paranoid. I've updated both
these to Tuples, and extended the tests to check these are not
modifiable.
>
> Andrew> + object->mapped_files = nullptr;
>
> Perhaps this has to be hoisted if the initialization of object->inferior
> does (see earlier review).
Done.
>
> Andrew> + gdbpy_ref<corefile_mapped_file_region_object> region_obj
> Andrew> + ((corefile_mapped_file_region_object *) PyObject_New
>
> I think the cast of the return value of PyObject_New shouldn't be
> needed. PyObject_New is a macro that supplies the cast.
>
> There were a few cases of this.
Fixed the two added in this commit. I also fixed one from patch #1 too.
>
> Andrew> + gdbpy_ref<> start (gdb_py_object_from_ulongest (obj->start));
> Andrew> + if (start == nullptr)
> Andrew> + return nullptr;
> Andrew> +
> Andrew> + return start.release ();
>
> The null check isn't really needed since the release does the same.
> I think it's normal to just write
>
> return gdb_py_object_from_ulongest (obj->start).release ();
Switched to this style. I was already using this for some of the string
creation stuff, so I'm not sure why I did it differently here.
I posted a V2 series with all these fixes.
Thanks,
Andrew
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCHv2 1/3] gdb/python: introduce gdb.Corefile API
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
0 siblings, 1 reply; 38+ messages in thread
From: Tom Tromey @ 2025-10-03 18:56 UTC (permalink / raw)
To: Andrew Burgess; +Cc: gdb-patches, Eli Zaretskii
>>>>> "Andrew" == Andrew Burgess <aburgess@redhat.com> writes:
Andrew> This commit starts adding some core file related features to the
Andrew> Python API.
Thanks.
Andrew> +/* Store a gdb.Corefile object in an inferior's registry. */
Andrew> +
Andrew> +static const registry<inferior>::key<corefile_object,
Andrew> + inferior_corefile_deleter>
Andrew> + cfpy_inferior_corefile_data_key;
I suspect this should now use the new python-specific registry
approaches, see gdbpy_registry.
Andrew> + bfd *abfd= obj->inferior->pspace->core_bfd ();
Nit: missing space before '='.
Tom
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCHv2 3/3] gdb/python: add Corefile.mapped_files method
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
0 siblings, 1 reply; 38+ messages in thread
From: Tom Tromey @ 2025-10-03 19:15 UTC (permalink / raw)
To: Andrew Burgess; +Cc: gdb-patches
Andrew> Add a new Corefile.mapped_files method which returns a list of
Andrew> gdb.CorefileMappedFile objects.
Andrew> Each gdb.CorefileMappedFile object represents a file that was mapped
Andrew> into the process when the core file was created.
Andrew> + ** New Inferior.corefile attribute. This read only attribute
Andrew> + contains the gdb.Corefile object if a core file is loaded into
Andrew> + the inferior, otherwise, this contains None.
This hunk is duplicated, it also appears in patch 1.
Other than that this looks good to me.
Approved-By: Tom Tromey <tom@tromey.com>
Tom
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCHv2 1/3] gdb/python: introduce gdb.Corefile API
2025-10-03 18:56 ` Tom Tromey
@ 2025-10-06 8:54 ` Andrew Burgess
2025-10-06 15:39 ` Tom Tromey
0 siblings, 1 reply; 38+ messages in thread
From: Andrew Burgess @ 2025-10-06 8:54 UTC (permalink / raw)
To: Tom Tromey; +Cc: gdb-patches, Eli Zaretskii
Tom Tromey <tom@tromey.com> writes:
>>>>>> "Andrew" == Andrew Burgess <aburgess@redhat.com> writes:
>
> Andrew> This commit starts adding some core file related features to the
> Andrew> Python API.
>
> Thanks.
>
> Andrew> +/* Store a gdb.Corefile object in an inferior's registry. */
> Andrew> +
> Andrew> +static const registry<inferior>::key<corefile_object,
> Andrew> + inferior_corefile_deleter>
> Andrew> + cfpy_inferior_corefile_data_key;
>
> I suspect this should now use the new python-specific registry
> approaches, see gdbpy_registry.
I took a look at this, and I don't think gdbpy_registry is the right
solution.
1. It is designed to hold objects which are stored on either an
objfile or a gdbarch, e.g. types. The use of templates allows us
to avoid the duplication needed to deal with accessing the same
type of "thing" either from the objfile or the gdbarch. In my case
I need to store on the inferior.
2. It is designed for cases where each objfile or gdbarch can hold
many "things" of a given type. E.g. types, as such within each
registry entry the "things" are stored in some container, like a
map or set. In my case each inferior has 0 or 1 associated core
files, so adding a set or map would be unnecessary.
I'm sure with a little additional effort we _could_ generalise
gdbpy_registry more to make it apply in my case, but I don't think that
would actually add any value. The existing code, for the cases in which
it applies, serves to remove some duplication. But for cases like mine,
I don't think gdbpy_registry would remove any code.
The only benefit of extending gdbpy_registry would be that _every_ use
of a registry in Python code could, potentially, be made to use the same
interface. But at a cost of making that interface more complex (to
handle the different cases).
Let me know your thoughts.
>
> Andrew> + bfd *abfd= obj->inferior->pspace->core_bfd ();
>
> Nit: missing space before '='.
Fixed.
Thanks,
Andrew
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCHv2 1/3] gdb/python: introduce gdb.Corefile API
2025-10-06 8:54 ` Andrew Burgess
@ 2025-10-06 15:39 ` Tom Tromey
2025-10-06 16:13 ` Andrew Burgess
0 siblings, 1 reply; 38+ messages in thread
From: Tom Tromey @ 2025-10-06 15:39 UTC (permalink / raw)
To: Andrew Burgess; +Cc: Tom Tromey, gdb-patches, Eli Zaretskii
>>>>> "Andrew" == Andrew Burgess <aburgess@redhat.com> writes:
Andrew> I took a look at this, and I don't think gdbpy_registry is the right
Andrew> solution.
Good enough for me, thanks.
I think the patch is fine otherwise. Can't recall if I did this:
Approved-By: Tom Tromey <tom@tromey.com>
Tom
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCHv2 1/3] gdb/python: introduce gdb.Corefile API
2025-10-06 15:39 ` Tom Tromey
@ 2025-10-06 16:13 ` Andrew Burgess
0 siblings, 0 replies; 38+ messages in thread
From: Andrew Burgess @ 2025-10-06 16:13 UTC (permalink / raw)
To: Tom Tromey; +Cc: Tom Tromey, gdb-patches, Eli Zaretskii
Tom Tromey <tom@tromey.com> writes:
>>>>>> "Andrew" == Andrew Burgess <aburgess@redhat.com> writes:
>
> Andrew> I took a look at this, and I don't think gdbpy_registry is the right
> Andrew> solution.
>
> Good enough for me, thanks.
>
> I think the patch is fine otherwise. Can't recall if I did this:
> Approved-By: Tom Tromey <tom@tromey.com>
I pushed this now.
Thanks,
Andrew
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCHv2 3/3] gdb/python: add Corefile.mapped_files method
2025-10-03 19:15 ` Tom Tromey
@ 2025-10-07 6:24 ` Tom de Vries
2025-10-07 12:21 ` Andrew Burgess
0 siblings, 1 reply; 38+ messages in thread
From: Tom de Vries @ 2025-10-07 6:24 UTC (permalink / raw)
To: Tom Tromey, Andrew Burgess; +Cc: gdb-patches
On 10/3/25 21:15, Tom Tromey wrote:
> Andrew> Add a new Corefile.mapped_files method which returns a list of
> Andrew> gdb.CorefileMappedFile objects.
>
> Andrew> Each gdb.CorefileMappedFile object represents a file that was mapped
> Andrew> into the process when the core file was created.
>
> Andrew> + ** New Inferior.corefile attribute. This read only attribute
> Andrew> + contains the gdb.Corefile object if a core file is loaded into
> Andrew> + the inferior, otherwise, this contains None.
>
> This hunk is duplicated, it also appears in patch 1.
>
> Other than that this looks good to me.
> Approved-By: Tom Tromey <tom@tromey.com>
Hi,
Starting with this commit, I see:
...
(gdb) check-build-ids^M
Python Exception <class 'AssertionError'>: build-id mismatch for
/lib64/libc.so.6^M
Error occurred in Python: build-id mismatch for /lib64/libc.so.6^M
(gdb) FAIL: gdb.python/py-corefile.exp: test mapped files data:
check-build-ids
...
Thanks,
- Tom
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCHv2 3/3] gdb/python: add Corefile.mapped_files method
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-16 20:00 ` Tom Tromey
0 siblings, 2 replies; 38+ messages in thread
From: Andrew Burgess @ 2025-10-07 12:21 UTC (permalink / raw)
To: Tom de Vries, Tom Tromey; +Cc: gdb-patches
Tom de Vries <tdevries@suse.de> writes:
> On 10/3/25 21:15, Tom Tromey wrote:
>> Andrew> Add a new Corefile.mapped_files method which returns a list of
>> Andrew> gdb.CorefileMappedFile objects.
>>
>> Andrew> Each gdb.CorefileMappedFile object represents a file that was mapped
>> Andrew> into the process when the core file was created.
>>
>> Andrew> + ** New Inferior.corefile attribute. This read only attribute
>> Andrew> + contains the gdb.Corefile object if a core file is loaded into
>> Andrew> + the inferior, otherwise, this contains None.
>>
>> This hunk is duplicated, it also appears in patch 1.
>>
>> Other than that this looks good to me.
>> Approved-By: Tom Tromey <tom@tromey.com>
>
> Hi,
>
> Starting with this commit, I see:
> ...
> (gdb) check-build-ids^M
> Python Exception <class 'AssertionError'>: build-id mismatch for
> /lib64/libc.so.6^M
> Error occurred in Python: build-id mismatch for /lib64/libc.so.6^M
> (gdb) FAIL: gdb.python/py-corefile.exp: test mapped files data:
> check-build-ids
> ...
So that test is checking that the build-ids of the objfiles that were
loaded match the build-ids pulled from the corefile.
For example, GDB should find a build-id for `/lib64/libc.so.6` in the
core file, then when GDB loads `/lib64/libc.so.6` an objfile is created,
we can also read the build-id via the objfile.
The failure tells us that for some reason these two methods to read the
build-id gave different results.
Now, `/lib64/libc.so.6` must have a build-id, otherwise the objfile
would return None for its build-id, and the check-build-ids command
would ignore this library and we shouldn't see an assert.
So for the assert to trigger one of these things must have happened:
+ Build-id is present in the core file, but GDB failed to extract it,
or extracted the wrong data. Resulting in either None, or a different
build-id, or
+ Build-id is not present in the core file, GDB will return None for
the build-id.
Of these two I suspect the second; for a period of time the GNU linker
was ... changed ... such that it no longer placed the build-id within
the first page of a generated ELF, as a result, the Linux kernel would
not include the build-id in core dumps.
We can check for the second case using:
readelf -WS /lib64/libc.so.6 | grep build-id
The output will be something like:
[ 2] .note.gnu.build-id NOTE 0000000000000370 000370 000024 00 A 0 0 4
It's the '000370' column we're interested in. If this value is greater
than a page size, then GDB isn't going to be able to find the build-id.
You could try applying the patch below. This isn't a real fix, but does
two things:
1. Ignores any None build-id values pulled from the core file. I
suspect this will be enough to get the test passing for you, but
the patch also does
2. prints both build-ids if there is a mismatch.
Unfortunately just doing (1) isn't a long term fix as this would also
ignore the case where a bug in GDB means that we fail to find the
build-id.
So what I'm going to do is try to extend the test so that we can use
maybe readelf like I show above to check if GDB _should_ be able to find
the build-id, and only run the build-id check _if_ we think GDB should
be able to find the build-ids.
Anyway, I'd be interested to hear if the patch resolves the failure for
you. If it doesn't then I'm on completely the wrong path and will need
to rethink.
Thanks,
Andrew
---
diff --git i/gdb/testsuite/gdb.python/py-corefile.py w/gdb/testsuite/gdb.python/py-corefile.py
index cffd037a23b..979a0281d12 100644
--- i/gdb/testsuite/gdb.python/py-corefile.py
+++ w/gdb/testsuite/gdb.python/py-corefile.py
@@ -101,9 +101,12 @@ class CheckBuildIds(gdb.Command):
p = pathlib.Path(m.filename).resolve()
b = m.build_id
+ if b is None:
+ continue
+
if p in path_to_build_id:
count += 1
- assert path_to_build_id[p] == b, "build-id mismatch for %s" % p
+ assert path_to_build_id[p] == b, "build-id mismatch for %s; %s vs %s" % (p, path_to_build_id[p], b)
assert count > 0, "no mapped files checked"
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCHv2 3/3] gdb/python: add Corefile.mapped_files method
2025-10-07 12:21 ` Andrew Burgess
@ 2025-10-07 13:08 ` Tom de Vries
2025-10-07 13:26 ` Andrew Burgess
2025-10-16 20:00 ` Tom Tromey
1 sibling, 1 reply; 38+ messages in thread
From: Tom de Vries @ 2025-10-07 13:08 UTC (permalink / raw)
To: Andrew Burgess, Tom Tromey; +Cc: gdb-patches
On 10/7/25 14:21, Andrew Burgess wrote:
> Tom de Vries <tdevries@suse.de> writes:
>
>> On 10/3/25 21:15, Tom Tromey wrote:
>>> Andrew> Add a new Corefile.mapped_files method which returns a list of
>>> Andrew> gdb.CorefileMappedFile objects.
>>>
>>> Andrew> Each gdb.CorefileMappedFile object represents a file that was mapped
>>> Andrew> into the process when the core file was created.
>>>
>>> Andrew> + ** New Inferior.corefile attribute. This read only attribute
>>> Andrew> + contains the gdb.Corefile object if a core file is loaded into
>>> Andrew> + the inferior, otherwise, this contains None.
>>>
>>> This hunk is duplicated, it also appears in patch 1.
>>>
>>> Other than that this looks good to me.
>>> Approved-By: Tom Tromey <tom@tromey.com>
>>
>> Hi,
>>
>> Starting with this commit, I see:
>> ...
>> (gdb) check-build-ids^M
>> Python Exception <class 'AssertionError'>: build-id mismatch for
>> /lib64/libc.so.6^M
>> Error occurred in Python: build-id mismatch for /lib64/libc.so.6^M
>> (gdb) FAIL: gdb.python/py-corefile.exp: test mapped files data:
>> check-build-ids
>> ...
>
> So that test is checking that the build-ids of the objfiles that were
> loaded match the build-ids pulled from the corefile.
>
> For example, GDB should find a build-id for `/lib64/libc.so.6` in the
> core file, then when GDB loads `/lib64/libc.so.6` an objfile is created,
> we can also read the build-id via the objfile.
>
> The failure tells us that for some reason these two methods to read the
> build-id gave different results.
>
> Now, `/lib64/libc.so.6` must have a build-id, otherwise the objfile
> would return None for its build-id, and the check-build-ids command
> would ignore this library and we shouldn't see an assert.
>
> So for the assert to trigger one of these things must have happened:
>
> + Build-id is present in the core file, but GDB failed to extract it,
> or extracted the wrong data. Resulting in either None, or a different
> build-id, or
>
> + Build-id is not present in the core file, GDB will return None for
> the build-id.
>
> Of these two I suspect the second; for a period of time the GNU linker
> was ... changed ... such that it no longer placed the build-id within
> the first page of a generated ELF, as a result, the Linux kernel would
> not include the build-id in core dumps.
>
> We can check for the second case using:
>
> readelf -WS /lib64/libc.so.6 | grep build-id
>
> The output will be something like:
>
> [ 2] .note.gnu.build-id NOTE 0000000000000370 000370 000024 00 A 0 0 4
>
> It's the '000370' column we're interested in. If this value is greater
> than a page size, then GDB isn't going to be able to find the build-id.
>
Hi Andrew,
it seems to be the second case indeed:
...
$ readelf -WS /lib64/libc.so.6 | grep build-id
[22] .note.gnu.build-id NOTE 00000000001fa178 1fa178
000024 00 A 0 0 4
...
Sofar, I've only encountered this problem on Tumbleweed (
https://bugzilla.suse.com/show_bug.cgi?id=1240689 ), this is the first
test-case for which I run into it on Leap 15.6. But they have the same
linker base version: 2.43.1 (though soon to be upgraded, AFAIU), so that
makes sense.
> You could try applying the patch below. This isn't a real fix, but does
> two things:
>
> 1. Ignores any None build-id values pulled from the core file. I
> suspect this will be enough to get the test passing for you, but
> the patch also does
>
> 2. prints both build-ids if there is a mismatch.
>
> Unfortunately just doing (1) isn't a long term fix as this would also
> ignore the case where a bug in GDB means that we fail to find the
> build-id.
>
Ack.
> So what I'm going to do is try to extend the test so that we can use
> maybe readelf like I show above to check if GDB _should_ be able to find
> the build-id, and only run the build-id check _if_ we think GDB should
> be able to find the build-ids.
>
FWIW, there are a few other test-cases that could benefit from such a
mechanism as well.
> Anyway, I'd be interested to hear if the patch resolves the failure for
> you. If it doesn't then I'm on completely the wrong path and will need
> to rethink.
The patch does fix this.
And FWIW, reverting part 1 gives us:
...
(gdb) check-build-ids^M
Python Exception <class 'AssertionError'>: build-id mismatch for
/lib64/libc.so.6; 16dc6ffdd6165c6cb0346d683a041c90daa99730 vs None^M
Error occurred in Python: build-id mismatch for /lib64/libc.so.6;
16dc6ffdd6165c6cb0346d683a041c90daa99730 vs None^M
(gdb) FAIL: $exp: test mapped files data: check-build-ids
...
Thanks,
- Tom
> Thanks,
> Andrew
>
> ---
>
> diff --git i/gdb/testsuite/gdb.python/py-corefile.py w/gdb/testsuite/gdb.python/py-corefile.py
> index cffd037a23b..979a0281d12 100644
> --- i/gdb/testsuite/gdb.python/py-corefile.py
> +++ w/gdb/testsuite/gdb.python/py-corefile.py
> @@ -101,9 +101,12 @@ class CheckBuildIds(gdb.Command):
> p = pathlib.Path(m.filename).resolve()
> b = m.build_id
>
> + if b is None:
> + continue
> +
> if p in path_to_build_id:
> count += 1
> - assert path_to_build_id[p] == b, "build-id mismatch for %s" % p
> + assert path_to_build_id[p] == b, "build-id mismatch for %s; %s vs %s" % (p, path_to_build_id[p], b)
>
> assert count > 0, "no mapped files checked"
>
>
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCHv2 3/3] gdb/python: add Corefile.mapped_files method
2025-10-07 13:08 ` Tom de Vries
@ 2025-10-07 13:26 ` Andrew Burgess
2025-10-07 14:38 ` Andrew Burgess
0 siblings, 1 reply; 38+ messages in thread
From: Andrew Burgess @ 2025-10-07 13:26 UTC (permalink / raw)
To: Tom de Vries, Tom Tromey; +Cc: gdb-patches
Tom de Vries <tdevries@suse.de> writes:
> On 10/7/25 14:21, Andrew Burgess wrote:
>> Tom de Vries <tdevries@suse.de> writes:
>>
>>> On 10/3/25 21:15, Tom Tromey wrote:
>>>> Andrew> Add a new Corefile.mapped_files method which returns a list of
>>>> Andrew> gdb.CorefileMappedFile objects.
>>>>
>>>> Andrew> Each gdb.CorefileMappedFile object represents a file that was mapped
>>>> Andrew> into the process when the core file was created.
>>>>
>>>> Andrew> + ** New Inferior.corefile attribute. This read only attribute
>>>> Andrew> + contains the gdb.Corefile object if a core file is loaded into
>>>> Andrew> + the inferior, otherwise, this contains None.
>>>>
>>>> This hunk is duplicated, it also appears in patch 1.
>>>>
>>>> Other than that this looks good to me.
>>>> Approved-By: Tom Tromey <tom@tromey.com>
>>>
>>> Hi,
>>>
>>> Starting with this commit, I see:
>>> ...
>>> (gdb) check-build-ids^M
>>> Python Exception <class 'AssertionError'>: build-id mismatch for
>>> /lib64/libc.so.6^M
>>> Error occurred in Python: build-id mismatch for /lib64/libc.so.6^M
>>> (gdb) FAIL: gdb.python/py-corefile.exp: test mapped files data:
>>> check-build-ids
>>> ...
>>
>> So that test is checking that the build-ids of the objfiles that were
>> loaded match the build-ids pulled from the corefile.
>>
>> For example, GDB should find a build-id for `/lib64/libc.so.6` in the
>> core file, then when GDB loads `/lib64/libc.so.6` an objfile is created,
>> we can also read the build-id via the objfile.
>>
>> The failure tells us that for some reason these two methods to read the
>> build-id gave different results.
>>
>> Now, `/lib64/libc.so.6` must have a build-id, otherwise the objfile
>> would return None for its build-id, and the check-build-ids command
>> would ignore this library and we shouldn't see an assert.
>>
>> So for the assert to trigger one of these things must have happened:
>>
>> + Build-id is present in the core file, but GDB failed to extract it,
>> or extracted the wrong data. Resulting in either None, or a different
>> build-id, or
>>
>> + Build-id is not present in the core file, GDB will return None for
>> the build-id.
>>
>> Of these two I suspect the second; for a period of time the GNU linker
>> was ... changed ... such that it no longer placed the build-id within
>> the first page of a generated ELF, as a result, the Linux kernel would
>> not include the build-id in core dumps.
>>
>> We can check for the second case using:
>>
>> readelf -WS /lib64/libc.so.6 | grep build-id
>>
>> The output will be something like:
>>
>> [ 2] .note.gnu.build-id NOTE 0000000000000370 000370 000024 00 A 0 0 4
>>
>> It's the '000370' column we're interested in. If this value is greater
>> than a page size, then GDB isn't going to be able to find the build-id.
>>
>
> Hi Andrew,
>
> it seems to be the second case indeed:
> ...
> $ readelf -WS /lib64/libc.so.6 | grep build-id
> [22] .note.gnu.build-id NOTE 00000000001fa178 1fa178
> 000024 00 A 0 0 4
> ...
>
> Sofar, I've only encountered this problem on Tumbleweed (
> https://bugzilla.suse.com/show_bug.cgi?id=1240689 ), this is the first
> test-case for which I run into it on Leap 15.6. But they have the same
> linker base version: 2.43.1 (though soon to be upgraded, AFAIU), so that
> makes sense.
>
>> You could try applying the patch below. This isn't a real fix, but does
>> two things:
>>
>> 1. Ignores any None build-id values pulled from the core file. I
>> suspect this will be enough to get the test passing for you, but
>> the patch also does
>>
>> 2. prints both build-ids if there is a mismatch.
>>
>> Unfortunately just doing (1) isn't a long term fix as this would also
>> ignore the case where a bug in GDB means that we fail to find the
>> build-id.
>>
>
> Ack.
>
>> So what I'm going to do is try to extend the test so that we can use
>> maybe readelf like I show above to check if GDB _should_ be able to find
>> the build-id, and only run the build-id check _if_ we think GDB should
>> be able to find the build-ids.
>>
>
> FWIW, there are a few other test-cases that could benefit from such a
> mechanism as well.
>
>> Anyway, I'd be interested to hear if the patch resolves the failure for
>> you. If it doesn't then I'm on completely the wrong path and will need
>> to rethink.
>
> The patch does fix this.
>
> And FWIW, reverting part 1 gives us:
> ...
> (gdb) check-build-ids^M
> Python Exception <class 'AssertionError'>: build-id mismatch for
> /lib64/libc.so.6; 16dc6ffdd6165c6cb0346d683a041c90daa99730 vs None^M
> Error occurred in Python: build-id mismatch for /lib64/libc.so.6;
> 16dc6ffdd6165c6cb0346d683a041c90daa99730 vs None^M
> (gdb) FAIL: $exp: test mapped files data: check-build-ids
> ...
>
Thanks for the diagnostic help. I'll work on improving the test to try
and catch these cases. If that works, then we might be able to adopt
this to help with other tests. Keeping this in mind, I'll add helper
functions to lib/gdb.exp.
Thanks,
Andrew
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCHv2 3/3] gdb/python: add Corefile.mapped_files method
2025-10-07 13:26 ` Andrew Burgess
@ 2025-10-07 14:38 ` Andrew Burgess
2025-10-07 15:43 ` Tom de Vries
0 siblings, 1 reply; 38+ messages in thread
From: Andrew Burgess @ 2025-10-07 14:38 UTC (permalink / raw)
To: Tom de Vries, Tom Tromey; +Cc: gdb-patches
Andrew Burgess <aburgess@redhat.com> writes:
>
> Thanks for the diagnostic help. I'll work on improving the test to try
> and catch these cases. If that works, then we might be able to adopt
> this to help with other tests. Keeping this in mind, I'll add helper
> functions to lib/gdb.exp.
Hi Tom,
At your leisure, could you check that the patch below resolves the FAIL
you reported please.
Thanks,
Andrew
---
commit 6d6ec8321fcbbd12dc1544487367e2adefdc8ac9
Author: Andrew Burgess <aburgess@redhat.com>
Date: Tue Oct 7 15:24:01 2025 +0100
gdb/testsuite: fix failure from gdb.python/py-corefile.exp
After commit:
commit f69c1d03c4d6c68ae3f90facd63245426c028047
Date: Mon Aug 25 16:48:22 2025 +0100
gdb/python: add Corefile.mapped_files method
Tom reported a failure:
(gdb) check-build-ids
Python Exception <class 'AssertionError'>: build-id mismatch for /lib64/libc.so.6
Error occurred in Python: build-id mismatch for /lib64/libc.so.6
(gdb) FAIL: gdb.python/py-corefile.exp: test mapped files data: check-build-ids
The discussion thread can be found here:
https://inbox.sourceware.org/gdb-patches/de21b43c-e3bd-4354-aace-bd3f50c1c64c@suse.de
The problem is an issue where some versions of the linker didn't place
the build-id within the first page of an ELF. As a result, the Linux
kernel would not include the build-id in the generated core file, and
so GDB cannot to find the build-id.
In this patch I've added mitigation for this problem.
The check-build-ids command was defined in Python, and previously did
all the checks, printing "PASS" if everything was fine, otherwise
raising a Python exception.
This has now changed to a show-build-ids Python command. This
extracts the build-ids from the inferior's objfiles and core file, and
prints a table. The .exp test script can then process this table, and
decide if a difference is bad or not.
When processing the show-build-ids table, if the build-id from the
objfile object matches the build-id from the core file, then this is
fine.
If we have an objfile build-id and a different core file build-id,
then this is an error, GDB managed to find the _wrong_ build-id. Very
bad.
If we have an objfile build-id, and no core file build-id, then we use
a new helper function added to lib/gdb.exp,
expect_build_id_in_core_file. This helper uses readelf to check where
the build-id is located within the file. If the build-id is within
the first page of the ELF then we expect the build-id to appear in the
core file, and GDB should be able to find it, in this case we have
found a GDB bug, and the test will FAIL.
If however, readelf says that the build-id is outside the first page
of the ELF, then we don't expect GDB to be able to find the
build-id (as it likely isn't in the core file). In this case we don't
count this as a test failure.
I'm only aware of GNU/Linux forcing the first page of every mapped ELF
into the generated core files, and so, I've made the part of the test
that relies on this Linux only.
This change should resolve the FAIL that Tom reported.
diff --git a/gdb/testsuite/gdb.python/py-corefile.exp b/gdb/testsuite/gdb.python/py-corefile.exp
index 3b57cc0e250..866b60a0dbe 100644
--- a/gdb/testsuite/gdb.python/py-corefile.exp
+++ b/gdb/testsuite/gdb.python/py-corefile.exp
@@ -211,8 +211,53 @@ with_test_prefix "test mapped files data" {
"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 build-ids within the core file mapping data. I'm only
+ # aware of GNU/Linux placing the first page of each mapped ELF
+ # into the generated core file so that the build-id can be found.
+ if {[istarget *-*-linux*]} {
+ set results [list]
+ gdb_test_multiple "show-build-ids" "" {
+ -re "^show-build-ids\r\n" {
+ exp_continue
+ }
+ -re "^Objfile Build-Id\\s+Core File Build-Id\\s+File Name\\s*\r\n" {
+ exp_continue
+ }
+ -re "^(\\S+)\\s+(\\S+)\\s+(\[^\r\n\]+)\r\n" {
+ set objfile_build_id $expect_out(1,string)
+ set core_file_build_id $expect_out(2,string)
+ set file_name $expect_out(3,string)
+ lappend results [list $objfile_build_id \
+ $core_file_build_id \
+ $file_name]
+ exp_continue
+ }
+ -re "^$gdb_prompt " {
+ pass $gdb_test_name
+ }
+ }
+
+ set bad_count 0
+ foreach entry $results {
+ set objfile_build_id [lindex $entry 0]
+ set core_file_build_id [lindex $entry 1]
+ set file_name [lindex $entry 2]
+ if { $objfile_build_id ne $core_file_build_id } {
+ if { $core_file_build_id ne "None" } {
+ verbose -log "Mismatched build-ids $objfile_build_id vs $core_file_build_id for $file_name"
+ incr bad_count
+ } elseif { [expect_build_id_in_core_file $file_name] } {
+ verbose -log "Failed to find build-id for $file_name"
+ incr bad_count
+ } else {
+ verbose -log "This build-id was likely not in the core file"
+ }
+ }
+ }
+
+ gdb_assert { $bad_count == 0 } \
+ "found expected build-ids in core file"
+ }
# 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
index cffd037a23b..5c8ee7b2f0c 100644
--- a/gdb/testsuite/gdb.python/py-corefile.py
+++ b/gdb/testsuite/gdb.python/py-corefile.py
@@ -78,9 +78,23 @@ class InfoProcPyMappings(gdb.Command):
InfoProcPyMappings()
-class CheckBuildIds(gdb.Command):
+# Assume that a core file is currently loaded.
+#
+# Look through all the objfiles for the current inferior, and record
+# any that have a build-id.
+#
+# Then look through the core file mapped files. Look for entries that
+# correspond with the loaded objfiles. For these matching entries,
+# capture the build-id extracted from the core file.
+#
+# Finally, print a table with the build-id from the objfile, the
+# build-id from the core file, and the file name.
+#
+# This is then processed from the test script to check the build-ids
+# match.
+class ShowBuildIds(gdb.Command):
def __init__(self):
- gdb.Command.__init__(self, "check-build-ids", gdb.COMMAND_DATA)
+ gdb.Command.__init__(self, "show-build-ids", gdb.COMMAND_DATA)
def invoke(self, args, from_tty):
inf = gdb.selected_inferior()
@@ -88,12 +102,17 @@ class CheckBuildIds(gdb.Command):
path_to_build_id = {}
+ # Initial length based on column headings.
+ longest_build_id = 18
+
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
+ path_to_build_id[p] = {"objfile": b}
+ if len(b) > longest_build_id:
+ longest_build_id = len(b)
count = 0
core_mapped_files = inf.corefile.mapped_files()
@@ -101,16 +120,39 @@ class CheckBuildIds(gdb.Command):
p = pathlib.Path(m.filename).resolve()
b = m.build_id
+ if b is not None and len(b) > longest_build_id:
+ longest_build_id = len(b)
+
if p in path_to_build_id:
- count += 1
- assert path_to_build_id[p] == b, "build-id mismatch for %s" % p
+ path_to_build_id[p]["corefile"] = b
- assert count > 0, "no mapped files checked"
+ format_str = (
+ "%-" + str(longest_build_id) + "s %-" + str(longest_build_id) + "s %s"
+ )
- print("PASS")
+ def make_title(string, length=0):
+ if length > 0:
+ padding_len = length - len(string)
+ else:
+ padding_len = 0
+
+ padding = " " * padding_len
+ style = gdb.Style("title")
+ return style.apply(string) + padding
+
+ print(
+ "%s %s %s"
+ % (
+ make_title("Objfile Build-Id", longest_build_id),
+ make_title("Core File Build-Id", longest_build_id),
+ make_title("File Name"),
+ )
+ )
+ for p, b in path_to_build_id.items():
+ print(format_str % (b["objfile"], b["corefile"], p))
-CheckBuildIds()
+ShowBuildIds()
class CheckMainExec(gdb.Command):
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index e07aab4cdd8..5dfcfefd7c1 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -11596,6 +11596,63 @@ proc section_get {exec section} {
return $retval
}
+# Return true if we expect the build-id from FILENAME to be included
+# in a core file.
+#
+# On GNU/Linux, when creating a core file, the kernel places the first
+# page of an ELF into the core file. If the build-id is within that
+# page then GDB can find the build-id from the core file.
+#
+# This proc checks that the target is GNU/Linux, and then uses readelf
+# to find the offset of the build-id within the ELF. If there is a
+# build-id, and it is within the first page, then return true.
+# Otherwise, return false.
+
+proc expect_build_id_in_core_file { filename } {
+ # I'm not sure if other kernels take care to add the first page of
+ # each ELF into the core file. If they do then this test can be
+ # relaxed.
+ if {![istarget *-*-linux*]} {
+ return false
+ }
+
+ # Use readelf to find the build-id note in FILENAME.
+ set readelf_program [gdb_find_readelf]
+ set cmd [list $readelf_program -WS $filename | grep ".note.gnu.build-id"]
+ set res [catch {exec {*}$cmd} output]
+ verbose -log "running: $cmd"
+ verbose -log "result: $res"
+ verbose -log "output: $output"
+ if { $res != 0 } {
+ return false
+ }
+
+ # Extract the OFFSET from the readelf output.
+ set res [regexp {NOTE[ \t]+([0-9a-f]+)[ \t]+([0-9a-f]+)} \
+ $output dummy addr offset]
+ if { $res != 1 } {
+ return false
+ }
+
+ # Convert OFFSET to decimal.
+ set offset [expr 0x$offset + 0]
+
+ # Now figure out the page size. This should be fine for Linux
+ # hosts, see the istarget check above.
+ if {[catch {exec getconf PAGESIZE} page_size]} {
+ # Failed to fetch page size.
+ return false
+ }
+
+ # If the build-id is within the first page, then we expect the
+ # kernel to include it in the core file. There is actually a
+ # kernel setting (see coredump_filter) that could prevent this,
+ # but the default behaviour is to include the first page of the
+ # ELF, so for now, we just assume this is on.
+ verbose -log "Page size is $page_size, Offset is $offset"
+ return [expr $offset < $page_size]
+}
+
# Return 1 if the compiler supports __builtin_trap, else return 0.
gdb_caching_proc have_builtin_trap {} {
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCHv2 3/3] gdb/python: add Corefile.mapped_files method
2025-10-07 14:38 ` Andrew Burgess
@ 2025-10-07 15:43 ` Tom de Vries
2025-10-07 16:28 ` Andrew Burgess
0 siblings, 1 reply; 38+ messages in thread
From: Tom de Vries @ 2025-10-07 15:43 UTC (permalink / raw)
To: Andrew Burgess, Tom Tromey; +Cc: gdb-patches
[-- Attachment #1: Type: text/plain, Size: 627 bytes --]
On 10/7/25 16:38, Andrew Burgess wrote:
> Andrew Burgess <aburgess@redhat.com> writes:
>
>>
>> Thanks for the diagnostic help. I'll work on improving the test to try
>> and catch these cases. If that works, then we might be able to adopt
>> this to help with other tests. Keeping this in mind, I'll add helper
>> functions to lib/gdb.exp.
>
> Hi Tom,
>
> At your leisure, could you check that the patch below resolves the FAIL
> you reported please.
>
Hi Andrew,
on Leap 15.6, yes.
On Tumbleweed, unfortunately no.
I've attached the gdb.log here, in case you're curious. I'll take a
look tomorrow.
Thanks,
- Tom
[-- Attachment #2: gdb.log --]
[-- Type: text/x-log, Size: 27184 bytes --]
Test run by root on Tue Oct 7 15:34:58 2025
Native configuration is x86_64-pc-linux-gnu
=== gdb tests ===
Schedule of variations:
unix
Running target unix
Using /usr/share/dejagnu/baseboards/unix.exp as board description file for target.
Using /usr/share/dejagnu/config/unix.exp as generic interface file for target.
Using /data/vries/gdb/src/gdb/testsuite/config/unix.exp as tool-and-target-specific interface file.
Running /data/vries/gdb/src/gdb/testsuite/gdb.python/py-corefile.exp ...
Executing on build: rm -rf /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile (timeout = 300)
builtin_spawn -ignore SIGHUP rm -rf /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile
gdb_do_cache: allow_python_tests ( )
Executing on host: /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/../../gdb/gdb -nw -nx -q -iex "set height 0" -iex "set width 0" -data-directory /data/vries/gdb/tumbleweed-20251005/build/gdb/data-directory -iex "set interactive-mode on" --configuration (timeout = 300)
builtin_spawn -ignore SIGHUP /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/../../gdb/gdb -nw -nx -q -iex set height 0 -iex set width 0 -data-directory /data/vries/gdb/tumbleweed-20251005/build/gdb/data-directory -iex set interactive-mode on --configuration
This GDB was configured as follows:
configure --host=x86_64-pc-linux-gnu --target=x86_64-pc-linux-gnu
--enable-targets=all
--with-auto-load-dir=$debugdir:$datadir/auto-load
--with-auto-load-safe-path=$debugdir:$datadir/auto-load
--with-expat
--with-gdb-datadir=/data/vries/gdb/tumbleweed-20251005/install/share/gdb (relocatable)
--with-jit-reader-dir=/data/vries/gdb/tumbleweed-20251005/install/lib/gdb (relocatable)
--without-libunwind-ia64
--with-lzma
--without-babeltrace
--without-intel-pt
--with-xxhash
--with-python=/usr
--with-python-libdir=/usr/lib
--with-debuginfod
--with-curses
--without-guile
--without-amd-dbgapi
--enable-source-highlight
--enable-threading
--enable-tui
--without-system-readline
--with-separate-debug-dir=/usr/lib/debug
--with-system-gdbinit=/data/vries/gdb/system-gdbinit
("Relocatable" means the directory can be moved with the GDB installation
tree, and GDB will still find it.)
GNU Readline library version: 8.2 (internal)
gdb_do_cache: universal_compile_options_c ( )
Executing on host: gcc -fdiagnostics-color=never -c -o /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/temp/25044/ccopts.o /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/temp/25044/ccopts.c (timeout = 300)
builtin_spawn -ignore SIGHUP gcc -fdiagnostics-color=never -c -o /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/temp/25044/ccopts.o /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/temp/25044/ccopts.c
gdb_do_cache: get_compiler_info_1 ( c )
get_compiler_info: gcc-15-2-0
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
Executing on host: gcc -fno-stack-protector -fdiagnostics-color=never -c -g -o /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile0.o /data/vries/gdb/src/gdb/testsuite/gdb.python/py-corefile.c (timeout = 300)
builtin_spawn -ignore SIGHUP gcc -fno-stack-protector -fdiagnostics-color=never -c -g -o /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile0.o /data/vries/gdb/src/gdb/testsuite/gdb.python/py-corefile.c
gdb_do_cache: universal_compile_options_c ( )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
gdb_do_cache: get_compiler_info_1 ( c )
Executing on host: gcc -fno-stack-protector /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile0.o -fdiagnostics-color=never -g -lm -o /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile (timeout = 300)
builtin_spawn -ignore SIGHUP gcc -fno-stack-protector /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile0.o -fdiagnostics-color=never -g -lm -o /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile
Executing on build: mv /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/coredir.25044/core.py-corefile.25114.6a709f42a6b1.1759851299 /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile.core (timeout = 300)
builtin_spawn -ignore SIGHUP mv /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/coredir.25044/core.py-corefile.25114.6a709f42a6b1.1759851299 /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile.core
Executing on build: rmdir /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/coredir.25044 (timeout = 300)
builtin_spawn -ignore SIGHUP rmdir /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/coredir.25044
Executing on build: cp /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile.core /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile-other.core (timeout = 300)
builtin_spawn -ignore SIGHUP cp /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile.core /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile-other.core
Starting logfile: /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/gdb.in.1
builtin_spawn /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/../../gdb/gdb -nw -nx -q -iex set height 0 -iex set width 0 -data-directory /data/vries/gdb/tumbleweed-20251005/build/gdb/data-directory -iex set interactive-mode on
(gdb) set height 0
(gdb) set width 0
(gdb) dir
Reinitialize source path to empty? (y or n) y
Source directories searched: $cdir:$cwd
(gdb) dir /data/vries/gdb/binutils-gdb.git/gdb/testsuite/gdb.python
Source directories searched: /data/vries/gdb/binutils-gdb.git/gdb/testsuite/gdb.python:$cdir:$cwd
(gdb) python inf = gdb.selected_inferior()
(gdb) PASS: gdb.python/py-corefile.exp: capture current inferior
python print(inf.corefile)
None
(gdb) PASS: gdb.python/py-corefile.exp: Inferior.corefile is None before loading a core file
core-file /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile.core
[New LWP 25114]
Reading symbols from /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile...
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Core was generated by `/data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile'.
Program terminated with signal SIGABRT, Aborted.
#0 __pthread_kill_implementation (threadid=<optimized out>, signo=signo@entry=6, no_tid=no_tid@entry=0) at pthread_kill.c:44
warning: 44 pthread_kill.c: No such file or directory
(gdb) PASS: gdb.python/py-corefile.exp: load core file
python print(inf.corefile)
<gdb.Corefile inferior=1 filename='/data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile.core'>
(gdb) PASS: gdb.python/py-corefile.exp: Inferior.corefile is a valid object after loading a core file
python core1=inf.corefile
(gdb) PASS: gdb.python/py-corefile.exp: capture gdb.Corefile object
python print(core1.__dict__)
{}
(gdb) PASS: gdb.python/py-corefile.exp: print Corefile.__dict__ when empty
python core1._my_attribute = "Hello"
(gdb) PASS: gdb.python/py-corefile.exp: write new attribute into Corefile object
python print(core1._my_attribute)
Hello
(gdb) PASS: gdb.python/py-corefile.exp: immediately read new attribute
python print(core1.__dict__)
{'_my_attribute': 'Hello'}
(gdb) PASS: gdb.python/py-corefile.exp: print Corefile.__dict__ after adding an attribute
python print(core1.filename)
/data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile.core
(gdb) PASS: gdb.python/py-corefile.exp: Corefile.filename attribute works as expected
python print(core1.is_valid())
True
(gdb) PASS: gdb.python/py-corefile.exp: Corefile.is_valid() is True while corefile is loaded
core-file
No core file now.
(gdb) PASS: gdb.python/py-corefile.exp: unload current core file
python print(core1.is_valid())
False
(gdb) PASS: gdb.python/py-corefile.exp: Corefile.is_valid() is False after corefile is unloaded
python print(core1.__dict__)
{'_my_attribute': 'Hello'}
(gdb) PASS: gdb.python/py-corefile.exp: print Corefile.__dict__ with attribute when invalid
python print(core1)
<gdb.Corefile (invalid)>
(gdb) PASS: gdb.python/py-corefile.exp: print an invalid gdb.Corefile object
python print(core1.filename)
Python Exception <class 'RuntimeError'>: Corefile no longer exists.
Error occurred in Python: Corefile no longer exists.
(gdb) PASS: gdb.python/py-corefile.exp: error when reading filename from invalid Corefile
python print(inf.corefile)
None
(gdb) PASS: gdb.python/py-corefile.exp: Inferior.corefile is None again after corefile unload
python print(core1._my_attribute)
Hello
(gdb) PASS: gdb.python/py-corefile.exp: read new attribute from invalid core file
add-inferior
[New inferior 2]
Added inferior 2
(gdb) PASS: gdb.python/py-corefile.exp: add-inferior
inferior 2
[Switching to inferior 2 [<null>] (<noexec>)]
(gdb) PASS: gdb.python/py-corefile.exp: inferior 2
core-file /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile.core
[New LWP 25114]
Reading symbols from /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile...
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Core was generated by `/data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile'.
Program terminated with signal SIGABRT, Aborted.
#0 __pthread_kill_implementation (threadid=<optimized out>, signo=signo@entry=6, no_tid=no_tid@entry=0) at pthread_kill.c:44
warning: 44 pthread_kill.c: No such file or directory
(gdb) PASS: gdb.python/py-corefile.exp: in second inferior: load core file
python print(inf.corefile)
None
(gdb) PASS: gdb.python/py-corefile.exp: in second inferior: first inferior still has no core file
python core2=gdb.selected_inferior().corefile
(gdb) PASS: gdb.python/py-corefile.exp: in second inferior: capture gdb.Corefile object
python print(core2._my_attribute)
Python Exception <class 'AttributeError'>: 'gdb.Corefile' object has no attribute '_my_attribute'
Error occurred in Python: 'gdb.Corefile' object has no attribute '_my_attribute'
(gdb) PASS: gdb.python/py-corefile.exp: in second inferior: try to read attribute that doesn't exist
python print(core2.filename)
/data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile.core
(gdb) PASS: gdb.python/py-corefile.exp: in second inferior: Corefile.filename attribute works as expected
inferior 1
[Switching to inferior 1 [<null>] (/data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile)]
(gdb) PASS: gdb.python/py-corefile.exp: in second inferior: inferior 1
python print(core2.filename)
/data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile.core
(gdb) PASS: gdb.python/py-corefile.exp: Corefile.filename attribute works from different progspace
core /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile-other.core
[New LWP 25114]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Core was generated by `/data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile'.
Program terminated with signal SIGABRT, Aborted.
#0 __pthread_kill_implementation (threadid=<optimized out>, signo=signo@entry=6, no_tid=no_tid@entry=0) at pthread_kill.c:44
warning: 44 pthread_kill.c: No such file or directory
(gdb) PASS: gdb.python/py-corefile.exp: load other corefile into inferior 1
inferior 2
[Switching to inferior 2 [process 25114] (/data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile)]
[Switching to thread 2.1 (Thread 0x7ff03939a380 (LWP 25114))]
#0 __pthread_kill_implementation (threadid=<optimized out>, signo=signo@entry=6, no_tid=no_tid@entry=0) at pthread_kill.c:44
warning: 44 pthread_kill.c: No such file or directory
(gdb) PASS: gdb.python/py-corefile.exp: remove second inferior: inferior 2
python print(inf.corefile.filename)
/data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile-other.core
(gdb) PASS: gdb.python/py-corefile.exp: remove second inferior: read inferior 1 corefile when in inferior 2
python core1=inf.corefile
(gdb) PASS: gdb.python/py-corefile.exp: remove second inferior: capture inferior 1 gdb.Corefile while in inferior 2
python print(core1._my_attribute)
Python Exception <class 'AttributeError'>: 'gdb.Corefile' object has no attribute '_my_attribute'
Error occurred in Python: 'gdb.Corefile' object has no attribute '_my_attribute'
(gdb) PASS: gdb.python/py-corefile.exp: remove second inferior: try to read attribute that doesn't exist
core-file
No core file now.
(gdb) PASS: gdb.python/py-corefile.exp: remove second inferior: core-file
python print(core2.filename)
Python Exception <class 'RuntimeError'>: Corefile no longer exists.
Error occurred in Python: Corefile no longer exists.
(gdb) PASS: gdb.python/py-corefile.exp: remove second inferior: error when reading filename from invalid Corefile
inferior 1
[Switching to inferior 1 [process 25114] (/data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile)]
[Switching to thread 1.1 (Thread 0x7ff03939a380 (LWP 25114))]
#0 __pthread_kill_implementation (threadid=<optimized out>, signo=signo@entry=6, no_tid=no_tid@entry=0) at pthread_kill.c:44
warning: 44 pthread_kill.c: No such file or directory
(gdb) PASS: gdb.python/py-corefile.exp: remove second inferior: inferior 1
remove-inferiors 2
(gdb) PASS: gdb.python/py-corefile.exp: remove second inferior: remove-inferiors 2
python print(core2.is_valid())
False
(gdb) PASS: gdb.python/py-corefile.exp: remove second inferior: Corefile.is_valid() is False after corefile is unloaded, and Progspace is deleted
python print(core2.filename)
Python Exception <class 'RuntimeError'>: Corefile no longer exists.
Error occurred in Python: Corefile no longer exists.
(gdb) PASS: gdb.python/py-corefile.exp: remove second inferior: error when reading filename of an invalid Corefile, from deleted program space
python print(core1.is_valid())
True
(gdb) PASS: gdb.python/py-corefile.exp: remove second inferior: check inferior 1 core file is still valid
Starting logfile: /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/gdb.in.2
builtin_spawn /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/../../gdb/gdb -nw -nx -q -iex set height 0 -iex set width 0 -data-directory /data/vries/gdb/tumbleweed-20251005/build/gdb/data-directory -iex set interactive-mode on
(gdb) set height 0
(gdb) set width 0
(gdb) dir
Reinitialize source path to empty? (y or n) y
Source directories searched: $cdir:$cwd
(gdb) dir /data/vries/gdb/binutils-gdb.git/gdb/testsuite/gdb.python
Source directories searched: /data/vries/gdb/binutils-gdb.git/gdb/testsuite/gdb.python:$cdir:$cwd
(gdb) source /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile.py
Success
(gdb) PASS: gdb.python/py-corefile.exp: test mapped files data: source python script
core-file /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile.core
[New LWP 25114]
Reading symbols from /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile...
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
Core was generated by `/data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile'.
Program terminated with signal SIGABRT, Aborted.
#0 __pthread_kill_implementation (threadid=<optimized out>, signo=signo@entry=6, no_tid=no_tid@entry=0) at pthread_kill.c:44
warning: 44 pthread_kill.c: No such file or directory
(gdb) PASS: gdb.python/py-corefile.exp: test mapped files data: load core file
pipe info proc mappings | tee /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile-out-1.txt
Mapped address spaces:
Start Addr End Addr Size Offset File
0x0000000000400000 0x0000000000401000 0x1000 0x0 /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile
0x0000000000401000 0x0000000000402000 0x1000 0x1000 /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile
0x0000000000402000 0x0000000000403000 0x1000 0x2000 /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile
0x0000000000403000 0x0000000000404000 0x1000 0x2000 /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile
0x0000000000404000 0x0000000000405000 0x1000 0x3000 /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile
0x00007ff03939b000 0x00007ff0393c3000 0x28000 0x0 /usr/lib64/libc.so.6
0x00007ff0393c3000 0x00007ff03953b000 0x178000 0x28000 /usr/lib64/libc.so.6
0x00007ff03953b000 0x00007ff03958e000 0x53000 0x1a0000 /usr/lib64/libc.so.6
0x00007ff03958e000 0x00007ff039592000 0x4000 0x1f2000 /usr/lib64/libc.so.6
0x00007ff039592000 0x00007ff03959b000 0x9000 0x1f6000 /usr/lib64/libc.so.6
0x00007ff0395a3000 0x00007ff0395b4000 0x11000 0x0 /usr/lib64/libm.so.6
0x00007ff0395b4000 0x00007ff03963b000 0x87000 0x11000 /usr/lib64/libm.so.6
0x00007ff03963b000 0x00007ff03969b000 0x60000 0x98000 /usr/lib64/libm.so.6
0x00007ff03969b000 0x00007ff03969c000 0x1000 0xf7000 /usr/lib64/libm.so.6
0x00007ff03969c000 0x00007ff03969e000 0x2000 0xf8000 /usr/lib64/libm.so.6
0x00007ff0396a8000 0x00007ff0396a9000 0x1000 0x0 /usr/lib64/ld-linux-x86-64.so.2
0x00007ff0396a9000 0x00007ff0396d4000 0x2b000 0x1000 /usr/lib64/ld-linux-x86-64.so.2
0x00007ff0396d4000 0x00007ff0396df000 0xb000 0x2c000 /usr/lib64/ld-linux-x86-64.so.2
0x00007ff0396df000 0x00007ff0396e1000 0x2000 0x37000 /usr/lib64/ld-linux-x86-64.so.2
0x00007ff0396e1000 0x00007ff0396e3000 0x2000 0x39000 /usr/lib64/ld-linux-x86-64.so.2
(gdb) PASS: gdb.python/py-corefile.exp: test mapped files data: capture built-in mappings output
pipe info proc py-mappings | tee /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile-out-2.txt
Mapped address spaces:
Start Addr End Addr Size Offset File
0x0000000000400000 0x0000000000401000 0x1000 0x0 /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile
0x0000000000401000 0x0000000000402000 0x1000 0x1000 /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile
0x0000000000402000 0x0000000000403000 0x1000 0x2000 /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile
0x0000000000403000 0x0000000000404000 0x1000 0x2000 /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile
0x0000000000404000 0x0000000000405000 0x1000 0x3000 /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile
0x00007ff03939b000 0x00007ff0393c3000 0x28000 0x0 /usr/lib64/libc.so.6
0x00007ff0393c3000 0x00007ff03953b000 0x178000 0x28000 /usr/lib64/libc.so.6
0x00007ff03953b000 0x00007ff03958e000 0x53000 0x1a0000 /usr/lib64/libc.so.6
0x00007ff03958e000 0x00007ff039592000 0x4000 0x1f2000 /usr/lib64/libc.so.6
0x00007ff039592000 0x00007ff03959b000 0x9000 0x1f6000 /usr/lib64/libc.so.6
0x00007ff0395a3000 0x00007ff0395b4000 0x11000 0x0 /usr/lib64/libm.so.6
0x00007ff0395b4000 0x00007ff03963b000 0x87000 0x11000 /usr/lib64/libm.so.6
0x00007ff03963b000 0x00007ff03969b000 0x60000 0x98000 /usr/lib64/libm.so.6
0x00007ff03969b000 0x00007ff03969c000 0x1000 0xf7000 /usr/lib64/libm.so.6
0x00007ff03969c000 0x00007ff03969e000 0x2000 0xf8000 /usr/lib64/libm.so.6
0x00007ff0396a8000 0x00007ff0396a9000 0x1000 0x0 /usr/lib64/ld-linux-x86-64.so.2
0x00007ff0396a9000 0x00007ff0396d4000 0x2b000 0x1000 /usr/lib64/ld-linux-x86-64.so.2
0x00007ff0396d4000 0x00007ff0396df000 0xb000 0x2c000 /usr/lib64/ld-linux-x86-64.so.2
0x00007ff0396df000 0x00007ff0396e1000 0x2000 0x37000 /usr/lib64/ld-linux-x86-64.so.2
0x00007ff0396e1000 0x00007ff0396e3000 0x2000 0x39000 /usr/lib64/ld-linux-x86-64.so.2
(gdb) PASS: gdb.python/py-corefile.exp: test mapped files data: capture Python based mappings data
shell diff -s /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile-out-1.txt /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile-out-2.txt
Files /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile-out-1.txt and /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile-out-2.txt are identical
(gdb) PASS: gdb.python/py-corefile.exp: test mapped files data: diff input and output one
show-build-ids
Objfile Build-Id Core File Build-Id File Name
91c6abb593a519ecd18e543eaac25f913999b230 91c6abb593a519ecd18e543eaac25f913999b230 /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile
Python Exception <class 'KeyError'>: 'corefile'
Error occurred in Python: 'corefile'
(gdb) PASS: gdb.python/py-corefile.exp: test mapped files data: show-build-ids
Mismatched build-ids Python vs Exception for <class 'KeyError'>: 'corefile'
Mismatched build-ids Error vs occurred for in Python: 'corefile'
FAIL: gdb.python/py-corefile.exp: test mapped files data: found expected build-ids in core file
check-main-executable
PASS
(gdb) PASS: gdb.python/py-corefile.exp: test mapped files data: check-main-executable
python core = gdb.selected_inferior().corefile
(gdb) PASS: gdb.python/py-corefile.exp: test mapped files data: python core = gdb.selected_inferior().corefile
python mapped_files = core.mapped_files()
(gdb) PASS: gdb.python/py-corefile.exp: test mapped files data: python mapped_files = core.mapped_files()
python print(type(mapped_files))
<class 'tuple'>
(gdb) PASS: gdb.python/py-corefile.exp: test mapped files data: python print(type(mapped_files))
python mapped_files[0] = None
Python Exception <class 'TypeError'>: 'tuple' object does not support item assignment
Error occurred in Python: 'tuple' object does not support item assignment
(gdb) PASS: gdb.python/py-corefile.exp: test mapped files data: python mapped_files[0] = None
python print(mapped_files[0] is None)
False
(gdb) PASS: gdb.python/py-corefile.exp: test mapped files data: python print(mapped_files[0] is None)
python regions = mapped_files[0].regions
(gdb) PASS: gdb.python/py-corefile.exp: test mapped files data: python regions = mapped_files[0].regions
python print(type(regions))
<class 'tuple'>
(gdb) PASS: gdb.python/py-corefile.exp: test mapped files data: python print(type(regions))
python regions[0] = None
Python Exception <class 'TypeError'>: 'tuple' object does not support item assignment
Error occurred in Python: 'tuple' object does not support item assignment
(gdb) PASS: gdb.python/py-corefile.exp: test mapped files data: python regions[0] = None
testcase /data/vries/gdb/src/gdb/testsuite/gdb.python/py-corefile.exp completed in 1 seconds
=== gdb Summary ===
# of expected passes 54
# of unexpected failures 1
Executing on host: /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/../../gdb/gdb -nw -nx -q -iex "set height 0" -iex "set width 0" -data-directory /data/vries/gdb/tumbleweed-20251005/build/gdb/data-directory -iex "set interactive-mode on" --version (timeout = 300)
builtin_spawn -ignore SIGHUP /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/../../gdb/gdb -nw -nx -q -iex set height 0 -iex set width 0 -data-directory /data/vries/gdb/tumbleweed-20251005/build/gdb/data-directory -iex set interactive-mode on --version
GNU gdb (GDB) 18.0.50.20251007-git
Copyright (C) 2025 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
/data/vries/gdb/tumbleweed-20251005/build/gdb/gdb version 18.0.50.20251007-git -nw -nx -q -iex "set height 0" -iex "set width 0" -data-directory /data/vries/gdb/tumbleweed-20251005/build/gdb/data-directory -iex "set interactive-mode on"
runtest completed at Tue Oct 7 15:34:59 2025
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCHv2 3/3] gdb/python: add Corefile.mapped_files method
2025-10-07 15:43 ` Tom de Vries
@ 2025-10-07 16:28 ` Andrew Burgess
2025-10-08 9:29 ` Andrew Burgess
0 siblings, 1 reply; 38+ messages in thread
From: Andrew Burgess @ 2025-10-07 16:28 UTC (permalink / raw)
To: Tom de Vries, Tom Tromey; +Cc: gdb-patches
Tom de Vries <tdevries@suse.de> writes:
> On 10/7/25 16:38, Andrew Burgess wrote:
>> Andrew Burgess <aburgess@redhat.com> writes:
>>
>>>
>>> Thanks for the diagnostic help. I'll work on improving the test to try
>>> and catch these cases. If that works, then we might be able to adopt
>>> this to help with other tests. Keeping this in mind, I'll add helper
>>> functions to lib/gdb.exp.
>>
>> Hi Tom,
>>
>> At your leisure, could you check that the patch below resolves the FAIL
>> you reported please.
>>
>
> Hi Andrew,
>
> on Leap 15.6, yes.
>
> On Tumbleweed, unfortunately no.
<snip>
> (gdb) PASS: gdb.python/py-corefile.exp: test mapped files data: capture Python based mappings data
> shell diff -s /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile-out-1.txt /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile-out-2.txt
> Files /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile-out-1.txt and /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile-out-2.txt are identical
> (gdb) PASS: gdb.python/py-corefile.exp: test mapped files data: diff input and output one
> show-build-ids
> Objfile Build-Id Core File Build-Id File Name
> 91c6abb593a519ecd18e543eaac25f913999b230 91c6abb593a519ecd18e543eaac25f913999b230 /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile
> Python Exception <class 'KeyError'>: 'corefile'
> Error occurred in Python: 'corefile'
It looks like there's an objfile discovered via
Inferior.progspace.objfiles(), which doesn't correspond to a file mapped
into the core file. FYI, I only consider objfiles that are _actual_
files, so ignore things like [vdso], etc, and I only consider objfiles
with a build-id. I figured that everything meeting this specification
had to be something that would appear in the core file...
The patch below will make the show-build-ids command a little more
resilient, when a file is missing it'll print "missing" in the column
now, rather than throwing the KeyError.
My guess is we'll find the same file under different names maybe?
Thanks,
Andrew
---
diff --git i/gdb/testsuite/gdb.python/py-corefile.py w/gdb/testsuite/gdb.python/py-corefile.py
index 5c8ee7b2f0c..01665ef0395 100644
--- i/gdb/testsuite/gdb.python/py-corefile.py
+++ w/gdb/testsuite/gdb.python/py-corefile.py
@@ -110,7 +110,7 @@ class ShowBuildIds(gdb.Command):
continue
p = pathlib.Path(o.filename).resolve()
b = o.build_id
- path_to_build_id[p] = {"objfile": b}
+ path_to_build_id[p] = {"objfile": b, "corefile": "missing" }
if len(b) > longest_build_id:
longest_build_id = len(b)
@@ -125,6 +125,8 @@ class ShowBuildIds(gdb.Command):
if p in path_to_build_id:
path_to_build_id[p]["corefile"] = b
+ else:
+ path_to_build_id[p] = {"objfile": "missing", "corefile": b }
format_str = (
"%-" + str(longest_build_id) + "s %-" + str(longest_build_id) + "s %s"
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCHv2 3/3] gdb/python: add Corefile.mapped_files method
2025-10-07 16:28 ` Andrew Burgess
@ 2025-10-08 9:29 ` Andrew Burgess
2025-10-08 10:36 ` Tom de Vries
0 siblings, 1 reply; 38+ messages in thread
From: Andrew Burgess @ 2025-10-08 9:29 UTC (permalink / raw)
To: Tom de Vries, Tom Tromey; +Cc: gdb-patches
Andrew Burgess <aburgess@redhat.com> writes:
> Tom de Vries <tdevries@suse.de> writes:
>
>> On 10/7/25 16:38, Andrew Burgess wrote:
>>> Andrew Burgess <aburgess@redhat.com> writes:
>>>
>>>>
>>>> Thanks for the diagnostic help. I'll work on improving the test to try
>>>> and catch these cases. If that works, then we might be able to adopt
>>>> this to help with other tests. Keeping this in mind, I'll add helper
>>>> functions to lib/gdb.exp.
>>>
>>> Hi Tom,
>>>
>>> At your leisure, could you check that the patch below resolves the FAIL
>>> you reported please.
>>>
>>
>> Hi Andrew,
>>
>> on Leap 15.6, yes.
>>
>> On Tumbleweed, unfortunately no.
>
> <snip>
>
>> (gdb) PASS: gdb.python/py-corefile.exp: test mapped files data: capture Python based mappings data
>> shell diff -s /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile-out-1.txt /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile-out-2.txt
>> Files /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile-out-1.txt and /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile-out-2.txt are identical
>> (gdb) PASS: gdb.python/py-corefile.exp: test mapped files data: diff input and output one
>> show-build-ids
>> Objfile Build-Id Core File Build-Id File Name
>> 91c6abb593a519ecd18e543eaac25f913999b230 91c6abb593a519ecd18e543eaac25f913999b230 /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile
>> Python Exception <class 'KeyError'>: 'corefile'
>> Error occurred in Python: 'corefile'
>
> It looks like there's an objfile discovered via
> Inferior.progspace.objfiles(), which doesn't correspond to a file mapped
> into the core file. FYI, I only consider objfiles that are _actual_
> files, so ignore things like [vdso], etc, and I only consider objfiles
> with a build-id. I figured that everything meeting this specification
> had to be something that would appear in the core file...
>
> The patch below will make the show-build-ids command a little more
> resilient, when a file is missing it'll print "missing" in the column
> now, rather than throwing the KeyError.
>
> My guess is we'll find the same file under different names maybe?
Just to follow up, I setup a Tumbleweed VM. I was able to reproduce the
original issue you reported, but for me, the patch I posted[1] resolves
the issue.
If you apply the snippet from [2] this should avoid the KeyError
exception, and should allow us to debug the problem.
Thanks,
Andrew
[1] https://inbox.sourceware.org/gdb-patches/87jz166al5.fsf@redhat.com
[1] https://inbox.sourceware.org/gdb-patches/87bjmi65h4.fsf@redhat.com
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCHv2 3/3] gdb/python: add Corefile.mapped_files method
2025-10-08 9:29 ` Andrew Burgess
@ 2025-10-08 10:36 ` Tom de Vries
2025-10-08 14:14 ` Andrew Burgess
0 siblings, 1 reply; 38+ messages in thread
From: Tom de Vries @ 2025-10-08 10:36 UTC (permalink / raw)
To: Andrew Burgess, Tom Tromey; +Cc: gdb-patches
On 10/8/25 11:29, Andrew Burgess wrote:
> Andrew Burgess <aburgess@redhat.com> writes:
>
>> Tom de Vries <tdevries@suse.de> writes:
>>
>>> On 10/7/25 16:38, Andrew Burgess wrote:
>>>> Andrew Burgess <aburgess@redhat.com> writes:
>>>>
>>>>>
>>>>> Thanks for the diagnostic help. I'll work on improving the test to try
>>>>> and catch these cases. If that works, then we might be able to adopt
>>>>> this to help with other tests. Keeping this in mind, I'll add helper
>>>>> functions to lib/gdb.exp.
>>>>
>>>> Hi Tom,
>>>>
>>>> At your leisure, could you check that the patch below resolves the FAIL
>>>> you reported please.
>>>>
>>>
>>> Hi Andrew,
>>>
>>> on Leap 15.6, yes.
>>>
>>> On Tumbleweed, unfortunately no.
>>
>> <snip>
>>
>>> (gdb) PASS: gdb.python/py-corefile.exp: test mapped files data: capture Python based mappings data
>>> shell diff -s /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile-out-1.txt /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile-out-2.txt
>>> Files /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile-out-1.txt and /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile-out-2.txt are identical
>>> (gdb) PASS: gdb.python/py-corefile.exp: test mapped files data: diff input and output one
>>> show-build-ids
>>> Objfile Build-Id Core File Build-Id File Name
>>> 91c6abb593a519ecd18e543eaac25f913999b230 91c6abb593a519ecd18e543eaac25f913999b230 /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile
>>> Python Exception <class 'KeyError'>: 'corefile'
>>> Error occurred in Python: 'corefile'
>>
>> It looks like there's an objfile discovered via
>> Inferior.progspace.objfiles(), which doesn't correspond to a file mapped
>> into the core file. FYI, I only consider objfiles that are _actual_
>> files, so ignore things like [vdso], etc, and I only consider objfiles
>> with a build-id. I figured that everything meeting this specification
>> had to be something that would appear in the core file...
>>
>> The patch below will make the show-build-ids command a little more
>> resilient, when a file is missing it'll print "missing" in the column
>> now, rather than throwing the KeyError.
>>
>> My guess is we'll find the same file under different names maybe?
>
> Just to follow up, I setup a Tumbleweed VM. I was able to reproduce the
> original issue you reported, but for me, the patch I posted[1] resolves
> the issue.
>
> If you apply the snippet from [2] this should avoid the KeyError
> exception, and should allow us to debug the problem.
>
Hi Andrew,
thanks for following up on this.
I put the gdb.log for Leap 15.6 and Tumbleweed side by side, and noticed
that the difference was presence of glibc debuginfo (in the form of
separate debug info objfiles).
After doing "zypper install glibc-debuginfo" I could reproduce the FAIL
on Leap 15.6. And after doing "dnf debuginfo-install glibc" in a fedora
rawhide container, it also reproduced there.
This fixes it for me in all those 3 cases:
...
diff --git a/gdb/testsuite/gdb.python/py-corefile.py b/gdb/testsuite/gdb.pyt
hon/py-corefile.py
index 01665ef0395..bc8082733bf 100644
--- a/gdb/testsuite/gdb.python/py-corefile.py
+++ b/gdb/testsuite/gdb.python/py-corefile.py
@@ -106,7 +106,7 @@ class ShowBuildIds(gdb.Command):
longest_build_id = 18
for o in objfiles:
- if not o.is_file or o.build_id is None:
+ if not o.is_file or o.build_id is None or o.owner is not
None:
continue
p = pathlib.Path(o.filename).resolve()
b = o.build_id
...
Thanks,
- Tom
> Thanks,
> Andrew
>
> [1] https://inbox.sourceware.org/gdb-patches/87jz166al5.fsf@redhat.com
> [1] https://inbox.sourceware.org/gdb-patches/87bjmi65h4.fsf@redhat.com
>
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCHv2 3/3] gdb/python: add Corefile.mapped_files method
2025-10-08 10:36 ` Tom de Vries
@ 2025-10-08 14:14 ` Andrew Burgess
2025-10-08 15:43 ` Tom de Vries
0 siblings, 1 reply; 38+ messages in thread
From: Andrew Burgess @ 2025-10-08 14:14 UTC (permalink / raw)
To: Tom de Vries, Tom Tromey; +Cc: gdb-patches
Tom de Vries <tdevries@suse.de> writes:
> On 10/8/25 11:29, Andrew Burgess wrote:
>> Andrew Burgess <aburgess@redhat.com> writes:
>>
>>> Tom de Vries <tdevries@suse.de> writes:
>>>
>>>> On 10/7/25 16:38, Andrew Burgess wrote:
>>>>> Andrew Burgess <aburgess@redhat.com> writes:
>>>>>
>>>>>>
>>>>>> Thanks for the diagnostic help. I'll work on improving the test to try
>>>>>> and catch these cases. If that works, then we might be able to adopt
>>>>>> this to help with other tests. Keeping this in mind, I'll add helper
>>>>>> functions to lib/gdb.exp.
>>>>>
>>>>> Hi Tom,
>>>>>
>>>>> At your leisure, could you check that the patch below resolves the FAIL
>>>>> you reported please.
>>>>>
>>>>
>>>> Hi Andrew,
>>>>
>>>> on Leap 15.6, yes.
>>>>
>>>> On Tumbleweed, unfortunately no.
>>>
>>> <snip>
>>>
>>>> (gdb) PASS: gdb.python/py-corefile.exp: test mapped files data: capture Python based mappings data
>>>> shell diff -s /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile-out-1.txt /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile-out-2.txt
>>>> Files /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile-out-1.txt and /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile-out-2.txt are identical
>>>> (gdb) PASS: gdb.python/py-corefile.exp: test mapped files data: diff input and output one
>>>> show-build-ids
>>>> Objfile Build-Id Core File Build-Id File Name
>>>> 91c6abb593a519ecd18e543eaac25f913999b230 91c6abb593a519ecd18e543eaac25f913999b230 /data/vries/gdb/tumbleweed-20251005/build/gdb/testsuite/outputs/gdb.python/py-corefile/py-corefile
>>>> Python Exception <class 'KeyError'>: 'corefile'
>>>> Error occurred in Python: 'corefile'
>>>
>>> It looks like there's an objfile discovered via
>>> Inferior.progspace.objfiles(), which doesn't correspond to a file mapped
>>> into the core file. FYI, I only consider objfiles that are _actual_
>>> files, so ignore things like [vdso], etc, and I only consider objfiles
>>> with a build-id. I figured that everything meeting this specification
>>> had to be something that would appear in the core file...
>>>
>>> The patch below will make the show-build-ids command a little more
>>> resilient, when a file is missing it'll print "missing" in the column
>>> now, rather than throwing the KeyError.
>>>
>>> My guess is we'll find the same file under different names maybe?
>>
>> Just to follow up, I setup a Tumbleweed VM. I was able to reproduce the
>> original issue you reported, but for me, the patch I posted[1] resolves
>> the issue.
>>
>> If you apply the snippet from [2] this should avoid the KeyError
>> exception, and should allow us to debug the problem.
>>
>
> Hi Andrew,
>
> thanks for following up on this.
>
> I put the gdb.log for Leap 15.6 and Tumbleweed side by side, and noticed
> that the difference was presence of glibc debuginfo (in the form of
> separate debug info objfiles).
>
> After doing "zypper install glibc-debuginfo" I could reproduce the FAIL
> on Leap 15.6. And after doing "dnf debuginfo-install glibc" in a fedora
> rawhide container, it also reproduced there.
>
> This fixes it for me in all those 3 cases:
> ...
> diff --git a/gdb/testsuite/gdb.python/py-corefile.py b/gdb/testsuite/gdb.pyt
> hon/py-corefile.py
> index 01665ef0395..bc8082733bf 100644
> --- a/gdb/testsuite/gdb.python/py-corefile.py
> +++ b/gdb/testsuite/gdb.python/py-corefile.py
> @@ -106,7 +106,7 @@ class ShowBuildIds(gdb.Command):
> longest_build_id = 18
>
> for o in objfiles:
> - if not o.is_file or o.build_id is None:
> + if not o.is_file or o.build_id is None or o.owner is not
> None:
> continue
> p = pathlib.Path(o.filename).resolve()
> b = o.build_id
Thanks for tracking that down. I figured out why, despite having the
debug info installed, I wasn't hitting this issue at my end (my mistake,
not really important), and could then reproduce this. I agree with your
fix.
Here's the patch I propose to push, this includes my original fix, plus
your Objfile.owner check.
Let me know what you think.
Thanks,
Andrew
---
commit 0f2a152fe641f82e074e8c995c456e27de4b9e5e
Author: Andrew Burgess <aburgess@redhat.com>
Date: Tue Oct 7 15:24:01 2025 +0100
gdb/testsuite: fix failure from gdb.python/py-corefile.exp
After commit:
commit f69c1d03c4d6c68ae3f90facd63245426c028047
Date: Mon Aug 25 16:48:22 2025 +0100
gdb/python: add Corefile.mapped_files method
Tom reported a failure:
(gdb) check-build-ids
Python Exception <class 'AssertionError'>: build-id mismatch for /lib64/libc.so.6
Error occurred in Python: build-id mismatch for /lib64/libc.so.6
(gdb) FAIL: gdb.python/py-corefile.exp: test mapped files data: check-build-ids
The discussion thread can be found here:
https://inbox.sourceware.org/gdb-patches/de21b43c-e3bd-4354-aace-bd3f50c1c64c@suse.de
There are a couple of problems.
First, there is an issue where some versions of the linker didn't
place the build-id within the first page of an ELF. As a result, the
Linux kernel would not include the build-id in the generated core
file, and so GDB cannot to find the build-id.
In this patch I've added mitigation for this problem.
I changed the 'check-build-ids' command (added via Python as part of
the test) to 'show-build-ids'. The updated command prints a table
containing the build-ids for each objfile as found via GDB's
Progspace.objfiles, and via the Corefile.mapped_files. This table is
then read by the TCL test script, and the build-ids are checked. If
there's a difference, then GDB can analyse the on disk ELF and work
out if the difference is due to the linker issue mentioned above. If
it is, then the difference is ignored.
In order to check for this linker issue I added a new helper proc to
lib/gdb.exp, expect_build_id_in_core_file.
The second problem with the original test is that it would consider
separate debug files as files that should appear in the core file.
There was Python code in the test that filtered the objfile list to
disregard entries that would not appear in the core file, but this
code needed extending to cover separate debug files.
The final issue is that I'm only aware of GNU/Linux forcing the first
page of every mapped ELF into the generated core files, so this test
would likely fail on non-Linux systems. I've made the part of the
test that relies on this behaviour Linux only.
This change should resolve the FAIL that Tom reported. Giving Tom a
Co-Author credit as he fixed the second issue, and helped a lot
debugging the first issue.
Co-Authored-By: Tom de Vries <tdevries@suse.de>
diff --git a/gdb/testsuite/gdb.python/py-corefile.exp b/gdb/testsuite/gdb.python/py-corefile.exp
index 3b57cc0e250..866b60a0dbe 100644
--- a/gdb/testsuite/gdb.python/py-corefile.exp
+++ b/gdb/testsuite/gdb.python/py-corefile.exp
@@ -211,8 +211,53 @@ with_test_prefix "test mapped files data" {
"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 build-ids within the core file mapping data. I'm only
+ # aware of GNU/Linux placing the first page of each mapped ELF
+ # into the generated core file so that the build-id can be found.
+ if {[istarget *-*-linux*]} {
+ set results [list]
+ gdb_test_multiple "show-build-ids" "" {
+ -re "^show-build-ids\r\n" {
+ exp_continue
+ }
+ -re "^Objfile Build-Id\\s+Core File Build-Id\\s+File Name\\s*\r\n" {
+ exp_continue
+ }
+ -re "^(\\S+)\\s+(\\S+)\\s+(\[^\r\n\]+)\r\n" {
+ set objfile_build_id $expect_out(1,string)
+ set core_file_build_id $expect_out(2,string)
+ set file_name $expect_out(3,string)
+ lappend results [list $objfile_build_id \
+ $core_file_build_id \
+ $file_name]
+ exp_continue
+ }
+ -re "^$gdb_prompt " {
+ pass $gdb_test_name
+ }
+ }
+
+ set bad_count 0
+ foreach entry $results {
+ set objfile_build_id [lindex $entry 0]
+ set core_file_build_id [lindex $entry 1]
+ set file_name [lindex $entry 2]
+ if { $objfile_build_id ne $core_file_build_id } {
+ if { $core_file_build_id ne "None" } {
+ verbose -log "Mismatched build-ids $objfile_build_id vs $core_file_build_id for $file_name"
+ incr bad_count
+ } elseif { [expect_build_id_in_core_file $file_name] } {
+ verbose -log "Failed to find build-id for $file_name"
+ incr bad_count
+ } else {
+ verbose -log "This build-id was likely not in the core file"
+ }
+ }
+ }
+
+ gdb_assert { $bad_count == 0 } \
+ "found expected build-ids in core file"
+ }
# 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
index cffd037a23b..bfb1c820d5d 100644
--- a/gdb/testsuite/gdb.python/py-corefile.py
+++ b/gdb/testsuite/gdb.python/py-corefile.py
@@ -78,9 +78,23 @@ class InfoProcPyMappings(gdb.Command):
InfoProcPyMappings()
-class CheckBuildIds(gdb.Command):
+# Assume that a core file is currently loaded.
+#
+# Look through all the objfiles for the current inferior, and record
+# any that have a build-id.
+#
+# Then look through the core file mapped files. Look for entries that
+# correspond with the loaded objfiles. For these matching entries,
+# capture the build-id extracted from the core file.
+#
+# Finally, print a table with the build-id from the objfile, the
+# build-id from the core file, and the file name.
+#
+# This is then processed from the test script to check the build-ids
+# match.
+class ShowBuildIds(gdb.Command):
def __init__(self):
- gdb.Command.__init__(self, "check-build-ids", gdb.COMMAND_DATA)
+ gdb.Command.__init__(self, "show-build-ids", gdb.COMMAND_DATA)
def invoke(self, args, from_tty):
inf = gdb.selected_inferior()
@@ -88,12 +102,17 @@ class CheckBuildIds(gdb.Command):
path_to_build_id = {}
+ # Initial length based on column headings.
+ longest_build_id = 18
+
for o in objfiles:
- if not o.is_file or o.build_id is None:
+ if not o.is_file or o.build_id is None or o.owner is not None:
continue
p = pathlib.Path(o.filename).resolve()
b = o.build_id
- path_to_build_id[p] = b
+ path_to_build_id[p] = {"objfile": b, "corefile": "missing"}
+ if len(b) > longest_build_id:
+ longest_build_id = len(b)
count = 0
core_mapped_files = inf.corefile.mapped_files()
@@ -101,16 +120,39 @@ class CheckBuildIds(gdb.Command):
p = pathlib.Path(m.filename).resolve()
b = m.build_id
+ if b is not None and len(b) > longest_build_id:
+ longest_build_id = len(b)
+
if p in path_to_build_id:
- count += 1
- assert path_to_build_id[p] == b, "build-id mismatch for %s" % p
+ path_to_build_id[p]["corefile"] = b
- assert count > 0, "no mapped files checked"
+ format_str = (
+ "%-" + str(longest_build_id) + "s %-" + str(longest_build_id) + "s %s"
+ )
- print("PASS")
+ def make_title(string, length=0):
+ if length > 0:
+ padding_len = length - len(string)
+ else:
+ padding_len = 0
+
+ padding = " " * padding_len
+ style = gdb.Style("title")
+ return style.apply(string) + padding
+
+ print(
+ "%s %s %s"
+ % (
+ make_title("Objfile Build-Id", longest_build_id),
+ make_title("Core File Build-Id", longest_build_id),
+ make_title("File Name"),
+ )
+ )
+ for p, b in path_to_build_id.items():
+ print(format_str % (b["objfile"], b["corefile"], p))
-CheckBuildIds()
+ShowBuildIds()
class CheckMainExec(gdb.Command):
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index e07aab4cdd8..5dfcfefd7c1 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -11596,6 +11596,63 @@ proc section_get {exec section} {
return $retval
}
+# Return true if we expect the build-id from FILENAME to be included
+# in a core file.
+#
+# On GNU/Linux, when creating a core file, the kernel places the first
+# page of an ELF into the core file. If the build-id is within that
+# page then GDB can find the build-id from the core file.
+#
+# This proc checks that the target is GNU/Linux, and then uses readelf
+# to find the offset of the build-id within the ELF. If there is a
+# build-id, and it is within the first page, then return true.
+# Otherwise, return false.
+
+proc expect_build_id_in_core_file { filename } {
+ # I'm not sure if other kernels take care to add the first page of
+ # each ELF into the core file. If they do then this test can be
+ # relaxed.
+ if {![istarget *-*-linux*]} {
+ return false
+ }
+
+ # Use readelf to find the build-id note in FILENAME.
+ set readelf_program [gdb_find_readelf]
+ set cmd [list $readelf_program -WS $filename | grep ".note.gnu.build-id"]
+ set res [catch {exec {*}$cmd} output]
+ verbose -log "running: $cmd"
+ verbose -log "result: $res"
+ verbose -log "output: $output"
+ if { $res != 0 } {
+ return false
+ }
+
+ # Extract the OFFSET from the readelf output.
+ set res [regexp {NOTE[ \t]+([0-9a-f]+)[ \t]+([0-9a-f]+)} \
+ $output dummy addr offset]
+ if { $res != 1 } {
+ return false
+ }
+
+ # Convert OFFSET to decimal.
+ set offset [expr 0x$offset + 0]
+
+ # Now figure out the page size. This should be fine for Linux
+ # hosts, see the istarget check above.
+ if {[catch {exec getconf PAGESIZE} page_size]} {
+ # Failed to fetch page size.
+ return false
+ }
+
+ # If the build-id is within the first page, then we expect the
+ # kernel to include it in the core file. There is actually a
+ # kernel setting (see coredump_filter) that could prevent this,
+ # but the default behaviour is to include the first page of the
+ # ELF, so for now, we just assume this is on.
+ verbose -log "Page size is $page_size, Offset is $offset"
+ return [expr $offset < $page_size]
+}
+
# Return 1 if the compiler supports __builtin_trap, else return 0.
gdb_caching_proc have_builtin_trap {} {
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCHv2 3/3] gdb/python: add Corefile.mapped_files method
2025-10-08 14:14 ` Andrew Burgess
@ 2025-10-08 15:43 ` Tom de Vries
2025-10-08 16:03 ` Andrew Burgess
0 siblings, 1 reply; 38+ messages in thread
From: Tom de Vries @ 2025-10-08 15:43 UTC (permalink / raw)
To: Andrew Burgess, Tom Tromey; +Cc: gdb-patches
On 10/8/25 16:14, Andrew Burgess wrote:
> Let me know what you think.
Hi Andrew,
thanks, this LGTM.
Please apply this as well:
...
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index 5dfcfefd7c1..ef6a8f859ef 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -11635,7 +11635,7 @@ proc expect_build_id_in_core_file { filename }
{
}
# Convert OFFSET to decimal.
- set offset [expr 0x$offset + 0]
+ set offset [expr {[subst 0x$offset]}]
# Now figure out the page size. This should be fine for Linux
# hosts, see the istarget check above.
@@ -11650,7 +11650,7 @@ proc expect_build_id_in_core_file { filename }
{
# but the default behaviour is to include the first page of the
# ELF, so for now, we just assume this is on.
verbose -log "Page size is $page_size, Offset is $offset"
- return [expr $offset < $page_size]
+ return [expr {$offset < $page_size}]
}
# Return 1 if the compiler supports __builtin_trap, else return 0.
...
to fix tclint warnings.
Thanks,
- Tom
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCHv2 3/3] gdb/python: add Corefile.mapped_files method
2025-10-08 15:43 ` Tom de Vries
@ 2025-10-08 16:03 ` Andrew Burgess
0 siblings, 0 replies; 38+ messages in thread
From: Andrew Burgess @ 2025-10-08 16:03 UTC (permalink / raw)
To: Tom de Vries, Tom Tromey; +Cc: gdb-patches
Tom de Vries <tdevries@suse.de> writes:
> On 10/8/25 16:14, Andrew Burgess wrote:
>> Let me know what you think.
>
> Hi Andrew,
>
> thanks, this LGTM.
>
> Please apply this as well:
> ...
> diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
> index 5dfcfefd7c1..ef6a8f859ef 100644
> --- a/gdb/testsuite/lib/gdb.exp
> +++ b/gdb/testsuite/lib/gdb.exp
> @@ -11635,7 +11635,7 @@ proc expect_build_id_in_core_file { filename }
> {
> }
>
> # Convert OFFSET to decimal.
> - set offset [expr 0x$offset + 0]
> + set offset [expr {[subst 0x$offset]}]
>
> # Now figure out the page size. This should be fine for Linux
> # hosts, see the istarget check above.
> @@ -11650,7 +11650,7 @@ proc expect_build_id_in_core_file { filename }
> {
> # but the default behaviour is to include the first page of the
> # ELF, so for now, we just assume this is on.
> verbose -log "Page size is $page_size, Offset is $offset"
> - return [expr $offset < $page_size]
> + return [expr {$offset < $page_size}]
> }
>
> # Return 1 if the compiler supports __builtin_trap, else return 0.
> ...
> to fix tclint warnings.
I applied these fixes and pushed the patch. Thank you for all your help
debugging this issue.
Thanks,
Andrew
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCHv2 2/3] gdb: make structured core file mappings processing global
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
0 siblings, 1 reply; 38+ messages in thread
From: Lancelot SIX @ 2025-10-13 13:57 UTC (permalink / raw)
To: Andrew Burgess; +Cc: gdb-patches, Tom Tromey
Hi Andrew,
I think there is an issue with this patch. When running with this and
address sanitizer, I end-up with use-after-free errors.
See below for what I think is the issue.
On Tue, Sep 23, 2025 at 02:44:07PM +0100, Andrew Burgess wrote:
> In corelow.c, within core_target::build_file_mappings, we have code
> that wraps around a call to gdbarch_read_core_file_mappings and
> provides more structure to the results.
>
> Specifically, gdbarch_read_core_file_mappings calls a callback once
> for every region of every mapped file. The wrapper code groups all of
> the mappings for one file into an instance of 'struct mapped_file',
> this allows all of the mapped regions to be associated with the
> build-id and filename of a file.
>
> In the next commit I plan to make this information available via the
> Python API, and so I need to allow access to this structured wrapping
> outside of corelow.c.
>
> This commit renames 'struct mapped_file' to 'struct core_mapped_file'
> and moves the struct into gdbcore.h. Then a new global function
> gdb_read_core_file_mappings is created into which I move the code to
> build the structured data.
>
> Then corelow.c is updated to call gdb_read_core_file_mappings.
>
> This commit does not extend the Python API, that is for the next
> commit.
>
> There should be no user visible changes after this commit.
>
> Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=32844
>
> Approved-By: Tom Tromey <tom@tromey.com>
> ---
> gdb/corelow.c | 218 +++++++++++++++++++++++++++-----------------------
> gdb/gdbcore.h | 43 ++++++++++
> 2 files changed, 160 insertions(+), 101 deletions(-)
>
> diff --git a/gdb/corelow.c b/gdb/corelow.c
> index 2f202dc1fbf..ee97e29556e 100644
> --- a/gdb/corelow.c
> +++ b/gdb/corelow.c
> @@ -366,108 +366,27 @@ core_target::core_target ()
> void
> core_target::build_file_mappings ()
> {
> - /* Type holding information about a single file mapped into the inferior
> - at the point when the core file was created. Associates a build-id
> - with the list of regions the file is mapped into. */
> - struct mapped_file
> - {
> - /* Type for a region of a file that was mapped into the inferior when
> - the core file was generated. */
> - struct region
> - {
> - /* Constructor. See member variables for argument descriptions. */
> - region (CORE_ADDR start_, CORE_ADDR end_, CORE_ADDR file_ofs_)
> - : start (start_),
> - end (end_),
> - file_ofs (file_ofs_)
> - { /* Nothing. */ }
> -
> - /* The inferior address for the start of the mapped region. */
> - CORE_ADDR start;
> -
> - /* The inferior address immediately after the mapped region. */
> - CORE_ADDR end;
> -
> - /* The offset within the mapped file for this content. */
> - CORE_ADDR file_ofs;
> - };
> -
> - /* If not nullptr, then this is the build-id associated with this
> - file. */
> - const bfd_build_id *build_id = nullptr;
> -
> - /* If true then we have seen multiple different build-ids associated
> - with the same filename. The build_id field will have been set back
> - to nullptr, and we should not set build_id in future. */
> - bool ignore_build_id_p = false;
> -
> - /* All the mapped regions of this file. */
> - std::vector<region> regions;
> - };
> -
> gdb::unordered_map<std::string, struct bfd *> bfd_map;
> gdb::unordered_set<std::string> unavailable_paths;
>
> /* All files mapped into the core file. The key is the filename. */
> - gdb::unordered_map<std::string, mapped_file> mapped_files;
> + std::vector<core_mapped_file> mapped_files
> + = gdb_read_core_file_mappings (m_core_gdbarch,
> + current_program_space->core_bfd ());
>
> - /* See linux_read_core_file_mappings() in linux-tdep.c for an example
> - read_core_file_mappings method. */
> - gdbarch_read_core_file_mappings (m_core_gdbarch,
> - current_program_space->core_bfd (),
> -
> - /* After determining the number of mappings, read_core_file_mappings
> - will invoke this lambda. */
> - [&] (ULONGEST)
> - {
> - },
> -
> - /* read_core_file_mappings will invoke this lambda for each mapping
> - that it finds. */
> - [&] (int num, ULONGEST start, ULONGEST end, ULONGEST file_ofs,
> - const char *filename, const bfd_build_id *build_id)
> - {
> - /* Architecture-specific read_core_mapping methods are expected to
> - weed out non-file-backed mappings. */
> - gdb_assert (filename != nullptr);
> -
> - /* Add this mapped region to the data for FILENAME. */
> - mapped_file &file_data = mapped_files[filename];
> - file_data.regions.emplace_back (start, end, file_ofs);
> - if (build_id != nullptr && !file_data.ignore_build_id_p)
> - {
> - if (file_data.build_id == nullptr)
> - file_data.build_id = build_id;
> - else if (!build_id_equal (build_id, file_data.build_id))
> - {
> - warning (_("Multiple build-ids found for %ps"),
> - styled_string (file_name_style.style (), filename));
> - file_data.build_id = nullptr;
> - file_data.ignore_build_id_p = true;
> - }
> - }
> - });
> -
> - /* Get the build-id of the core file. */
> - const bfd_build_id *core_build_id
> - = build_id_bfd_get (current_program_space->core_bfd ());
> -
> - for (const auto &[filename, file_data] : mapped_files)
> + for (const core_mapped_file &file_data : mapped_files)
> {
> - /* If this mapped file has the same build-id as was discovered for
> - the core-file itself, then we assume this is the main
> - executable. Record the filename as we can use this later. */
> - if (file_data.build_id != nullptr
> - && m_expected_exec_filename.empty ()
> - && build_id_equal (file_data.build_id, core_build_id))
> - m_expected_exec_filename = filename;
> + /* If this mapped file is marked as the main executable then record
> + the filename as we can use this later. */
> + if (file_data.is_main_exec && m_expected_exec_filename.empty ())
> + m_expected_exec_filename = file_data.filename;
>
> /* Use exec_file_find() to do sysroot expansion. It'll
> also strip the potential sysroot "target:" prefix. If
> there is no sysroot, an equivalent (possibly more
> canonical) pathname will be provided. */
> gdb::unique_xmalloc_ptr<char> expanded_fname
> - = exec_file_find (filename.c_str (), nullptr);
> + = exec_file_find (file_data.filename.c_str (), nullptr);
>
> bool build_id_mismatch = false;
> if (expanded_fname != nullptr && file_data.build_id != nullptr)
> @@ -509,7 +428,7 @@ core_target::build_file_mappings ()
> {
> abfd = find_objfile_by_build_id (current_program_space,
> file_data.build_id,
> - filename.c_str ());
> + file_data.filename.c_str ());
>
> if (abfd != nullptr)
> {
> @@ -527,7 +446,7 @@ core_target::build_file_mappings ()
> }
>
> std::vector<mem_range> ranges;
> - for (const mapped_file::region ®ion : file_data.regions)
> + for (const core_mapped_file::region ®ion : file_data.regions)
> ranges.emplace_back (region.start, region.end - region.start);
>
> if (expanded_fname == nullptr
> @@ -545,7 +464,7 @@ core_target::build_file_mappings ()
> bool content_is_in_core_file_p = true;
>
> /* Record all regions for this file as unavailable. */
> - for (const mapped_file::region ®ion : file_data.regions)
> + for (const core_mapped_file::region ®ion : file_data.regions)
> {
> /* Check to see if the region is available within the core
> file. */
> @@ -577,33 +496,33 @@ core_target::build_file_mappings ()
> if (build_id_mismatch)
> {
> if (expanded_fname == nullptr
> - || filename == expanded_fname.get ())
> + || file_data.filename == expanded_fname.get ())
> warning (_("File %ps doesn't match build-id from core-file "
> "during file-backed mapping processing"),
> styled_string (file_name_style.style (),
> - filename.c_str ()));
> + file_data.filename.c_str ()));
> else
> warning (_("File %ps which was expanded to %ps, doesn't match "
> "build-id from core-file during file-backed "
> "mapping processing"),
> styled_string (file_name_style.style (),
> - filename.c_str ()),
> + file_data.filename.c_str ()),
> styled_string (file_name_style.style (),
> expanded_fname.get ()));
> }
> else if (!content_is_in_core_file_p)
> {
> if (expanded_fname == nullptr
> - || filename == expanded_fname.get ())
> + || file_data.filename == expanded_fname.get ())
> warning (_("Can't open file %ps during file-backed mapping "
> "note processing"),
> styled_string (file_name_style.style (),
> - filename.c_str ()));
> + file_data.filename.c_str ()));
> else
> warning (_("Can't open file %ps which was expanded to %ps "
> "during file-backed mapping note processing"),
> styled_string (file_name_style.style (),
> - filename.c_str ()),
> + file_data.filename.c_str ()),
> styled_string (file_name_style.style (),
> expanded_fname.get ()));
> }
> @@ -617,7 +536,7 @@ core_target::build_file_mappings ()
> abfd.get ());
>
> /* Create sections for each mapped region. */
> - for (const mapped_file::region ®ion : file_data.regions)
> + for (const core_mapped_file::region ®ion : file_data.regions)
> {
> /* Make new BFD section. All sections have the same name,
> which is permitted by bfd_make_section_anyway(). */
> @@ -653,7 +572,7 @@ core_target::build_file_mappings ()
> soname = gdb_bfd_read_elf_soname (actual_filename);
> }
>
> - m_mapped_file_info.add (soname.get (), filename.c_str (),
> + m_mapped_file_info.add (soname.get (), file_data.filename.c_str (),
> actual_filename, std::move (ranges),
> file_data.build_id);
> }
> @@ -2163,6 +2082,103 @@ mapped_file_info::lookup (const char *filename,
>
> /* See gdbcore.h. */
>
> +std::vector<core_mapped_file>
> +gdb_read_core_file_mappings (struct gdbarch *gdbarch, struct bfd *cbfd)
> +{
> + std::vector<core_mapped_file> results;
> +
> + /* A map entry used while building RESULTS. */
> + struct map_entry
> + {
> + explicit map_entry (core_mapped_file *ptr)
> + : file_data (ptr)
> + { /* Nothing. */ }
> +
> + /* Points to an entry in RESULTS, this allows entries to be quickly
> + looked up and updated as new mappings are read. */
> + core_mapped_file *file_data = nullptr;
> +
> + /* If true then we have seen multiple different build-ids associated
> + with the filename of FILE_DATA. The FILE_DATA->build_id field will
> + have been set to nullptr, and we should not set FILE_DATA->build_id
> + in future. */
> + bool ignore_build_id_p = false;
> + };
> +
> + /* All files mapped into the core file. The key is the filename. */
> + gdb::unordered_map<std::string, map_entry> mapped_files;
> +
> + /* Get the build-id of the core file. At least on Linux, this will be
> + the build-id for the main executable. If other targets add the
> + gdbarch_read_core_file_mappings method, then it might turn out that
> + this logic is no longer true, in which case this might need to move
> + into the gdbarch_read_core_file_mappings method. */
> + const bfd_build_id *core_build_id = build_id_bfd_get (cbfd);
> +
> + /* See linux_read_core_file_mappings() in linux-tdep.c for an example
> + read_core_file_mappings method. */
> + gdbarch_read_core_file_mappings (gdbarch, cbfd,
> + /* After determining the number of mappings, read_core_file_mappings
> + will invoke this lambda. */
> + [&] (ULONGEST)
> + {
> + },
> +
> + /* read_core_file_mappings will invoke this lambda for each mapping
> + that it finds. */
> + [&] (int num, ULONGEST start, ULONGEST end, ULONGEST file_ofs,
> + const char *filename, const bfd_build_id *build_id)
> + {
> + /* Architecture-specific read_core_mapping methods are expected to
> + weed out non-file-backed mappings. */
> + gdb_assert (filename != nullptr);
> +
> + /* Add this mapped region to the data for FILENAME. */
> + auto iter = mapped_files.find (filename);
> + if (iter == mapped_files.end ())
> + {
> + /* Create entry in results list. */
> + results.emplace_back ();
> +
> + /* The entry to be added to the lookup map. */
> + map_entry entry (&results.back ());
This bit is (I think) invalid. You are taking the address of an element
in the vector to place it in the map_entry cache (mapped_files).
However, taking the address of the element of a vector in this case is
wrong. As you grow the vector (results.emplace_back), the backing
storage might be grown, and as a consequence values moved and existing
pointers/references/iterators are invalidated.
Using a list could be a drop-in replacement, or storing the index in the
vector rather than the element address would fix the issue.
I can submit a patch doing either of those, depending on what you
prefer.
Best,
Lancelot.
> + entry.file_data->filename = filename;
> +
> + /* Add entry to the quick lookup map and update ITER. */
> + auto inserted_result
> + = mapped_files.insert ({filename, std::move (entry)});
> + gdb_assert (inserted_result.second);
> + iter = inserted_result.first;
> + }
> +
> + core_mapped_file &file_data = *iter->second.file_data;
> + bool &ignore_build_id_p = iter->second.ignore_build_id_p;
> +
> + file_data.regions.emplace_back (start, end, file_ofs);
> + if (build_id != nullptr && !ignore_build_id_p)
> + {
> + if (file_data.build_id == nullptr)
> + file_data.build_id = build_id;
> + else if (!build_id_equal (build_id, file_data.build_id))
> + {
> + warning (_("Multiple build-ids found for %ps"),
> + styled_string (file_name_style.style (), filename));
> + file_data.build_id = nullptr;
> + ignore_build_id_p = true;
> + }
> + }
> +
> + if (build_id != nullptr
> + && core_build_id != nullptr
> + && build_id_equal (build_id, core_build_id))
> + file_data.is_main_exec = true;
> + });
> +
> + return results;
> +}
> +
> +/* See gdbcore.h. */
> +
> std::optional <core_target_mapped_file_info>
> core_target_find_mapped_file (const char *filename,
> std::optional<CORE_ADDR> addr)
> diff --git a/gdb/gdbcore.h b/gdb/gdbcore.h
> index e0e3843c97b..23432ecbd07 100644
> --- a/gdb/gdbcore.h
> +++ b/gdb/gdbcore.h
> @@ -258,4 +258,47 @@ std::optional<core_target_mapped_file_info>
> core_target_find_mapped_file (const char *filename,
> std::optional<CORE_ADDR> addr);
>
> +/* Type holding information about a single file mapped into the inferior
> + at the point when the core file was created. Associates a build-id
> + with the list of regions the file is mapped into. */
> +struct core_mapped_file
> +{
> + /* Type for a region of a file that was mapped into the inferior when
> + the core file was generated. */
> + struct region
> + {
> + /* Constructor. See member variables for argument descriptions. */
> + region (CORE_ADDR start_, CORE_ADDR end_, CORE_ADDR file_ofs_)
> + : start (start_),
> + end (end_),
> + file_ofs (file_ofs_)
> + { /* Nothing. */ }
> +
> + /* The inferior address for the start of the mapped region. */
> + CORE_ADDR start;
> +
> + /* The inferior address immediately after the mapped region. */
> + CORE_ADDR end;
> +
> + /* The offset within the mapped file for this content. */
> + CORE_ADDR file_ofs;
> + };
> +
> + /* The filename as recorded in the core file. */
> + std::string filename;
> +
> + /* If not nullptr, then this is the build-id associated with this
> + file. */
> + const bfd_build_id *build_id = nullptr;
> +
> + /* All the mapped regions of this file. */
> + std::vector<region> regions;
> +
> + /* True if this is the main executable. */
> + bool is_main_exec = false;
> +};
> +
> +extern std::vector<core_mapped_file> gdb_read_core_file_mappings
> + (struct gdbarch *gdbarch, struct bfd *cbfd);
> +
> #endif /* GDB_GDBCORE_H */
> --
> 2.47.1
>
>
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCHv2 2/3] gdb: make structured core file mappings processing global
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
0 siblings, 2 replies; 38+ messages in thread
From: Andrew Burgess @ 2025-10-13 14:37 UTC (permalink / raw)
To: Lancelot SIX; +Cc: gdb-patches, Tom Tromey
Lancelot SIX <lancelot.six@amd.com> writes:
> Hi Andrew,
>
> I think there is an issue with this patch. When running with this and
> address sanitizer, I end-up with use-after-free errors.
>
> See below for what I think is the issue.
>
> On Tue, Sep 23, 2025 at 02:44:07PM +0100, Andrew Burgess wrote:
>> In corelow.c, within core_target::build_file_mappings, we have code
>> that wraps around a call to gdbarch_read_core_file_mappings and
>> provides more structure to the results.
>>
>> Specifically, gdbarch_read_core_file_mappings calls a callback once
>> for every region of every mapped file. The wrapper code groups all of
>> the mappings for one file into an instance of 'struct mapped_file',
>> this allows all of the mapped regions to be associated with the
>> build-id and filename of a file.
>>
>> In the next commit I plan to make this information available via the
>> Python API, and so I need to allow access to this structured wrapping
>> outside of corelow.c.
>>
>> This commit renames 'struct mapped_file' to 'struct core_mapped_file'
>> and moves the struct into gdbcore.h. Then a new global function
>> gdb_read_core_file_mappings is created into which I move the code to
>> build the structured data.
>>
>> Then corelow.c is updated to call gdb_read_core_file_mappings.
>>
>> This commit does not extend the Python API, that is for the next
>> commit.
>>
>> There should be no user visible changes after this commit.
>>
>> Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=32844
>>
>> Approved-By: Tom Tromey <tom@tromey.com>
>> ---
>> gdb/corelow.c | 218 +++++++++++++++++++++++++++-----------------------
>> gdb/gdbcore.h | 43 ++++++++++
>> 2 files changed, 160 insertions(+), 101 deletions(-)
>>
>> diff --git a/gdb/corelow.c b/gdb/corelow.c
>> index 2f202dc1fbf..ee97e29556e 100644
>> --- a/gdb/corelow.c
>> +++ b/gdb/corelow.c
>> @@ -366,108 +366,27 @@ core_target::core_target ()
>> void
>> core_target::build_file_mappings ()
>> {
>> - /* Type holding information about a single file mapped into the inferior
>> - at the point when the core file was created. Associates a build-id
>> - with the list of regions the file is mapped into. */
>> - struct mapped_file
>> - {
>> - /* Type for a region of a file that was mapped into the inferior when
>> - the core file was generated. */
>> - struct region
>> - {
>> - /* Constructor. See member variables for argument descriptions. */
>> - region (CORE_ADDR start_, CORE_ADDR end_, CORE_ADDR file_ofs_)
>> - : start (start_),
>> - end (end_),
>> - file_ofs (file_ofs_)
>> - { /* Nothing. */ }
>> -
>> - /* The inferior address for the start of the mapped region. */
>> - CORE_ADDR start;
>> -
>> - /* The inferior address immediately after the mapped region. */
>> - CORE_ADDR end;
>> -
>> - /* The offset within the mapped file for this content. */
>> - CORE_ADDR file_ofs;
>> - };
>> -
>> - /* If not nullptr, then this is the build-id associated with this
>> - file. */
>> - const bfd_build_id *build_id = nullptr;
>> -
>> - /* If true then we have seen multiple different build-ids associated
>> - with the same filename. The build_id field will have been set back
>> - to nullptr, and we should not set build_id in future. */
>> - bool ignore_build_id_p = false;
>> -
>> - /* All the mapped regions of this file. */
>> - std::vector<region> regions;
>> - };
>> -
>> gdb::unordered_map<std::string, struct bfd *> bfd_map;
>> gdb::unordered_set<std::string> unavailable_paths;
>>
>> /* All files mapped into the core file. The key is the filename. */
>> - gdb::unordered_map<std::string, mapped_file> mapped_files;
>> + std::vector<core_mapped_file> mapped_files
>> + = gdb_read_core_file_mappings (m_core_gdbarch,
>> + current_program_space->core_bfd ());
>>
>> - /* See linux_read_core_file_mappings() in linux-tdep.c for an example
>> - read_core_file_mappings method. */
>> - gdbarch_read_core_file_mappings (m_core_gdbarch,
>> - current_program_space->core_bfd (),
>> -
>> - /* After determining the number of mappings, read_core_file_mappings
>> - will invoke this lambda. */
>> - [&] (ULONGEST)
>> - {
>> - },
>> -
>> - /* read_core_file_mappings will invoke this lambda for each mapping
>> - that it finds. */
>> - [&] (int num, ULONGEST start, ULONGEST end, ULONGEST file_ofs,
>> - const char *filename, const bfd_build_id *build_id)
>> - {
>> - /* Architecture-specific read_core_mapping methods are expected to
>> - weed out non-file-backed mappings. */
>> - gdb_assert (filename != nullptr);
>> -
>> - /* Add this mapped region to the data for FILENAME. */
>> - mapped_file &file_data = mapped_files[filename];
>> - file_data.regions.emplace_back (start, end, file_ofs);
>> - if (build_id != nullptr && !file_data.ignore_build_id_p)
>> - {
>> - if (file_data.build_id == nullptr)
>> - file_data.build_id = build_id;
>> - else if (!build_id_equal (build_id, file_data.build_id))
>> - {
>> - warning (_("Multiple build-ids found for %ps"),
>> - styled_string (file_name_style.style (), filename));
>> - file_data.build_id = nullptr;
>> - file_data.ignore_build_id_p = true;
>> - }
>> - }
>> - });
>> -
>> - /* Get the build-id of the core file. */
>> - const bfd_build_id *core_build_id
>> - = build_id_bfd_get (current_program_space->core_bfd ());
>> -
>> - for (const auto &[filename, file_data] : mapped_files)
>> + for (const core_mapped_file &file_data : mapped_files)
>> {
>> - /* If this mapped file has the same build-id as was discovered for
>> - the core-file itself, then we assume this is the main
>> - executable. Record the filename as we can use this later. */
>> - if (file_data.build_id != nullptr
>> - && m_expected_exec_filename.empty ()
>> - && build_id_equal (file_data.build_id, core_build_id))
>> - m_expected_exec_filename = filename;
>> + /* If this mapped file is marked as the main executable then record
>> + the filename as we can use this later. */
>> + if (file_data.is_main_exec && m_expected_exec_filename.empty ())
>> + m_expected_exec_filename = file_data.filename;
>>
>> /* Use exec_file_find() to do sysroot expansion. It'll
>> also strip the potential sysroot "target:" prefix. If
>> there is no sysroot, an equivalent (possibly more
>> canonical) pathname will be provided. */
>> gdb::unique_xmalloc_ptr<char> expanded_fname
>> - = exec_file_find (filename.c_str (), nullptr);
>> + = exec_file_find (file_data.filename.c_str (), nullptr);
>>
>> bool build_id_mismatch = false;
>> if (expanded_fname != nullptr && file_data.build_id != nullptr)
>> @@ -509,7 +428,7 @@ core_target::build_file_mappings ()
>> {
>> abfd = find_objfile_by_build_id (current_program_space,
>> file_data.build_id,
>> - filename.c_str ());
>> + file_data.filename.c_str ());
>>
>> if (abfd != nullptr)
>> {
>> @@ -527,7 +446,7 @@ core_target::build_file_mappings ()
>> }
>>
>> std::vector<mem_range> ranges;
>> - for (const mapped_file::region ®ion : file_data.regions)
>> + for (const core_mapped_file::region ®ion : file_data.regions)
>> ranges.emplace_back (region.start, region.end - region.start);
>>
>> if (expanded_fname == nullptr
>> @@ -545,7 +464,7 @@ core_target::build_file_mappings ()
>> bool content_is_in_core_file_p = true;
>>
>> /* Record all regions for this file as unavailable. */
>> - for (const mapped_file::region ®ion : file_data.regions)
>> + for (const core_mapped_file::region ®ion : file_data.regions)
>> {
>> /* Check to see if the region is available within the core
>> file. */
>> @@ -577,33 +496,33 @@ core_target::build_file_mappings ()
>> if (build_id_mismatch)
>> {
>> if (expanded_fname == nullptr
>> - || filename == expanded_fname.get ())
>> + || file_data.filename == expanded_fname.get ())
>> warning (_("File %ps doesn't match build-id from core-file "
>> "during file-backed mapping processing"),
>> styled_string (file_name_style.style (),
>> - filename.c_str ()));
>> + file_data.filename.c_str ()));
>> else
>> warning (_("File %ps which was expanded to %ps, doesn't match "
>> "build-id from core-file during file-backed "
>> "mapping processing"),
>> styled_string (file_name_style.style (),
>> - filename.c_str ()),
>> + file_data.filename.c_str ()),
>> styled_string (file_name_style.style (),
>> expanded_fname.get ()));
>> }
>> else if (!content_is_in_core_file_p)
>> {
>> if (expanded_fname == nullptr
>> - || filename == expanded_fname.get ())
>> + || file_data.filename == expanded_fname.get ())
>> warning (_("Can't open file %ps during file-backed mapping "
>> "note processing"),
>> styled_string (file_name_style.style (),
>> - filename.c_str ()));
>> + file_data.filename.c_str ()));
>> else
>> warning (_("Can't open file %ps which was expanded to %ps "
>> "during file-backed mapping note processing"),
>> styled_string (file_name_style.style (),
>> - filename.c_str ()),
>> + file_data.filename.c_str ()),
>> styled_string (file_name_style.style (),
>> expanded_fname.get ()));
>> }
>> @@ -617,7 +536,7 @@ core_target::build_file_mappings ()
>> abfd.get ());
>>
>> /* Create sections for each mapped region. */
>> - for (const mapped_file::region ®ion : file_data.regions)
>> + for (const core_mapped_file::region ®ion : file_data.regions)
>> {
>> /* Make new BFD section. All sections have the same name,
>> which is permitted by bfd_make_section_anyway(). */
>> @@ -653,7 +572,7 @@ core_target::build_file_mappings ()
>> soname = gdb_bfd_read_elf_soname (actual_filename);
>> }
>>
>> - m_mapped_file_info.add (soname.get (), filename.c_str (),
>> + m_mapped_file_info.add (soname.get (), file_data.filename.c_str (),
>> actual_filename, std::move (ranges),
>> file_data.build_id);
>> }
>> @@ -2163,6 +2082,103 @@ mapped_file_info::lookup (const char *filename,
>>
>> /* See gdbcore.h. */
>>
>> +std::vector<core_mapped_file>
>> +gdb_read_core_file_mappings (struct gdbarch *gdbarch, struct bfd *cbfd)
>> +{
>> + std::vector<core_mapped_file> results;
>> +
>> + /* A map entry used while building RESULTS. */
>> + struct map_entry
>> + {
>> + explicit map_entry (core_mapped_file *ptr)
>> + : file_data (ptr)
>> + { /* Nothing. */ }
>> +
>> + /* Points to an entry in RESULTS, this allows entries to be quickly
>> + looked up and updated as new mappings are read. */
>> + core_mapped_file *file_data = nullptr;
>> +
>> + /* If true then we have seen multiple different build-ids associated
>> + with the filename of FILE_DATA. The FILE_DATA->build_id field will
>> + have been set to nullptr, and we should not set FILE_DATA->build_id
>> + in future. */
>> + bool ignore_build_id_p = false;
>> + };
>> +
>> + /* All files mapped into the core file. The key is the filename. */
>> + gdb::unordered_map<std::string, map_entry> mapped_files;
>> +
>> + /* Get the build-id of the core file. At least on Linux, this will be
>> + the build-id for the main executable. If other targets add the
>> + gdbarch_read_core_file_mappings method, then it might turn out that
>> + this logic is no longer true, in which case this might need to move
>> + into the gdbarch_read_core_file_mappings method. */
>> + const bfd_build_id *core_build_id = build_id_bfd_get (cbfd);
>> +
>> + /* See linux_read_core_file_mappings() in linux-tdep.c for an example
>> + read_core_file_mappings method. */
>> + gdbarch_read_core_file_mappings (gdbarch, cbfd,
>> + /* After determining the number of mappings, read_core_file_mappings
>> + will invoke this lambda. */
>> + [&] (ULONGEST)
>> + {
>> + },
>> +
>> + /* read_core_file_mappings will invoke this lambda for each mapping
>> + that it finds. */
>> + [&] (int num, ULONGEST start, ULONGEST end, ULONGEST file_ofs,
>> + const char *filename, const bfd_build_id *build_id)
>> + {
>> + /* Architecture-specific read_core_mapping methods are expected to
>> + weed out non-file-backed mappings. */
>> + gdb_assert (filename != nullptr);
>> +
>> + /* Add this mapped region to the data for FILENAME. */
>> + auto iter = mapped_files.find (filename);
>> + if (iter == mapped_files.end ())
>> + {
>> + /* Create entry in results list. */
>> + results.emplace_back ();
>> +
>> + /* The entry to be added to the lookup map. */
>> + map_entry entry (&results.back ());
>
> This bit is (I think) invalid. You are taking the address of an element
> in the vector to place it in the map_entry cache (mapped_files).
> However, taking the address of the element of a vector in this case is
> wrong. As you grow the vector (results.emplace_back), the backing
> storage might be grown, and as a consequence values moved and existing
> pointers/references/iterators are invalidated.
>
> Using a list could be a drop-in replacement, or storing the index in the
> vector rather than the element address would fix the issue.
>
> I can submit a patch doing either of those, depending on what you
> prefer.
>
Thanks for tracking this down.
If you have a patch or want to create the patch, then feel free to go
with whatever approach you feel is best.
But I don't want to dump my mess on you. I'm happy to fix this up.
Just let me know.
Thanks,
Andrew
^ permalink raw reply [flat|nested] 38+ messages in thread
* RE: [PATCHv2 2/3] gdb: make structured core file mappings processing global
2025-10-13 14:37 ` Andrew Burgess
@ 2025-10-13 15:16 ` Six, Lancelot
2025-10-14 9:12 ` Lancelot SIX
1 sibling, 0 replies; 38+ messages in thread
From: Six, Lancelot @ 2025-10-13 15:16 UTC (permalink / raw)
To: Andrew Burgess; +Cc: gdb-patches, Tom Tromey
[AMD Official Use Only - AMD Internal Distribution Only]
>
> Thanks for tracking this down.
>
> If you have a patch or want to create the patch, then feel free to go
> with whatever approach you feel is best.
>
> But I don't want to dump my mess on you. I'm happy to fix this up.
>
> Just let me know.
>
> Thanks,
> Andrew
>
I already have a patch for that, but while testing it, ASAN finds other use-after-free errors. I'll try to get a series fixing them all at once. If fixing the rest takes too long, I'll send that one patch alone.
Best,
Lancelot.
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCHv2 2/3] gdb: make structured core file mappings processing global
2025-10-13 14:37 ` Andrew Burgess
2025-10-13 15:16 ` Six, Lancelot
@ 2025-10-14 9:12 ` Lancelot SIX
1 sibling, 0 replies; 38+ messages in thread
From: Lancelot SIX @ 2025-10-14 9:12 UTC (permalink / raw)
To: Andrew Burgess; +Cc: gdb-patches, Tom Tromey
>>
>
> Thanks for tracking this down.
>
> If you have a patch or want to create the patch, then feel free to go
> with whatever approach you feel is best.
>
> But I don't want to dump my mess on you. I'm happy to fix this up.
>
> Just let me know.
>
> Thanks,
> Andrew
>
>
Hi,
FYI, I have submitted a fix for this here [1].
This small series fixes 2 use-after-free I currently see on master.
Best,
Lancelot.
[1]
https://inbox.sourceware.org/gdb-patches/20251013172524.1502740-1-lancelot.six@amd.com/T/#m001a75b5514956d936748dcda9e4cfb6b1c95b86
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCHv2 3/3] gdb/python: add Corefile.mapped_files method
2025-10-07 12:21 ` Andrew Burgess
2025-10-07 13:08 ` Tom de Vries
@ 2025-10-16 20:00 ` Tom Tromey
2025-10-17 10:02 ` Andrew Burgess
1 sibling, 1 reply; 38+ messages in thread
From: Tom Tromey @ 2025-10-16 20:00 UTC (permalink / raw)
To: Andrew Burgess; +Cc: Tom de Vries, Tom Tromey, gdb-patches
>>>>> "Andrew" == Andrew Burgess <aburgess@redhat.com> writes:
Andrew> + Build-id is not present in the core file, GDB will return None for
Andrew> the build-id.
Andrew> Of these two I suspect the second; for a period of time the GNU linker
Andrew> was ... changed ... such that it no longer placed the build-id within
Andrew> the first page of a generated ELF, as a result, the Linux kernel would
Andrew> not include the build-id in core dumps.
Andrew> We can check for the second case using:
Andrew> readelf -WS /lib64/libc.so.6 | grep build-id
Andrew> The output will be something like:
Andrew> [ 2] .note.gnu.build-id NOTE 0000000000000370 000370 000024 00 A 0 0 4
Andrew> It's the '000370' column we're interested in. If this value is greater
Andrew> than a page size, then GDB isn't going to be able to find the build-id.
I have a question about this work.
There's an internal AdaCore test that creates a core file, then uses
'file' on a different executable and 'core' to check if a warning is
issued.
gdb 16 on this test does:
(gdb) core core
warning: core file may not match specified executable file.
[...]
However with git master we don't get that.
Now, the executables in question do have build ids:
bapiya. readelf -WS call_crash|grep build-id
[ 2] .note.gnu.build-id NOTE 0000000000000390 000390 000024 00 A 0 0 4
bapiya. readelf -WS crash|grep build-id
[ 2] .note.gnu.build-id NOTE 0000000000000390 000390 000024 00 A 0 0 4
... and I verified the the IDs are actually different.
I'm not really sure how to find the relevant page size, per your comment
above.
I can't readily tell if the core file has an ID. gdb thinks there
isn't, using the command from py-corefile.py.
That by itself seems a little weird but I am wondering if the warning
should appear anyway, at least in this case, because the core file
records it was created by 'crash' but the current file has a different
base name ("call_crash").
IOW, is the lack of a warning in this case now intentional?
And, how can I understand why the ID isn't in the core?
I'm having some trouble figuring out which 'ld' the compiler is invoking.
Tom
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCHv2 3/3] gdb/python: add Corefile.mapped_files method
2025-10-16 20:00 ` Tom Tromey
@ 2025-10-17 10:02 ` Andrew Burgess
2025-10-17 13:32 ` Andrew Burgess
0 siblings, 1 reply; 38+ messages in thread
From: Andrew Burgess @ 2025-10-17 10:02 UTC (permalink / raw)
To: Tom Tromey; +Cc: Tom de Vries, Tom Tromey, gdb-patches
Tom Tromey <tromey@adacore.com> writes:
>>>>>> "Andrew" == Andrew Burgess <aburgess@redhat.com> writes:
>
> Andrew> + Build-id is not present in the core file, GDB will return None for
> Andrew> the build-id.
>
> Andrew> Of these two I suspect the second; for a period of time the GNU linker
> Andrew> was ... changed ... such that it no longer placed the build-id within
> Andrew> the first page of a generated ELF, as a result, the Linux kernel would
> Andrew> not include the build-id in core dumps.
>
> Andrew> We can check for the second case using:
>
> Andrew> readelf -WS /lib64/libc.so.6 | grep build-id
>
> Andrew> The output will be something like:
>
> Andrew> [ 2] .note.gnu.build-id NOTE 0000000000000370 000370 000024 00 A 0 0 4
>
> Andrew> It's the '000370' column we're interested in. If this value is greater
> Andrew> than a page size, then GDB isn't going to be able to find the build-id.
>
> I have a question about this work.
>
> There's an internal AdaCore test that creates a core file, then uses
> 'file' on a different executable and 'core' to check if a warning is
> issued.
>
> gdb 16 on this test does:
>
> (gdb) core core
> warning: core file may not match specified executable file.
> [...]
>
> However with git master we don't get that.
>
> Now, the executables in question do have build ids:
>
> bapiya. readelf -WS call_crash|grep build-id
> [ 2] .note.gnu.build-id NOTE 0000000000000390 000390 000024 00 A 0 0 4
> bapiya. readelf -WS crash|grep build-id
> [ 2] .note.gnu.build-id NOTE 0000000000000390 000390 000024 00 A 0 0 4
>
> ... and I verified the the IDs are actually different.
>
> I'm not really sure how to find the relevant page size, per your comment
> above.
>
> I can't readily tell if the core file has an ID. gdb thinks there
> isn't, using the command from py-corefile.py.
>
> That by itself seems a little weird but I am wondering if the warning
> should appear anyway, at least in this case, because the core file
> records it was created by 'crash' but the current file has a different
> base name ("call_crash").
>
> IOW, is the lack of a warning in this case now intentional?
No this is not intentional, and I can reproduce this.
But I thought we had a test that covered this specific case already, so
I'm really surprised this wasn't flagged during testing.
I'll investigate what's going on here, I guess I've managed to break
something....
>
> And, how can I understand why the ID isn't in the core?
> I'm having some trouble figuring out which 'ld' the compiler is invoking.
The offsets of those build-ids looks fine (less than 0x1000) so should
be within the first page of the ELF. This will be something I've
managed to break somehow :(
Sorry about that.
Thanks,
Andrew
^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: [PATCHv2 3/3] gdb/python: add Corefile.mapped_files method
2025-10-17 10:02 ` Andrew Burgess
@ 2025-10-17 13:32 ` Andrew Burgess
0 siblings, 0 replies; 38+ messages in thread
From: Andrew Burgess @ 2025-10-17 13:32 UTC (permalink / raw)
To: Tom Tromey; +Cc: Tom de Vries, Tom Tromey, gdb-patches
Andrew Burgess <aburgess@redhat.com> writes:
> Tom Tromey <tromey@adacore.com> writes:
>
>>
>> IOW, is the lack of a warning in this case now intentional?
>
> No this is not intentional, and I can reproduce this.
>
> But I thought we had a test that covered this specific case already, so
> I'm really surprised this wasn't flagged during testing.
>
> I'll investigate what's going on here, I guess I've managed to break
> something....
>
It turns out that it wasn't the Python core file API series that broke
this, it was moving the core file BFD from program_space to core_target
that was at fault, which was also me.
Anyway, I posted a fix here:
https://inbox.sourceware.org/gdb-patches/1272a6df222d57b169b97a14efd081a0c73d405d.1760707749.git.aburgess@redhat.com
Thanks,
Andrew
^ permalink raw reply [flat|nested] 38+ messages in thread
end of thread, other threads:[~2025-10-17 13:33 UTC | newest]
Thread overview: 38+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
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 ` [PATCH 3/3] gdb/python: add Corefile.mapped_files method Andrew Burgess
2025-09-16 17:54 ` 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
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox