mirror of
https://github.com/bminor/binutils-gdb.git
synced 2025-11-16 12:34:43 +00:00
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 <tom@tromey.com>
This commit is contained in:
13
gdb/NEWS
13
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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
/* 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 */
|
||||
};
|
||||
|
||||
@@ -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"
|
||||
}
|
||||
|
||||
144
gdb/testsuite/gdb.python/py-corefile.py
Normal file
144
gdb/testsuite/gdb.python/py-corefile.py
Normal file
@@ -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")
|
||||
Reference in New Issue
Block a user