mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2026-08-27 00:26:02 -04:00
Back-port the following upstream commits to enable core file support within GDB's DAP protocol implementation: *0a481bb9a6gdb/dap: add support for opening core files *4ecaac39c2gdb/python: new events.corefile_changed event *ecac42af73Always propagate exceptions in DAP *fc8e5a565bgdb: make structured core file mappings processing global *f69c1d03c4gdb/python: add Corefile.mapped_files method *7862554bcfgdb/python: introduce gdb.Corefile API The commit messages for each individual commit follow: ---0a481bb9a6: gdb/dap: add support for opening core files This patch adds core file support to GDB's DAP interface. Core files are supported as a GDB specific argument to 'attach', the new argument is 'coreFile', the name of the core file to debug. I think handling core files via attach makes the most sense; attach is for connecting to existing processes, but these targets are (usually) stopped as soon as GDB attaches, and that's what a core file looks like, a target that was running, but is now stopped. It just happens that core file targets are special in that the target cannot be resumed again, nor can the user modify the program state (e.g. write to memory or registers). Prior to starting this work I took a look at what lldb does. The documentation is not super clear, but this page seems to indicate that lldb might also use the 'coreFile' argument to 'attach': https://lldb.llvm.org/use/lldbdap.html#configuration-settings-reference Like I said, it's not very clear, but search for "coreFile" and you'll see it mentioned, just once, under the "attach" header. In order to be compatible with lldb I used the same argument name with the same capitalisation. The new argument is added to the documentation and mentioned in NEWS. I had to make some changes to testsuite/lib/dap-support.exp to support this new feature. There's a new dap_corefile proc to handle setting up the initial connection. This seemed cleaner that overloading dap_attach, even though under the hood it is still an 'attach' request that gets sent. The new test tries to write to memory and registers with the core file target in place, neither of these requests succeed, which is what we want, but the exceptions are logged into the dap log file. The dap_shutdown proc calls dap_check_log_file to check the log for exceptions, and these two exceptions are spotted and trigger a FAIL. To avoid this I've added a new "expected_exception_count" argument for dap_shutdown. Now we check that we see the expected number of exceptions. We don't check for the specific exception types right now, but as the test is already checking that the expected requests fail, I think we're OK. Approved-By: Tom Tromey <tom@tromey.com> ---4ecaac39c2: gdb/python: new events.corefile_changed event Add a new Python event registry, events.corefile_changed. This event is emitted each time the corefile within an inferior changes. The event object has a single 'inferior' attribute which is the gdb.Inferior object for which the core file changed. The user can then inspect Inferior.corefile to see details about the new core file, or this will be None if the core file was removed from the inferior. I've updated the existing test to cover this new event. The new test covers both the corefile_changed event, but also monitors the exited event. This ties into the work done in the previous commit where we use whether the inferior has exited or not as a guard for whether core_target::exit_core_file_inferior should be called. Unloading a core file should result in a single corefile_changed event and a single exited event. Reviewed-By: Eli Zaretskii <eliz@gnu.org> ---ecac42af73: Always propagate exceptions in DAP This changes the DAP exec_and_log function to always transform an exception into a DAPException and propagate it. As the bug points out, we haven't always wrapped calls when appropriate. I think it's better to cause the request to fail by default; if any spot truly needs to ignore errors, that is readily done at the point of call. Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=33346 ---fc8e5a565b: gdb: make structured core file mappings processing global 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> ---f69c1d03c4: 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> ---7862554bcf: gdb/python: introduce gdb.Corefile API 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> Approved-By: Tom Tromey <tom@tromey.com>
723 lines
20 KiB
C
723 lines
20 KiB
C
/* 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"
|
||
#include "gdbcore.h"
|
||
#include "gdbsupport/rsp-low.h"
|
||
#include "py-event.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;
|
||
|
||
/* 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. */
|
||
|
||
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->mapped_files = 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;
|
||
}
|
||
|
||
/* 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;
|
||
}
|
||
|
||
/* Emit a CorefileChangedEvent event, INF is the inferior in which the core
|
||
file changed. Return 0 on success, or a negative value on error. */
|
||
|
||
static int
|
||
emit_corefile_changed_event (inferior *inf)
|
||
{
|
||
/* If there are no listeners then we are done. */
|
||
if (evregpy_no_listeners_p (gdb_py_events.corefile_changed))
|
||
return 0;
|
||
|
||
gdbpy_ref<> event_obj
|
||
= create_event_object (&corefile_changed_event_object_type);
|
||
if (event_obj == nullptr)
|
||
return -1;
|
||
|
||
gdbpy_ref<inferior_object> inf_obj = inferior_to_inferior_object (inf);
|
||
if (inf_obj == nullptr
|
||
|| evpy_add_attribute (event_obj.get (), "inferior",
|
||
(PyObject *) inf_obj.get ()) < 0)
|
||
return -1;
|
||
|
||
return evpy_emit_event (event_obj.get (), gdb_py_events.corefile_changed);
|
||
}
|
||
|
||
/* Callback from gdb::observers::core_file_changed. The core file for
|
||
INF has been changed. */
|
||
|
||
static void
|
||
cfpy_corefile_changed (inferior *inf)
|
||
{
|
||
/* It's safe to do this even if Python is not initialized, but there
|
||
should be nothing to clear in that case. */
|
||
cfpy_inferior_corefile_data_key.clear (inf);
|
||
|
||
if (!gdb_python_initialized)
|
||
return;
|
||
|
||
gdbpy_enter enter_py;
|
||
|
||
if (emit_corefile_changed_event (inf) < 0)
|
||
gdbpy_print_stack ();
|
||
}
|
||
|
||
/* 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_XDECREF (corefile->mapped_files);
|
||
|
||
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 ()));
|
||
}
|
||
|
||
|
||
|
||
/* 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 ()
|
||
{
|
||
gdb::observers::core_file_changed.attach (cfpy_corefile_changed,
|
||
"py-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;
|
||
}
|
||
|
||
GDBPY_INITIALIZE_FILE (gdbpy_initialize_corefile);
|
||
|
||
|
||
|
||
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." },
|
||
{ "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 }
|
||
};
|
||
|
||
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 */
|
||
};
|
||
|
||
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 */
|
||
};
|