From f69c1d03c4d6c68ae3f90facd63245426c028047 Mon Sep 17 00:00:00 2001 From: Andrew Burgess Date: Mon, 25 Aug 2025 16:48:22 +0100 Subject: [PATCH] gdb/python: add Corefile.mapped_files method 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 Approved-By: Tom Tromey --- gdb/NEWS | 13 +- 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, 672 insertions(+), 1 deletion(-) create mode 100644 gdb/testsuite/gdb.python/py-corefile.py diff --git a/gdb/NEWS b/gdb/NEWS index ebd0e063227..b0146852c5c 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -87,7 +87,18 @@ qExecAndArgs 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 c671d231345..ed113173667 100644 --- a/gdb/doc/python.texi +++ b/gdb/doc/python.texi @@ -8927,12 +8927,73 @@ it is invalid at the time the method is called, or the attribute 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 bda93fa1325..7c9a4478f24 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 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 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 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) +/* 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; +} + + + +/* 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 (); +} + + + 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))" \ + "^" + 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))" \ + "^" + 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 . + +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.3