mirror of
https://github.com/JakeHillion/drgn.git
synced 2024-12-22 17:23:06 +00:00
Document with Sphinx
drgn has pretty thorough in-program documentation, but it doesn't have a nice overview or introduction to the basic concepts. This commit adds that using Sphinx. In order to avoid documenting everything in two places, the libdrgn bindings have their docstrings generated from the API documentation. The alternative would be to use Sphinx's autodoc extension, but that's not as flexible and would also require building the extension to build the docs. The documentation for the helpers is generated using autodoc and a small custom extension.
This commit is contained in:
parent
9be2627418
commit
393a1f3149
1
.gitignore
vendored
1
.gitignore
vendored
@ -5,6 +5,7 @@
|
||||
/build
|
||||
/coverage.info
|
||||
/dist
|
||||
/docs/_build
|
||||
/drgn.egg-info
|
||||
/htmlcov
|
||||
__pycache__
|
||||
|
3
.readthedocs.yml
Normal file
3
.readthedocs.yml
Normal file
@ -0,0 +1,3 @@
|
||||
version: 2
|
||||
sphinx:
|
||||
configuration: docs/conf.py
|
@ -1 +1,2 @@
|
||||
recursive-include docs *.py *.rst
|
||||
recursive-include tests *.py
|
||||
|
95
README.md
95
README.md
@ -1,95 +0,0 @@
|
||||
drgn
|
||||
====
|
||||
|
||||
`drgn` is a debugger-as-a-library. In contrast to existing debuggers like
|
||||
[GDB](https://www.gnu.org/software/gdb/) which excel in breakpoint-based
|
||||
debugging, drgn focuses on live introspection. drgn exposes the types and data
|
||||
in a program for easy, expressive scripting.
|
||||
|
||||
drgn was developed for debugging the Linux kernel (as an alternative to the
|
||||
[crash](http://people.redhat.com/anderson/) utility), but it can also debug
|
||||
userspace programs written in C. C++ support is planned.
|
||||
|
||||
Python is the main interface for drgn, although an experimental C library,
|
||||
`libdrgn`, is also provided.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
drgn is built with setuptools. Build it like so:
|
||||
|
||||
```
|
||||
$ python3 setup.py build_ext -i
|
||||
```
|
||||
|
||||
Then, you can either run it locally:
|
||||
|
||||
```
|
||||
$ python3 -m drgn --help
|
||||
```
|
||||
|
||||
Or install it and run it:
|
||||
|
||||
```
|
||||
$ sudo python3 setup.py install
|
||||
$ drgn --help
|
||||
```
|
||||
|
||||
Or, pick your favorite Python package installation method.
|
||||
|
||||
Getting Started
|
||||
---------------
|
||||
|
||||
drgn can be used as a command line tool or as a library. The rest of this
|
||||
section describes using the CLI; the CLI is basically a wrapper around the
|
||||
library which provides a nice interface, including history and tab completion.
|
||||
|
||||
To debug the running kernel, run `sudo drgn -k`. To debug a running program,
|
||||
run `sudo drgn -p $PID`. To debug a core dump (either a kernel vmcore or a
|
||||
userspace core dump), run `drgn -c $PATH`.
|
||||
|
||||
The drgn CLI has an interactive mode and a script mode. If no arguments are
|
||||
passed, drgn runs in interactive mode; otherwise, the given script is run with
|
||||
the given arguments. The drgn CLI is actually just the Python interpreter
|
||||
initialized with a `prog` object representing the debugged program:
|
||||
|
||||
```
|
||||
$ sudo drgn -k
|
||||
>>> prog.type('struct list_head')
|
||||
struct list_head {
|
||||
struct list_head *next;
|
||||
struct list_head *prev;
|
||||
}
|
||||
>>> prog['modules']
|
||||
(struct list_head){
|
||||
.next = (struct list_head *)0xffffffffc0b91048,
|
||||
.prev = (struct list_head *)0xffffffffc0066148,
|
||||
}
|
||||
>>> prog['init_task'].pid
|
||||
(pid_t)0
|
||||
>>> from drgn.helpers.linux import list_for_each_entry
|
||||
>>> for mod in list_for_each_entry('struct module', prog['modules'].address_of_(), 'list'):
|
||||
... if mod.refcnt.counter > 10:
|
||||
... print(mod.name)
|
||||
...
|
||||
(char [56])"snd"
|
||||
(char [56])"evdev"
|
||||
(char [56])"i915"
|
||||
```
|
||||
|
||||
See the in-program documentation in interactive mode with `help(drgn)` for more
|
||||
information. See `examples` and `drgn/helpers` for some examples.
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
Copyright 2018-2019 - Omar Sandoval
|
||||
|
||||
Licensed under the GPLv3 or later
|
||||
|
||||
Acknowledgements
|
||||
----------------
|
||||
|
||||
drgn is named after
|
||||
[this](https://giraffesgiraffes.bandcamp.com/track/drgnfkr-2) because dragons
|
||||
eat [dwarves](http://dwarfstd.org/).
|
99
README.rst
Normal file
99
README.rst
Normal file
@ -0,0 +1,99 @@
|
||||
drgn
|
||||
====
|
||||
|
||||
.. image:: https://readthedocs.org/projects/drgn/badge/?version=latest
|
||||
:target: https://drgn.readthedocs.io/en/latest/?badge=latest
|
||||
:alt: Documentation Status
|
||||
|
||||
.. start-introduction
|
||||
|
||||
drgn is a debugger-as-a-library. In contrast to existing debuggers like `GDB
|
||||
<https://www.gnu.org/software/gdb/>`_ which focus on breakpoint-based
|
||||
debugging, drgn excels in live introspection. drgn exposes the types and
|
||||
variables in a program for easy, expressive scripting in Python. For example,
|
||||
you can debug the Linux kernel:
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
>>> from drgn.helpers.linux import list_for_each_entry
|
||||
>>> for mod in list_for_each_entry('struct module',
|
||||
... prog['modules'].address_of_(),
|
||||
... 'list'):
|
||||
... if mod.refcnt.counter > 10:
|
||||
... print(mod.name)
|
||||
...
|
||||
(char [56])"snd"
|
||||
(char [56])"evdev"
|
||||
(char [56])"i915"
|
||||
|
||||
drgn was developed for debugging the Linux kernel (as an alternative to the
|
||||
`crash <http://people.redhat.com/anderson/>`_ utility), but it can also debug
|
||||
userspace programs written in C. C++ support is planned.
|
||||
|
||||
.. end-introduction
|
||||
|
||||
Documentation can be found at `drgn.readthedocs.io
|
||||
<https://drgn.readthedocs.io>`_.
|
||||
|
||||
Installation
|
||||
------------
|
||||
|
||||
Install the following dependencies:
|
||||
|
||||
* Python 3.6 or newer
|
||||
* elfutils development libraries (libelf and libdw)
|
||||
* GNU autotools (autoconf, automake, and libtool) and pkgconf
|
||||
|
||||
Then, run:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ git clone https://github.com/osandov/drgn.git
|
||||
$ cd drgn
|
||||
$ python3 setup.py build
|
||||
$ sudo python3 setup.py install
|
||||
|
||||
See the `installation documentation
|
||||
<https://drgn.readthedocs.io/en/latest/installation.html>`_ for more details.
|
||||
|
||||
Quick Start
|
||||
-----------
|
||||
|
||||
.. start-quick-start
|
||||
|
||||
To debug the running kernel, run ``sudo drgn -k``. To debug a running program,
|
||||
run ``sudo drgn -p $PID``. To debug a core dump (either a kernel vmcore or a
|
||||
userspace core dump), run ``drgn -c $PATH``. The program must have debugging
|
||||
symbols available.
|
||||
|
||||
Then, you can access variables in the program with ``prog['name']``, access
|
||||
structure members with ``.``, use various predefined helpers, and more:
|
||||
|
||||
.. code-block:: pycon
|
||||
|
||||
$ sudo drgn -k
|
||||
>>> prog['init_task'].comm
|
||||
(char [16])"swapper/0"
|
||||
>>> d_path(fget(find_task(prog, 1), 0).f_path.address_of_())
|
||||
b'/dev/null'
|
||||
>>> max(task.stime for task in for_each_task(prog))
|
||||
(u64)4192109975952
|
||||
>>> sum(disk.gendisk.part0.nr_sects for disk in for_each_disk(prog))
|
||||
(sector_t)999705952
|
||||
|
||||
.. end-quick-start
|
||||
|
||||
See the `user guide <https://drgn.readthedocs.io/en/latest/user_guide.html>`_
|
||||
for more information.
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
.. start-license
|
||||
|
||||
Copyright 2018-2019 Omar Sandoval
|
||||
|
||||
drgn is licensed under the `GPLv3
|
||||
<https://www.gnu.org/licenses/gpl-3.0.en.html>`_ or later.
|
||||
|
||||
.. end-license
|
892
docs/api_reference.rst
Normal file
892
docs/api_reference.rst
Normal file
@ -0,0 +1,892 @@
|
||||
API Reference
|
||||
=============
|
||||
|
||||
.. module:: drgn
|
||||
|
||||
Programs
|
||||
--------
|
||||
|
||||
.. class:: Program
|
||||
|
||||
A ``Program`` represents a crashed or running program. It can be used to
|
||||
lookup type definitions, access variables, and read arbitrary memory.
|
||||
|
||||
The main functionality of a ``Program`` is looking up objects (i.e.,
|
||||
variables, constants, or functions). This is usually done with the
|
||||
:meth:`[] <__getitem__>` operator.
|
||||
|
||||
This class cannot be constructed directly. Instead, use one of the
|
||||
:ref:`api-program-constructors`.
|
||||
|
||||
.. attribute:: flags
|
||||
|
||||
Flags which apply to this program.
|
||||
|
||||
:vartype: ProgramFlags
|
||||
|
||||
.. attribute:: word_size
|
||||
|
||||
Size of a word in this program in bytes.
|
||||
|
||||
:vartype: int
|
||||
|
||||
.. attribute:: byteorder
|
||||
|
||||
Byte order (a.k.a. endianness) in this program (either ``'little'``
|
||||
or ``'big'``).
|
||||
|
||||
:vartype: str
|
||||
|
||||
.. attribute:: __getitem__(name)
|
||||
|
||||
Implement ``self[name]``. Get the object (variable, constant, or
|
||||
function) with the given name.
|
||||
|
||||
If there are multiple objects with the same name, one is returned
|
||||
arbitrarily. In this case, the :meth:`variable()`, :meth:`constant()`,
|
||||
or :meth:`function()` methods can be used instead.
|
||||
|
||||
>>> prog['jiffies']
|
||||
Object(prog, 'volatile unsigned long', address=0xffffffff94c05000)
|
||||
|
||||
:param str name: The object name.
|
||||
:rtype: Object
|
||||
|
||||
.. attribute:: variable(name, filename=None)
|
||||
|
||||
Get the variable with the given name.
|
||||
|
||||
>>> prog.variable('jiffies')
|
||||
Object(prog, 'volatile unsigned long', address=0xffffffff94c05000)
|
||||
|
||||
:param str name: The variable name.
|
||||
:param filename: The source code file that contains the definition. See
|
||||
:ref:`api-filenames`.
|
||||
:type filename: str or None
|
||||
:rtype: Object
|
||||
:raises LookupError: if no variables with the given name are found in
|
||||
the given file
|
||||
|
||||
.. attribute:: constant(name, filename=None)
|
||||
|
||||
Get the constant (e.g., enumeration constant) with the given name.
|
||||
|
||||
Note that support for macro constants is not yet implemented for DWARF
|
||||
files, and most compilers don't generate macro debugging information
|
||||
by default anyways.
|
||||
|
||||
>>> prog.constant('PIDTYPE_MAX')
|
||||
Object(prog, 'enum pid_type', value=4)
|
||||
|
||||
:param str name: The constant name.
|
||||
:param filename: The source code file that contains the definition. See
|
||||
:ref:`api-filenames`.
|
||||
:type filename: str or None
|
||||
:rtype: Object
|
||||
:raises LookupError: if no constants with the given name are found in
|
||||
the given file
|
||||
|
||||
.. attribute:: function(name, filename=None)
|
||||
|
||||
Get the function with the given name.
|
||||
|
||||
>>> prog.function('schedule')
|
||||
Object(prog, 'void (void)', address=0xffffffff94392370)
|
||||
|
||||
:param str name: The function name.
|
||||
:param filename: The source code file that contains the definition. See
|
||||
:ref:`api-filenames`.
|
||||
:type filename: str or None
|
||||
:rtype: Object
|
||||
:raises LookupError: if no functions with the given name are found in
|
||||
the given file
|
||||
|
||||
.. attribute:: type(name, filename=None)
|
||||
|
||||
Get the type with the given name.
|
||||
|
||||
>>> prog.type('long')
|
||||
int_type(name='long', size=8, is_signed=True)
|
||||
|
||||
:param str name: The type name.
|
||||
:param filename: The source code file that contains the definition. See
|
||||
:ref:`api-filenames`.
|
||||
:type filename: str or None
|
||||
:rtype: Type
|
||||
:raises LookupError: if no types with the given name are found in
|
||||
the given file
|
||||
|
||||
.. attribute:: read(address, size, physical=False)
|
||||
|
||||
Read *size* bytes of memory starting at *address* in the program. The
|
||||
address may be virtual (the default) or physical if the program
|
||||
supports it.
|
||||
|
||||
>>> prog.read(0xffffffffbe012b40, 16)
|
||||
b'swapper/0\x00\x00\x00\x00\x00\x00\x00'
|
||||
|
||||
:param int address: The starting address.
|
||||
:param int size: The number of bytes to read.
|
||||
:param bool physical: Whether *address* is a physical memory address.
|
||||
If ``False``, then it is a virtual memory address. Physical memory
|
||||
can usually only be read when the program is an operating system
|
||||
kernel.
|
||||
:rtype: bytes
|
||||
:raises FaultError: if the address range is invalid or the type of
|
||||
address (physical or virtual) is not supported by the program
|
||||
:raises ValueError: if *size* is negative
|
||||
|
||||
.. class:: ProgramFlags
|
||||
|
||||
``ProgramFlags`` is an :class:`enum.IntFlag` of flags that can apply to a
|
||||
:class:`Program` (e.g., about what kind of program it is).
|
||||
|
||||
.. attribute:: IS_LINUX_KERNEL
|
||||
|
||||
The program is the Linux kernel.
|
||||
|
||||
.. _api-filenames:
|
||||
|
||||
Filenames
|
||||
^^^^^^^^^
|
||||
|
||||
The :meth:`Program.type()`, :meth:`Program.variable()`,
|
||||
:meth:`Program.constant()`, and :meth:`Program.function()` methods all take a
|
||||
*filename* parameter to distinguish between multiple definitions with the same
|
||||
name. The filename refers to the source code file that contains the definition.
|
||||
``None`` matches any definition. Otherwise, the filename is matched from right
|
||||
to left, so ``'stdio.h'``, ``'include/stdio.h'``, ``'usr/include/stdio.h'``,
|
||||
and ``'/usr/include/stdio.h'`` would all match a definition in
|
||||
``/usr/include/stdio.h``. If multiple definitions match, one is returned
|
||||
arbitrarily.
|
||||
|
||||
.. _api-program-constructors:
|
||||
|
||||
Program Constructors
|
||||
^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
The drgn command line interface automatically creates a :class:`Program` named
|
||||
``prog``. However, drgn may also be used as a library without the CLI, in which
|
||||
case a ``Program`` must be created manually.
|
||||
|
||||
.. function:: program_from_core_dump(path, verbose=False)
|
||||
|
||||
Create a :class:`Program` from a core dump file. The type of program (e.g.,
|
||||
userspace or kernel) is determined automatically.
|
||||
|
||||
:param str path: Core dump file path.
|
||||
:param bool verbose: Whether to print non-fatal errors to stderr (e.g.,
|
||||
about not being able to find debugging symbols).
|
||||
:rtype: Program
|
||||
|
||||
.. function:: program_from_kernel(verbose=False)
|
||||
|
||||
Create a :class:`Program` from the running operating system kernel. This
|
||||
requires root privileges.
|
||||
|
||||
:param bool verbose: Whether to print non-fatal errors to stderr (e.g.,
|
||||
about not being able to find kernel modules or debugging symbols).
|
||||
:rtype: Program
|
||||
|
||||
.. function:: program_from_pid(pid)
|
||||
|
||||
Create a :class:`Program` from a running program with the given PID. This
|
||||
requires appropriate permissions (on Linux, :manpage:`ptrace(2)` attach
|
||||
permissions).
|
||||
|
||||
:param int pid: Process ID of the program to debug.
|
||||
:rtype: Program
|
||||
|
||||
Objects
|
||||
-------
|
||||
|
||||
.. class:: Object(prog, type=None, *, address=None, value=None, byteorder=None, bit_offset=None, bit_field_size=None)
|
||||
|
||||
An ``Object`` represents a symbol or value in a program. An object may
|
||||
exist in the memory of the program (a *reference*), or it may be a
|
||||
temporary computed value (a *value*).
|
||||
|
||||
All instances of this class have two attributes: :attr:`prog_`, the program
|
||||
that the object is from; and :attr:`type_`, the type of the object.
|
||||
Reference objects also have an :attr:`address_` attribute. Objects may also
|
||||
have a :attr:`byteorder_`, :attr:`bit_offset_`, and
|
||||
:attr:`bit_field_size_`.
|
||||
|
||||
:func:`repr()` of an object returns a Python representation of the object:
|
||||
|
||||
>>> print(repr(prog['jiffies']))
|
||||
Object(prog, 'volatile long unsigned int', address=0xffffffffbf005000)
|
||||
|
||||
:class:`str() <str>` returns a representation of the object in programming
|
||||
language syntax:
|
||||
|
||||
>>> print(prog['jiffies'])
|
||||
(volatile long unsigned int)4326237045
|
||||
|
||||
Note that the drgn CLI is set up so that objects are displayed with
|
||||
``str()`` instead of ``repr()`` (the latter is the default behavior of
|
||||
Python's interactive mode). This means that in the drgn CLI, the call to
|
||||
``print()`` in the second example above is not necessary.
|
||||
|
||||
Objects support the following operators:
|
||||
|
||||
* Arithmetic operators: ``+``, ``-``, ``*``, ``/``, ``%``
|
||||
* Bitwise operators: ``<<``, ``>>``, ``&``, ``|``, ``^``, ``~``
|
||||
* Relational operators: ``==``, ``!=``, ``<``, ``>``, ``<=``, ``>=``
|
||||
* Subscripting: :meth:`[] <__getitem__>` (Python does not have a unary
|
||||
``*`` operator, so pointers are dereferenced with ``ptr[0]``)
|
||||
* Member access: :meth:`. <__getattribute__>` (Python does not have a
|
||||
``->`` operator, so ``.`` is also used to access members of pointers to
|
||||
structures)
|
||||
* The address-of operator: :meth:`drgn.Object.address_of_()` (this is a
|
||||
method because Python does not have a ``&`` operator)
|
||||
* Array length: :meth:`len() <__len__>`
|
||||
|
||||
These operators all have the semantics of the program's programming
|
||||
language. For example, adding two objects from a program written in C
|
||||
results in an object with a type and value according to the rules of C:
|
||||
|
||||
>>> Object(prog, 'unsigned long', value=2**64 - 1) + Object(prog, 'int', value=1)
|
||||
Object(prog, 'unsigned long', value=0)
|
||||
|
||||
If only one operand to a binary operator is an object, the other operand
|
||||
will be converted to an object according to the language's rules for
|
||||
literals:
|
||||
|
||||
>>> Object(prog, 'char', value=0) - 1
|
||||
Object(prog, 'int', value=-1)
|
||||
|
||||
The standard :class:`int() <int>`, :class:`float() <float>`, and
|
||||
:class:`bool() <bool>` functions convert an object to that Python type.
|
||||
Conversion to ``bool`` uses the programming language's notion of
|
||||
"truthiness". Additionally, certain Python functions will automatically
|
||||
coerce an object to the appropriate Python type (e.g., :func:`hex()`,
|
||||
:func:`round()`, and :meth:`list subscripting <object.__getitem__>`).
|
||||
|
||||
Object attributes and methods are named with a trailing underscore to avoid
|
||||
conflicting with structure or union members. The attributes and methods
|
||||
always take precedence; use :meth:`member_()` if there is a conflict.
|
||||
|
||||
Objects are usually obtained directly from a :class:`Program`, but they can
|
||||
be constructed manually, as well (for example, if you got a variable
|
||||
address from a log file).
|
||||
|
||||
:param Program prog: The program to create this object in.
|
||||
:param type: The type of the object. If omitted, this is deduced from
|
||||
*value* according to the language's rules for literals.
|
||||
:type type: str or Type
|
||||
:param int address: The address of this object in the program. Either this
|
||||
or *value* must be given, but not both.
|
||||
:param value: The value of this object. See :meth:`value_()`.
|
||||
:param byteorder: Byte order of the object. This should be ``'little'`` or
|
||||
``'big'``. The default is ``None``, which indicates the program byte
|
||||
order. This must be ``None`` for primitive values.
|
||||
:type byteorder: str or None
|
||||
:param bit_offset: Offset in bits from the object's address to the
|
||||
beginning of the object. The default is ``None``, which means no
|
||||
offset. This must be ``None`` for primitive values.
|
||||
:type bit_offset: int or None
|
||||
:param bit_field_size: Size in bits of this object if it is a bit field.
|
||||
The default is ``None``, which means the object is not a bit field.
|
||||
:type bit_field_size: int or None
|
||||
|
||||
.. attribute:: prog_
|
||||
|
||||
Program that this object is from.
|
||||
|
||||
:vartype: Program
|
||||
|
||||
.. attribute:: type_
|
||||
|
||||
Type of this object.
|
||||
|
||||
:vartype: Type
|
||||
|
||||
.. attribute:: address_
|
||||
|
||||
Address of this object if it is a reference, ``None`` if it is a value.
|
||||
|
||||
:vartype: int or None
|
||||
|
||||
.. attribute:: byteorder_
|
||||
|
||||
Byte order of this object (either ``'little'`` or ``'big'``) if it is a
|
||||
reference or a non-primitive value, ``None`` otherwise.
|
||||
|
||||
:vartype: str or None
|
||||
|
||||
.. attribute:: bit_offset_
|
||||
|
||||
Offset in bits from this object's address to the beginning of the
|
||||
object if it is a reference or a non-primitive value, ``None``
|
||||
otherwise.
|
||||
|
||||
:vartype: int or None
|
||||
|
||||
.. attribute:: bit_field_size_
|
||||
|
||||
Size in bits of this object if it is a bit field, ``None`` if it is
|
||||
not.
|
||||
|
||||
:vartype: int or None
|
||||
|
||||
.. method:: __getattribute__(name)
|
||||
|
||||
Implement ``self.name``.
|
||||
|
||||
If *name* is an attribute of the :class:`Object` class, then this
|
||||
returns that attribute. Otherwise, it is equivalent to
|
||||
:meth:`member_()`.
|
||||
|
||||
>>> print(prog['init_task'].pid)
|
||||
(pid_t)0
|
||||
|
||||
:param str name: Attribute name.
|
||||
|
||||
.. method:: __getitem__(idx)
|
||||
|
||||
Implement ``self[idx]``. Get the array element at the given index.
|
||||
|
||||
>>> print(prog['init_task'].comm[0])
|
||||
(char)115
|
||||
|
||||
This is only valid for pointers and arrays.
|
||||
|
||||
:param int idx: The array index.
|
||||
:rtype: Object
|
||||
:raises TypeError: if this object is not a pointer or array
|
||||
|
||||
.. method:: __len__()
|
||||
|
||||
Implement ``len(self)``. Get the number of elements in this object.
|
||||
|
||||
>>> len(prog['init_task'].comm)
|
||||
16
|
||||
|
||||
This is only valid for arrays.
|
||||
|
||||
:rtype: int
|
||||
:raises TypeError: if this object is not an array with complete type
|
||||
|
||||
.. method:: value_()
|
||||
|
||||
Get the value of this object as a Python object.
|
||||
|
||||
For basic types (integer, floating-point, boolean), this returns an
|
||||
object of the directly corresponding Python type (``int``, ``float``,
|
||||
``bool``). For pointers, this returns the address value of the pointer.
|
||||
For enums, this returns an ``int``. For structures and unions, this
|
||||
returns a ``dict`` of members. For arrays, this returns a ``list`` of
|
||||
values.
|
||||
|
||||
:raises FaultError: if reading the object causes a bad memory access
|
||||
:raises TypeError: if this object has an unreadable type (e.g.,
|
||||
``void``)
|
||||
|
||||
.. method:: string_()
|
||||
|
||||
Read a null-terminated string pointed to by this object.
|
||||
|
||||
This is only valid for pointers and arrays. The element type is
|
||||
ignored; this operates byte-by-byte.
|
||||
|
||||
For pointers and flexible arrays, this stops at the first null byte.
|
||||
|
||||
For complete arrays, this stops at the first null byte or at the end of
|
||||
the array.
|
||||
|
||||
:rtype: bytes
|
||||
:raises FaultError: if reading the string causes a bad memory access
|
||||
:raises TypeError: if this object is not a pointer or array
|
||||
|
||||
.. method:: member_(name)
|
||||
|
||||
Get a member of this object.
|
||||
|
||||
This is valid for structures, unions, and pointers to either.
|
||||
|
||||
Normally the dot operator (``.``) can be used to accomplish the same
|
||||
thing, but this method can be used if there is a name conflict with an
|
||||
Object member or method.
|
||||
|
||||
:param str name: Name of the member.
|
||||
:rtype: Object
|
||||
:raises TypeError: if this object is not a structure, union, or a
|
||||
pointer to either
|
||||
:raises LookupError: if this object does not have a member with the
|
||||
given name
|
||||
|
||||
.. method:: address_of_()
|
||||
|
||||
Get a pointer to this object.
|
||||
|
||||
This corresponds to the address-of (``&``) operator in C. It is only
|
||||
possible for reference objects, as value objects don't have an address
|
||||
in the program.
|
||||
|
||||
As opposed to :attr:`address_`, this returns an ``Object``, not an
|
||||
``int``.
|
||||
|
||||
:rtype: Object
|
||||
:raises ValueError: if this object is a value
|
||||
|
||||
.. method:: read_()
|
||||
|
||||
Read this object (which may be a reference or a value) and return it as
|
||||
a value object.
|
||||
|
||||
This is useful if the object can change in the running program (but of
|
||||
course nothing stops the program from modifying the object while it is
|
||||
being read).
|
||||
|
||||
As opposed to :meth:`value_()`, this returns an ``Object``, not a
|
||||
standard Python type.
|
||||
|
||||
:rtype: Object
|
||||
:raises FaultError: if reading this object causes a bad memory access
|
||||
:raises TypeError: if this object has an unreadable type (e.g.,
|
||||
``void``)
|
||||
|
||||
.. function:: NULL(prog, type)
|
||||
|
||||
Get an object representing ``NULL`` casted to the given type.
|
||||
|
||||
This is equivalent to ``Object(prog, type, value=0)``.
|
||||
|
||||
:param Program prog: The program.
|
||||
:param type: The type.
|
||||
:type type: str or Type
|
||||
:rtype: Object
|
||||
|
||||
.. function:: cast(type, obj)
|
||||
|
||||
Get the value of the given object casted to another type.
|
||||
|
||||
Objects with a scalar type (integer, boolean, enumerated, floating-point,
|
||||
or pointer) can be casted to a different scalar type. Other objects can
|
||||
only be casted to the same type. This always results in a value object. See
|
||||
also :func:`drgn.reinterpret()`.
|
||||
|
||||
:param type: The type to cast to.
|
||||
:type type: str or Type
|
||||
:param Object obj: The object to cast.
|
||||
:rtype: Object
|
||||
|
||||
.. function:: reinterpret(type, obj, byteorder=None)
|
||||
|
||||
Get a copy of the given object reinterpreted as another type and/or byte
|
||||
order.
|
||||
|
||||
This reinterprets the raw memory of the object, so an object can be
|
||||
reinterpreted as any other type. However, value objects with a scalar type
|
||||
cannot be reinterpreted, as their memory layout in the program is not
|
||||
known. Reinterpreting a reference results in a reference, and
|
||||
reinterpreting a value results in a value. See also :func:`drgn.cast()`.
|
||||
|
||||
:param type: The type to reinterpret as.
|
||||
:type type: str or Type
|
||||
:param Object obj: The object to reinterpret.
|
||||
:param byteorder: The byte order to reinterpret as. This should be
|
||||
``'little'`` or ``'big'``. The default is ``None``, which indicates the
|
||||
program byte order.
|
||||
:type byteorder: str or None
|
||||
:rtype: Object
|
||||
|
||||
.. function:: container_of(ptr, type, member)
|
||||
|
||||
Get the containing object of a pointer object.
|
||||
|
||||
This corresponds to the ``container_of()`` macro in C.
|
||||
|
||||
:param Object ptr: The pointer.
|
||||
:param type: The type of the containing object.
|
||||
:type type: str or Type
|
||||
:param str member: The name of the member in ``type``.
|
||||
:raises TypeError: if the object is not a pointer or the type is not a
|
||||
structure or union type
|
||||
:raises LookupError: If the type does not have a member with the given name
|
||||
|
||||
.. _api-reference-types:
|
||||
|
||||
Types
|
||||
-----
|
||||
|
||||
.. class:: Type
|
||||
|
||||
A ``Type`` object describes a type in a program. Each kind of type (e.g.,
|
||||
integer, structure) has different attributes (e.g., name, size). Types can
|
||||
also have qualifiers (e.g., constant, atomic). Accessing an attribute which
|
||||
does not apply to a type raises an :exc:`AttributeError`.
|
||||
|
||||
:func:`repr()` of a Type returns a Python representation of the type:
|
||||
|
||||
>>> print(repr(prog.type('sector_t')))
|
||||
typedef_type(name='sector_t', type=int_type(name='unsigned long', size=8, is_signed=False))
|
||||
|
||||
:class:`str() <str>` returns a representation of the type in programming
|
||||
language syntax:
|
||||
|
||||
>>> print(prog.type('sector_t'))
|
||||
typedef unsigned long sector_t
|
||||
|
||||
The drgn CLI is set up so that types are displayed with ``str()`` instead
|
||||
of ``repr()`` by default.
|
||||
|
||||
This class cannot be constructed directly. Instead, use one of the
|
||||
:ref:`api-type-constructors`.
|
||||
|
||||
.. attribute:: kind
|
||||
|
||||
Kind of this type.
|
||||
|
||||
:vartype: TypeKind
|
||||
|
||||
.. attribute:: qualifiers
|
||||
|
||||
Bitmask of this type's qualifier.
|
||||
|
||||
:vartype: Qualifiers
|
||||
|
||||
.. attribute:: name
|
||||
|
||||
Name of this type. This is present for integer, boolean,
|
||||
floating-point, complex, and typedef types.
|
||||
|
||||
:vartype: str
|
||||
|
||||
.. attribute:: tag
|
||||
|
||||
Tag of this type, or ``None`` if this is an anonymous type. This is
|
||||
present for structure, union, and enumerated types.
|
||||
|
||||
:vartype: str or None
|
||||
|
||||
.. attribute:: size
|
||||
|
||||
Size of this type in bytes, or ``None`` if this is an incomplete type.
|
||||
This is present for integer, boolean, floating-point, complex,
|
||||
structure, union, and pointer types.
|
||||
|
||||
:vartype: int or None
|
||||
|
||||
.. attribute:: length
|
||||
|
||||
Number of elements in this type, or ``None`` if this is an incomplete
|
||||
type. This is only present for array types.
|
||||
|
||||
:vartype: int or None
|
||||
|
||||
.. attribute:: is_signed
|
||||
|
||||
Whether this type is signed. This is only present for integer types.
|
||||
|
||||
:vartype: bool
|
||||
|
||||
.. attribute:: type
|
||||
|
||||
Type underlying this type, defined as follows:
|
||||
|
||||
* For complex types, the corresponding the real type.
|
||||
* For typedef types, the aliased type.
|
||||
* For enumerated types, the compatible integer type, which is ``None``
|
||||
if this is an incomplete type.
|
||||
* For pointer types, the referenced type.
|
||||
* For array types, the element type.
|
||||
* For function types, the return type.
|
||||
|
||||
For other types, this attribute is not present.
|
||||
|
||||
:vartype: Type
|
||||
|
||||
.. attribute:: members
|
||||
|
||||
List of members of this type, or ``None`` if this is an incomplete
|
||||
type. This is present for structure and union types.
|
||||
|
||||
Each member is a (type, name, bit offset, bit field size) tuple. The
|
||||
name is ``None`` if the member is unnamed; the bit field size is zero
|
||||
if the member is not a bit field.
|
||||
|
||||
:vartype: list[tuple(Type, str or None, int, int)]
|
||||
|
||||
.. attribute:: enumerators
|
||||
|
||||
List of enumeration constants of this type, or ``None`` if this is an
|
||||
incomplete type. This is only present for enumerated types.
|
||||
|
||||
Each enumeration constant is a (name, value) tuple.
|
||||
|
||||
:vartype: list[tuple(str, int)] or None
|
||||
|
||||
.. attribute:: parameters
|
||||
|
||||
List of parameters of this type. This is only present for function
|
||||
types.
|
||||
|
||||
Each parameter is a (type, name) tuple. The name is ``None`` if the
|
||||
parameter is unnamed.
|
||||
|
||||
:vartype: list[tuple(Type, str or None)]
|
||||
|
||||
.. attribute:: is_variadic
|
||||
|
||||
Whether this type takes a variable number of arguments. This is only
|
||||
present for function types.
|
||||
|
||||
:vartype: bool
|
||||
|
||||
.. method:: type_name()
|
||||
|
||||
Get a descriptive full name of this type.
|
||||
|
||||
:rtype: str
|
||||
|
||||
.. method:: is_complete()
|
||||
|
||||
Get whether this type is complete (i.e., the type definition is known).
|
||||
This is always ``False`` for void types. It may be ``False`` for
|
||||
structure, union, enumerated, and array types, as well as typedef types
|
||||
where the underlying type is one of those. Otherwise, it is always
|
||||
``True``.
|
||||
|
||||
:rtype: bool
|
||||
|
||||
.. method:: qualified(qualifiers)
|
||||
|
||||
Get a copy of this type with different qualifiers.
|
||||
|
||||
Note that the original qualifiers are replaced, not added to.
|
||||
|
||||
:param qualifiers: New type qualifiers.
|
||||
:type qualifiers: Qualifiers or None
|
||||
:rtype: Type
|
||||
|
||||
.. method:: unqualified()
|
||||
|
||||
Get a copy of this type with no qualifiers.
|
||||
|
||||
:rtype: Type
|
||||
|
||||
.. class:: TypeKind
|
||||
|
||||
``TypeKind`` is an :class:`enum.Enum` of the different kinds of types.
|
||||
|
||||
.. attribute:: VOID
|
||||
|
||||
Void type.
|
||||
|
||||
.. attribute:: INT
|
||||
|
||||
Integer type.
|
||||
|
||||
.. attribute:: BOOL
|
||||
|
||||
Boolean type.
|
||||
|
||||
.. attribute:: FLOAT
|
||||
|
||||
Floating-point type.
|
||||
|
||||
.. attribute:: COMPLEX
|
||||
|
||||
Complex type.
|
||||
|
||||
.. attribute:: STRUCT
|
||||
|
||||
Structure type.
|
||||
|
||||
.. attribute:: UNION
|
||||
|
||||
Union type.
|
||||
|
||||
.. attribute:: ENUM
|
||||
|
||||
Enumerated type.
|
||||
|
||||
.. attribute:: TYPEDEF
|
||||
|
||||
Type definition (a.k.a. alias) type.
|
||||
|
||||
.. attribute:: POINTER
|
||||
|
||||
Pointer type.
|
||||
|
||||
.. attribute:: ARRAY
|
||||
|
||||
Array type.
|
||||
|
||||
.. attribute:: FUNCTION
|
||||
|
||||
Function type.
|
||||
|
||||
.. class:: Qualifiers
|
||||
|
||||
``Qualifiers`` is an :class:`enum.IntFlag` of type qualifiers.
|
||||
|
||||
.. attribute:: CONST
|
||||
|
||||
Constant type.
|
||||
|
||||
.. attribute:: VOLATILE
|
||||
|
||||
Volatile type.
|
||||
|
||||
.. attribute:: RESTRICT
|
||||
|
||||
`Restrict <https://en.cppreference.com/w/c/language/restrict>`_ type.
|
||||
|
||||
.. attribute:: ATOMIC
|
||||
|
||||
Atomic type.
|
||||
|
||||
.. _api-type-constructors:
|
||||
|
||||
Type Constructors
|
||||
^^^^^^^^^^^^^^^^^
|
||||
|
||||
Custom drgn types can be created with the following factory functions. These
|
||||
can be used just like types obtained from :meth:`Program.type()`.
|
||||
|
||||
.. function:: void_type(qualifiers=None)
|
||||
|
||||
Create a new void type. It has kind :attr:`TypeKind.VOID`.
|
||||
|
||||
:param qualifiers: :attr:`Type.qualifiers`
|
||||
:type qualifiers: Qualifiers or None
|
||||
:rtype: Type
|
||||
|
||||
.. function:: int_type(name, size, is_signed, qualifiers=None)
|
||||
|
||||
Create a new integer type. It has kind :attr:`TypeKind.INT`.
|
||||
|
||||
:param str name: :attr:`Type.name`
|
||||
:param int size: :attr:`Type.size`
|
||||
:param bool is_signed: :attr:`Type.is_signed`
|
||||
:param qualifiers: :attr:`Type.qualifiers`
|
||||
:type qualifiers: Qualifiers or None
|
||||
:rtype: Type
|
||||
|
||||
.. function:: bool_type(name, size, qualifiers=None)
|
||||
|
||||
Create a new boolean type. It has kind :attr:`TypeKind.BOOL`.
|
||||
|
||||
:param str name: :attr:`Type.name`
|
||||
:param int size: :attr:`Type.size`
|
||||
:param qualifiers: :attr:`Type.qualifiers`
|
||||
:type qualifiers: Qualifiers or None
|
||||
:rtype: Type
|
||||
|
||||
.. function:: float_type(name, size, qualifiers=None)
|
||||
|
||||
Create a new floating-point type. It has kind :attr:`TypeKind.FLOAT`.
|
||||
|
||||
:param str name: :attr:`Type.name`
|
||||
:param int size: :attr:`Type.size`
|
||||
:param qualifiers: :attr:`Type.qualifiers`
|
||||
:type qualifiers: Qualifiers or None
|
||||
:rtype: Type
|
||||
|
||||
.. function:: complex_type(name, size, type, qualifiers=None)
|
||||
|
||||
Create a new complex type. It has kind :attr:`TypeKind.COMPLEX`.
|
||||
|
||||
:param str name: :attr:`Type.name`
|
||||
:param int size: :attr:`Type.size`
|
||||
:param Type type: The corresponding real type (:attr:`Type.type`)
|
||||
:param qualifiers: :attr:`Type.qualifiers`
|
||||
:type qualifiers: Qualifiers or None
|
||||
:rtype: Type
|
||||
|
||||
.. function:: struct_type(tag, size, members, qualifiers=None)
|
||||
|
||||
Create a new structure type. It has kind :attr:`TypeKind.STRUCT`.
|
||||
|
||||
:param tag: :attr:`Type.tag`
|
||||
:type tag: str or None
|
||||
:param int size: :attr:`Type.size`
|
||||
:param list[tuple] members: :attr:`Type.members`. The type of a member may
|
||||
be given as a callable returning a ``Type``; it will be called the
|
||||
first time that the member is accessed. The name, bit offset, and bit
|
||||
field size may be omitted; they default to ``None``, 0, and 0,
|
||||
respectively.
|
||||
:param qualifiers: :attr:`Type.qualifiers`
|
||||
:type qualifiers: Qualifiers or None
|
||||
:rtype: Type
|
||||
|
||||
.. function:: union_type(tag, size, members, qualifiers=None)
|
||||
|
||||
Create a new union type. It has kind :attr:`TypeKind.UNION`. Otherwise,
|
||||
this is the same as :func:`struct_type()`.
|
||||
|
||||
.. function:: enum_type(tag, type, enumerators, qualifiers=None)
|
||||
|
||||
Create a new enumerated type. It has kind :attr:`TypeKind.ENUM`.
|
||||
|
||||
:param tag: :attr:`Type.tag`
|
||||
:type tag: str or None
|
||||
:param type: The compatible integer type (:attr:`Type.type`)
|
||||
:type param Type or None:
|
||||
:param enumerators: :attr:`Type.enumerators`
|
||||
:type enumerators: list[tuple] or None
|
||||
:param qualifiers: :attr:`Type.qualifiers`
|
||||
:type qualifiers: Qualifiers or None
|
||||
:rtype: Type
|
||||
|
||||
.. function:: typedef_type(name, type, qualifiers=None)
|
||||
|
||||
Create a new typedef type. It has kind :attr:`TypeKind.TYPEDEF`.
|
||||
|
||||
:param str name: :attr:`Type.name`
|
||||
:param Type type: The aliased type (:attr:`Type.type`)
|
||||
:param qualifiers: :attr:`Type.qualifiers`
|
||||
:type qualifiers: Qualifiers or None
|
||||
:rtype: Type
|
||||
|
||||
.. function:: pointer_type(size, type, qualifiers=None)
|
||||
|
||||
Create a new pointer type. It has kind :attr:`TypeKind.POINTER`,
|
||||
|
||||
:param int size: :attr:`Type.size`
|
||||
:param Type type: The referenced type (:attr:`Type.type`)
|
||||
:param qualifiers: :attr:`Type.qualifiers`
|
||||
:type qualifiers: Qualifiers or None
|
||||
:rtype: Type
|
||||
|
||||
.. function:: array_type(length, type, qualifiers=None)
|
||||
|
||||
Create a new array type. It has kind :attr:`TypeKind.ARRAY`.
|
||||
|
||||
:param length: :attr:`Type.length`
|
||||
:type length: int or None
|
||||
:param Type type: The element type (:attr:`Type.type`)
|
||||
:param qualifiers: :attr:`Type.qualifiers`
|
||||
:type qualifiers: Qualifiers or None
|
||||
:rtype: Type
|
||||
|
||||
.. function:: function_type(type, parameters, is_variadic=False, qualifiers=None)
|
||||
|
||||
Create a new function type. It has kind :attr:`TypeKind.FUNCTION`.
|
||||
|
||||
:param Type type: The return type (:attr:`Type.type`)
|
||||
:param list[tuple] parameters: :attr:`Type.parameters`. The type of a
|
||||
parameter may be given as a callable returning a ``Type``; it will be
|
||||
called the first time that the parameter is accessed. The name may be
|
||||
omitted and defaults to ``None``.
|
||||
:param bool is_variadic: :attr:`Type.is_variadic`
|
||||
:param qualifiers: :attr:`Type.qualifiers`
|
||||
:type qualifiers: Qualifiers or None
|
||||
:rtype: Type
|
||||
|
||||
Exceptions
|
||||
----------
|
||||
|
||||
.. exception:: FaultError
|
||||
|
||||
This error is raised when a bad memory access is attempted (i.e., when
|
||||
accessing a memory address which is not valid in a program, or when
|
||||
accessing out of bounds of a value object).
|
||||
|
||||
.. exception:: FileFormatError
|
||||
|
||||
This is error raised when a file cannot be parsed according to its expected
|
||||
format (e.g., ELF or DWARF).
|
38
docs/conf.py
Normal file
38
docs/conf.py
Normal file
@ -0,0 +1,38 @@
|
||||
import os.path
|
||||
import sys
|
||||
|
||||
sys.path.append(os.path.abspath('..'))
|
||||
sys.path.append(os.path.abspath('exts'))
|
||||
|
||||
master_doc = 'index'
|
||||
|
||||
extensions = [
|
||||
'autopackage',
|
||||
'setuptools_config',
|
||||
'sphinx.ext.autodoc',
|
||||
'sphinx.ext.extlinks',
|
||||
'sphinx.ext.intersphinx',
|
||||
'sphinx.ext.viewcode',
|
||||
]
|
||||
|
||||
autodoc_mock_imports = ['_drgn']
|
||||
|
||||
extlinks = {
|
||||
'linux': ('https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/%s', ''),
|
||||
}
|
||||
|
||||
intersphinx_mapping = {
|
||||
'python': ('https://docs.python.org/3', None),
|
||||
}
|
||||
|
||||
manpages_url = 'http://man7.org/linux/man-pages/man{section}/{page}.{section}.html'
|
||||
|
||||
html_theme = 'alabaster'
|
||||
|
||||
html_theme_options = {
|
||||
'description': 'Debugger-as-a-library',
|
||||
'github_user': 'osandov',
|
||||
'github_repo': 'drgn',
|
||||
'github_button': True,
|
||||
'github_type': 'star',
|
||||
}
|
65
docs/exts/autopackage.py
Normal file
65
docs/exts/autopackage.py
Normal file
@ -0,0 +1,65 @@
|
||||
# Copyright 2018-2019 - Omar Sandoval
|
||||
# SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
import docutils.nodes
|
||||
from docutils.statemachine import StringList
|
||||
import importlib
|
||||
import pkgutil
|
||||
import sphinx.ext.autodoc
|
||||
from sphinx.util.docutils import SphinxDirective
|
||||
from sphinx.util.nodes import nested_parse_with_titles
|
||||
|
||||
|
||||
# sphinx.ext.autodoc doesn't recursively document packages, so we need our own
|
||||
# directive to do that.
|
||||
class AutopackageDirective(SphinxDirective):
|
||||
required_arguments = 1
|
||||
optional_arguments = 0
|
||||
|
||||
def run(self):
|
||||
sourcename = ''
|
||||
def aux(name):
|
||||
module = importlib.import_module(name)
|
||||
|
||||
contents = StringList()
|
||||
contents.append(f'.. automodule:: {name}', sourcename)
|
||||
if hasattr(module, '__all__'):
|
||||
module_attrs = [
|
||||
attr_name for attr_name in module.__all__
|
||||
if getattr(module, attr_name).__module__ == name
|
||||
]
|
||||
if module_attrs:
|
||||
contents.append(f" :members: {', '.join(module_attrs)}",
|
||||
sourcename)
|
||||
else:
|
||||
contents.append(' :members:', sourcename)
|
||||
contents.append('', sourcename)
|
||||
|
||||
node = docutils.nodes.section()
|
||||
nested_parse_with_titles(self.state, contents, node)
|
||||
|
||||
# If this module defines any sections, then submodules should go
|
||||
# inside of the last one.
|
||||
section = node
|
||||
for child in node.children:
|
||||
if isinstance(child, docutils.nodes.section):
|
||||
section = child
|
||||
|
||||
if hasattr(module, '__path__'):
|
||||
submodules = sorted(module_info.name for module_info in
|
||||
pkgutil.iter_modules(module.__path__,
|
||||
prefix=name + '.'))
|
||||
for submodule in submodules:
|
||||
section.extend(aux(submodule))
|
||||
|
||||
return node.children
|
||||
|
||||
with sphinx.ext.autodoc.mock(self.env.config.autodoc_mock_imports):
|
||||
return aux(self.arguments[0])
|
||||
|
||||
|
||||
def setup(app):
|
||||
app.setup_extension('sphinx.ext.autodoc')
|
||||
app.add_directive('autopackage', AutopackageDirective)
|
||||
|
||||
return {'parallel_read_safe': True}
|
63
docs/exts/setuptools_config.py
Normal file
63
docs/exts/setuptools_config.py
Normal file
@ -0,0 +1,63 @@
|
||||
# https://pypi.org/project/jaraco.packaging/
|
||||
#
|
||||
# Copyright Jason R. Coombs
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import os
|
||||
import sys
|
||||
import subprocess
|
||||
|
||||
|
||||
if 'check_output' not in dir(subprocess):
|
||||
import subprocess32 as subprocess
|
||||
|
||||
|
||||
def setup(app):
|
||||
app.add_config_value('package_url', '', '')
|
||||
app.connect('builder-inited', load_config_from_setup)
|
||||
app.connect('html-page-context', add_package_url)
|
||||
|
||||
|
||||
def load_config_from_setup(app):
|
||||
"""
|
||||
Replace values in app.config from package metadata
|
||||
"""
|
||||
# for now, assume project root is one level up
|
||||
root = os.path.join(app.confdir, '..')
|
||||
setup_script = os.path.join(root, 'setup.py')
|
||||
fields = ['--name', '--version', '--url', '--author']
|
||||
dist_info_cmd = [sys.executable, setup_script] + fields
|
||||
output = subprocess.check_output(
|
||||
dist_info_cmd,
|
||||
cwd=root,
|
||||
universal_newlines=True,
|
||||
)
|
||||
outputs = output.strip().split('\n')
|
||||
project, version, url, author = outputs
|
||||
app.config.project = project
|
||||
app.config.version = app.config.release = version
|
||||
app.config.package_url = url
|
||||
app.config.author = app.config.copyright = author
|
||||
|
||||
|
||||
def add_package_url(app, pagename, templatename, context, doctree):
|
||||
context['package_url'] = app.config.package_url
|
1
docs/helpers.rst
Normal file
1
docs/helpers.rst
Normal file
@ -0,0 +1 @@
|
||||
.. autopackage:: drgn.helpers
|
37
docs/index.rst
Normal file
37
docs/index.rst
Normal file
@ -0,0 +1,37 @@
|
||||
drgn
|
||||
====
|
||||
|
||||
.. include:: ../README.rst
|
||||
:start-after: start-introduction
|
||||
:end-before: end-introduction
|
||||
|
||||
In addition to the main Python API, an experimental C library, ``libdrgn``, is
|
||||
also available.
|
||||
|
||||
See the :doc:`installation` instructions. Then, start with the
|
||||
:doc:`user_guide`.
|
||||
|
||||
License
|
||||
-------
|
||||
|
||||
.. include:: ../README.rst
|
||||
:start-after: start-license
|
||||
:end-before: end-license
|
||||
|
||||
Acknowledgements
|
||||
----------------
|
||||
|
||||
drgn is named after `this
|
||||
<https://giraffesgiraffes.bandcamp.com/track/drgnfkr-2>`_ because dragons eat
|
||||
`dwarves <http://dwarfstd.org/>`_.
|
||||
|
||||
Table of Contents
|
||||
-----------------
|
||||
|
||||
.. toctree::
|
||||
:maxdepth: 3
|
||||
|
||||
installation
|
||||
user_guide
|
||||
api_reference
|
||||
helpers
|
47
docs/installation.rst
Normal file
47
docs/installation.rst
Normal file
@ -0,0 +1,47 @@
|
||||
Installation
|
||||
============
|
||||
|
||||
.. highlight:: console
|
||||
|
||||
drgn depends on `Python <https://www.python.org/>`_ 3.6 or newer as well as
|
||||
`elfutils <https://sourceware.org/elfutils/>`_. The build requires `GCC
|
||||
<https://gcc.gnu.org/>`_ or `Clang <https://clang.llvm.org/>`_, `pkgconf
|
||||
<http://pkgconf.org/>`_, and `setuptools
|
||||
<https://pypi.org/project/setuptools/>`_. A build from a Git checkout also
|
||||
requires the GNU Autotools (`autoconf
|
||||
<https://www.gnu.org/software/autoconf/>`_, `automake
|
||||
<https://www.gnu.org/software/automake/automake.html>`_, and `libtool
|
||||
<https://www.gnu.org/software/libtool/libtool.html>`_). Install those
|
||||
dependencies:
|
||||
|
||||
Arch Linux::
|
||||
|
||||
$ sudo pacman -S --needed autoconf automake libtool gcc pkgconf libelf python python-setuptools
|
||||
|
||||
Debian/Ubuntu::
|
||||
|
||||
$ sudo apt-get install autoconf automake libtool gcc pkgconf libelf-dev libdw-dev python3 python3-setuptools
|
||||
|
||||
Note that Debian, Ubuntu Trusty, and Ubuntu Xenial ship Python versions which
|
||||
are too old, so a newer version must be installed manually.
|
||||
|
||||
Due to a packaging `bug
|
||||
<https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=885071>`_, the following may
|
||||
also be required::
|
||||
|
||||
$ sudo apt-get install liblzma-dev zlib1g-dev
|
||||
|
||||
Fedora::
|
||||
|
||||
$ sudo dnf install autoconf automake libtool gcc pkgconf elfutils-devel python3 python3-setuptools
|
||||
|
||||
Then, drgn can be built and installed::
|
||||
|
||||
$ python3 setup.py build
|
||||
$ sudo python3 setup.py install
|
||||
$ drgn --help
|
||||
|
||||
Or, it can be be built and run locally::
|
||||
|
||||
$ python3 setup.py build_ext -i
|
||||
$ python3 -m drgn --help
|
284
docs/user_guide.rst
Normal file
284
docs/user_guide.rst
Normal file
@ -0,0 +1,284 @@
|
||||
User Guide
|
||||
==========
|
||||
|
||||
Quick Start
|
||||
-----------
|
||||
|
||||
.. include:: ../README.rst
|
||||
:start-after: start-quick-start
|
||||
:end-before: end-quick-start
|
||||
|
||||
Core Concepts
|
||||
-------------
|
||||
|
||||
.. highlight:: pycon
|
||||
|
||||
Programs
|
||||
^^^^^^^^
|
||||
|
||||
A program being debugged is represented by an instance of the
|
||||
:class:`drgn.Program` class. The drgn CLI is initialized with a ``Program``
|
||||
named ``prog``; unless you are using the drgn library directly, this is usually
|
||||
the only ``Program`` you will need.
|
||||
|
||||
A ``Program`` is used to look up type definitions, access variables, and read
|
||||
arbitrary memory::
|
||||
|
||||
>>> prog.type('unsigned long')
|
||||
int_type(name='unsigned long', size=8, is_signed=False)
|
||||
>>> prog['jiffies']
|
||||
Object(prog, 'volatile unsigned long', address=0xffffffffbe405000)
|
||||
>>> prog.read(0xffffffffbe411e10, 16)
|
||||
b'swapper/0\x00\x00\x00\x00\x00\x00\x00'
|
||||
|
||||
The :meth:`drgn.Program.type()`, :meth:`drgn.Program.variable()`,
|
||||
:meth:`drgn.Program.constant()`, and :meth:`drgn.Program.function()` methods
|
||||
look up those various things in a program. :meth:`drgn.Program.read()` reads
|
||||
memory from the program's address space. The :meth:`[]
|
||||
<drgn.Program.__getitem__>` operator looks up a variable, constant, or
|
||||
function::
|
||||
|
||||
>>> prog['jiffies'] == prog.variable('jiffies')
|
||||
True
|
||||
|
||||
It is usually more convenient to use the ``[]`` operator rather than the
|
||||
``variable()``, ``constant()``, or ``function()`` methods unless the program
|
||||
has multiple objects with the same name, in which case the methods provide more
|
||||
control.
|
||||
|
||||
Objects
|
||||
^^^^^^^
|
||||
|
||||
Variables, constants, functions, and computed values are all called *objects*
|
||||
in drgn. Objects are represented by the :class:`drgn.Object` class. An object
|
||||
may exist in the memory of the program (a *reference*)::
|
||||
|
||||
>>> Object(prog, 'int', address=0xffffffffc09031a0)
|
||||
|
||||
Or, an object may be a temporary computed value (a *value*)::
|
||||
|
||||
>>> Object(prog, 'int', value=4)
|
||||
|
||||
What makes drgn scripts expressive is that objects can be used almost exactly
|
||||
like they would be in the program's own source code. For example, structure
|
||||
members can be accessed with the dot (``.``) operator, arrays can be
|
||||
subscripted with ``[]``, arithmetic can be performed, and objects can be
|
||||
compared::
|
||||
|
||||
>>> print(prog['init_task'].comm[0])
|
||||
(char)115
|
||||
>>> print(repr(prog['init_task'].nsproxy.mnt_ns.mounts + 1))
|
||||
Object(prog, 'unsigned int', value=34)
|
||||
>>> prog['init_task'].nsproxy.mnt_ns.pending_mounts > 0
|
||||
False
|
||||
|
||||
A common use case is converting a ``drgn.Object`` to a Python value so it can
|
||||
be used by a standard Python library. There are a few ways to do this:
|
||||
|
||||
* The :meth:`drgn.Object.value_()` method gets the value of the object with the
|
||||
directly corresponding Python type (i.e., integers and pointers become
|
||||
``int``, floating-point types become ``float``, booleans become ``bool``,
|
||||
arrays become ``list``, structures and unions become ``dict``).
|
||||
* The :meth:`drgn.Object.string_()` method gets a null-terminated string as
|
||||
``bytes`` from an array or pointer.
|
||||
* The :class:`int() <int>`, :class:`float() <float>`, and :class:`bool()
|
||||
<bool>` functions do an explicit conversion to that Python type.
|
||||
|
||||
Objects have several attributes; the most important are
|
||||
:attr:`drgn.Object.prog_` and :attr:`drgn.Object.type_`. The former is the
|
||||
:class:`drgn.Program` that the object is from, and the latter is the
|
||||
:class:`drgn.Type` of the object.
|
||||
|
||||
Note that all attributes and methods of the ``Object`` class end with an
|
||||
underscore (``_``) in order to avoid conflicting with structure or union
|
||||
members. The ``Object`` attributes and methods always take precedence; use
|
||||
:meth:`drgn.Object.member_()` if there is a conflict.
|
||||
|
||||
References vs. Values
|
||||
"""""""""""""""""""""
|
||||
|
||||
The main difference between reference objects and value objects is how they are
|
||||
evaluated. References are read from the program's memory every time they are
|
||||
evaluated; values simply return the stored value (:meth:`drgn.Object.read_()`
|
||||
reads a reference object and returns it as a value object)::
|
||||
|
||||
>>> import time
|
||||
>>> jiffies = prog['jiffies']
|
||||
>>> jiffies.value_()
|
||||
4391639989
|
||||
>>> time.sleep(1)
|
||||
>>> jiffies.value_()
|
||||
4391640290
|
||||
>>> jiffies2 = jiffies.read_()
|
||||
>>> jiffies2.value_()
|
||||
4391640291
|
||||
>>> time.sleep(1)
|
||||
>>> jiffies2.value_()
|
||||
4391640291
|
||||
>>> jiffies.value_()
|
||||
4391640593
|
||||
|
||||
References have a :attr:`drgn.Object.address_` attribute, which is the object's
|
||||
address as a Python ``int``. This is slightly different from the
|
||||
:meth:`drgn.Object.address_of_()` method, which returns the address as a
|
||||
``drgn.Object``. Of course, both references and values can have a pointer type;
|
||||
``address_`` refers to the address of the pointer object itself, and
|
||||
:meth:`drgn.Object.value_()` refers to the value of the pointer (i.e., the
|
||||
address it points to)::
|
||||
|
||||
>>> address = prog['jiffies'].address_
|
||||
>>> type(address)
|
||||
<class 'int'>
|
||||
>>> print(hex(address))
|
||||
0xffffffffbe405000
|
||||
>>> jiffiesp = prog['jiffies'].address_of_()
|
||||
>>> jiffiesp
|
||||
Object(prog, 'volatile unsigned long *', value=0xffffffffbe405000)
|
||||
>>> print(hex(jiffiesp.value_()))
|
||||
0xffffffffbe405000
|
||||
|
||||
Types
|
||||
^^^^^
|
||||
|
||||
drgn automatically obtains type definitions from the program. Types are
|
||||
represented by the :class:`drgn.Type` class and created by various factory
|
||||
functions like :func:`int_type()`::
|
||||
|
||||
>>> prog.type('int')
|
||||
int_type(name='int', size=4, is_signed=True)
|
||||
|
||||
You won't usually need to work with types directly, but see
|
||||
:ref:`api-reference-types` if you do.
|
||||
|
||||
Helpers
|
||||
^^^^^^^
|
||||
|
||||
Some programs have common data structures that you may want to examine. For
|
||||
example, consider linked lists in the Linux kernel:
|
||||
|
||||
.. code-block:: c
|
||||
|
||||
struct list_head {
|
||||
struct list_head *next, *prev;
|
||||
};
|
||||
|
||||
#define list_for_each(pos, head) \
|
||||
for (pos = (head)->next; pos != (head); pos = pos->next)
|
||||
|
||||
When working with these lists, you'd probably want to define a function:
|
||||
|
||||
.. code-block:: python3
|
||||
|
||||
def list_for_each(head):
|
||||
pos = head.next
|
||||
while pos != head:
|
||||
yield pos
|
||||
pos = pos.next
|
||||
|
||||
Then, you could use it like so for any list you need to look at::
|
||||
|
||||
>>> for pos in list_for_each(head):
|
||||
... do_something_with(pos)
|
||||
|
||||
Of course, it would be a waste of time and effort for everyone to have to
|
||||
define these helpers for themselves, so drgn includes a collection of helpers
|
||||
for many use cases. See :doc:`helpers`.
|
||||
|
||||
Command Line Interface
|
||||
----------------------
|
||||
|
||||
The drgn CLI is basically a wrapper around the drgn library which automatically
|
||||
creates a :class:`drgn.Program`. The CLI can be run in interactive mode or
|
||||
script mode.
|
||||
|
||||
Script Mode
|
||||
^^^^^^^^^^^
|
||||
|
||||
Script mode is useful for reusable scripts. Simply pass the path to the script
|
||||
along with any arguments:
|
||||
|
||||
.. code-block:: console
|
||||
|
||||
$ cat script.py
|
||||
import sys
|
||||
from drgn.helpers.linux import find_task
|
||||
|
||||
pid = int(sys.argv[1])
|
||||
uid = find_task(prog, pid).cred.uid.val.value_()
|
||||
print(f'PID {pid} is being run by UID {uid}')
|
||||
$ sudo drgn -k script.py 601
|
||||
PID 601 is being run by UID 1000
|
||||
|
||||
It's even possible to run drgn scripts directly with the proper `shebang
|
||||
<https://en.wikipedia.org/wiki/Shebang_(Unix)>`_::
|
||||
|
||||
$ cat script2.py
|
||||
#!/usr/bin/drgn -k
|
||||
|
||||
mounts = prog['init_task'].nsproxy.mnt_ns.mounts.value_()
|
||||
print(f'You have {mounts} filesystems mounted')
|
||||
$ sudo ./script2.py
|
||||
You have 36 filesystems mounted
|
||||
|
||||
You usually shouldn't depend on an executable being installed at a specific
|
||||
absolute path. With newer versions of GNU coreutils (since v8.30), you can
|
||||
use |env -S|_:
|
||||
|
||||
.. |env -S| replace:: ``env --split-string``
|
||||
.. _env -S: https://www.gnu.org/software/coreutils/manual/html_node/env-invocation.html#g_t_002dS_002f_002d_002dsplit_002dstring-usage-in-scripts
|
||||
|
||||
.. code-block:: sh
|
||||
|
||||
#!/usr/bin/env -S drgn -k
|
||||
|
||||
Interactive Mode
|
||||
^^^^^^^^^^^^^^^^
|
||||
|
||||
Interactive mode uses the Python interpreter's `interactive mode
|
||||
<https://docs.python.org/3/tutorial/interpreter.html#interactive-mode>`_ and
|
||||
adds a few nice features, including:
|
||||
|
||||
* History
|
||||
* Tab completion
|
||||
* Automatic import of relevant helpers
|
||||
* Pretty printing of objects and types
|
||||
|
||||
The default behavior of the Python `REPL
|
||||
<https://en.wikipedia.org/wiki/Read%E2%80%93eval%E2%80%93print_loop>`_ is to
|
||||
print the output of :func:`repr()`. For :class:`drgn.Object` and
|
||||
:class:`drgn.Type`, this is a raw representation::
|
||||
|
||||
>>> print(repr(prog['jiffies']))
|
||||
Object(prog, 'volatile unsigned long', address=0xffffffffbe405000)
|
||||
>>> print(repr(prog.type('atomic_t')))
|
||||
typedef_type(name='atomic_t', type=struct_type(tag=None, size=4, members=((int_type(name='int', size=4, is_signed=True), 'counter', 0, 0),)))
|
||||
|
||||
The standard :func:`print()` function uses the output of :func:`str()`. For
|
||||
drgn objects and types, this is a representation in programming language
|
||||
syntax::
|
||||
|
||||
>>> print(prog['jiffies'])
|
||||
(volatile unsigned long)4395387628
|
||||
>>> print(prog.type('atomic_t'))
|
||||
typedef struct {
|
||||
int counter;
|
||||
} atomic_t
|
||||
|
||||
In interactive mode, the drgn CLI automatically uses ``str()`` instead of
|
||||
``repr()`` for objects and types, so you don't need to call ``print()``
|
||||
explicitly::
|
||||
|
||||
$ sudo drgn -k
|
||||
>>> prog['jiffies']
|
||||
(volatile unsigned long)4395387628
|
||||
>>> prog.type('atomic_t')
|
||||
typedef struct {
|
||||
int counter;
|
||||
} atomic_t
|
||||
|
||||
Next Steps
|
||||
----------
|
||||
|
||||
Refer to the :doc:`api_reference`. Look through the :doc:`helpers`. Browse
|
||||
through the official `examples
|
||||
<https://github.com/osandov/drgn/tree/master/examples>`_.
|
@ -40,18 +40,17 @@ that package should be considered implementation details and should not
|
||||
be used.
|
||||
"""
|
||||
|
||||
from typing import Union
|
||||
|
||||
from _drgn import (
|
||||
__version__,
|
||||
FaultError,
|
||||
FileFormatError,
|
||||
NULL,
|
||||
Object,
|
||||
Program,
|
||||
ProgramFlags,
|
||||
Qualifiers,
|
||||
Type,
|
||||
TypeKind,
|
||||
__version__,
|
||||
array_type,
|
||||
bool_type,
|
||||
cast,
|
||||
@ -102,13 +101,3 @@ __all__ = [
|
||||
'union_type',
|
||||
'void_type',
|
||||
]
|
||||
|
||||
|
||||
def NULL(prog: Program, type: Union[str, Type]) -> Object:
|
||||
"""
|
||||
Return an Object representing NULL cast to the given type. The type can
|
||||
be a string or a Type object.
|
||||
|
||||
This is equivalent to Object(prog, type, value=0).
|
||||
"""
|
||||
return Object(prog, type, value=0)
|
||||
|
@ -1,11 +1,18 @@
|
||||
# Copyright 2018 - Omar Sandoval
|
||||
# Copyright 2018-2019 - Omar Sandoval
|
||||
# SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
"""
|
||||
Common program helpers
|
||||
Helpers
|
||||
-------
|
||||
|
||||
This package contains subpackages which provide helpers for working with
|
||||
particular types of programs.
|
||||
The ``drgn.helpers`` package contains subpackages which provide helpers for
|
||||
working with particular types of programs. Currently, there are only helpers
|
||||
for the Linux kernel. In the future, there may be helpers for, e.g., glibc and
|
||||
libstdc++.
|
||||
|
||||
Parameter types and return types are :class:`drgn.Object` unless noted
|
||||
otherwise. Many helpers include a C function signature indicating the expected
|
||||
object types.
|
||||
"""
|
||||
|
||||
from typing import Iterable
|
||||
|
@ -1,21 +1,33 @@
|
||||
# Copyright 2018 - Omar Sandoval
|
||||
# Copyright 2018-2019 - Omar Sandoval
|
||||
# SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
"""
|
||||
Linux kernel helpers
|
||||
Linux Kernel
|
||||
------------
|
||||
|
||||
This package contains several modules for working with data structures and
|
||||
subsystems in the Linux kernel. The helpers are available from the individual
|
||||
modules in which they are defined and from this top-level package. E.g., the
|
||||
following are both valid:
|
||||
The ``drgn.helpers.linux`` package contains several modules for working with
|
||||
data structures and subsystems in the Linux kernel. The helpers are available
|
||||
from the individual modules in which they are defined and from this top-level
|
||||
package. E.g., the following are both valid:
|
||||
|
||||
>>> from drgn.helpers.linux.list import list_for_each_entry
|
||||
>>> from drgn.helpers.linux import list_for_each_entry
|
||||
|
||||
In interactive mode, the following is done automatically when debugging the
|
||||
Linux kernel:
|
||||
Iterator macros (``for_each_foo``) are a common idiom in the Linux kernel. The
|
||||
equivalent drgn helpers are implemented as Python :ref:`generators
|
||||
<python:tut-generators>`. For example, the following code in C:
|
||||
|
||||
>>> from drgn.helpers.linux import *
|
||||
.. code-block:: c
|
||||
|
||||
list_for_each(pos, head)
|
||||
do_something_with(pos);
|
||||
|
||||
Translates to the following code in Python:
|
||||
|
||||
.. code-block:: python3
|
||||
|
||||
for pos in list_for_each(head):
|
||||
do_something_with(pos)
|
||||
"""
|
||||
|
||||
from drgn.helpers.linux.block import *
|
||||
|
@ -1,92 +1,110 @@
|
||||
# Copyright 2018 - Omar Sandoval
|
||||
# Copyright 2018-2019 - Omar Sandoval
|
||||
# SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
"""
|
||||
Linux kernel block layer helpers
|
||||
Block Layer
|
||||
-----------
|
||||
|
||||
This module provides helpers for working with the Linux block layer, including
|
||||
disks (struct gendisk) and partitions (struct hd_struct).
|
||||
The ``drgn.helpers.linux.block`` module provides helpers for working with the
|
||||
Linux block layer, including disks (``struct gendisk``) and partitions
|
||||
(``struct hd_struct``).
|
||||
"""
|
||||
|
||||
import typing
|
||||
|
||||
from drgn import Object, container_of
|
||||
from drgn.helpers import escape_string
|
||||
from drgn.helpers.linux.device import MAJOR, MINOR
|
||||
from drgn.helpers.linux.device import MAJOR, MINOR, MKDEV
|
||||
from drgn.helpers.linux.list import list_for_each_entry
|
||||
|
||||
__all__ = [
|
||||
'Disk',
|
||||
'Partition',
|
||||
'disk_devt',
|
||||
'disk_name',
|
||||
'for_each_disk',
|
||||
'print_disks',
|
||||
'part_devt',
|
||||
'part_name',
|
||||
'for_each_partition',
|
||||
'print_partitions',
|
||||
]
|
||||
|
||||
|
||||
class Disk(typing.NamedTuple):
|
||||
"""A disk. gendisk is a struct gendisk * object."""
|
||||
major: int
|
||||
minor: int
|
||||
name: bytes
|
||||
gendisk: Object
|
||||
def disk_devt(disk):
|
||||
"""
|
||||
.. c:function:: dev_t disk_devt(struct gendisk *disk)
|
||||
|
||||
Get a disk's device number.
|
||||
"""
|
||||
return MKDEV(disk.major, disk.first_minor)
|
||||
|
||||
|
||||
def disk_name(disk):
|
||||
"""
|
||||
.. c:function:: char *disk_name(struct gendisk *disk)
|
||||
|
||||
Get the name of a disk (e.g., ``sda``).
|
||||
|
||||
:rtype: bytes
|
||||
"""
|
||||
return disk.disk_name.string_()
|
||||
|
||||
|
||||
def for_each_disk(prog):
|
||||
"""
|
||||
for_each_disk() -> Iterator[Disk]
|
||||
Iterate over all disks in the system.
|
||||
|
||||
Return an iterator over all disks in the system.
|
||||
:return: Iterator of ``struct gendisk *`` objects.
|
||||
"""
|
||||
devices = prog['block_class'].p.klist_devices.k_list.address_of_()
|
||||
disk_type = prog['disk_type'].address_of_()
|
||||
for device in list_for_each_entry('struct device', devices, 'knode_class.n_node'):
|
||||
for device in list_for_each_entry('struct device', devices,
|
||||
'knode_class.n_node'):
|
||||
if device.type == disk_type:
|
||||
obj = container_of(device, 'struct gendisk', 'part0.__dev')
|
||||
dev = device.devt.value_()
|
||||
yield Disk(MAJOR(dev), MINOR(dev), device.kobj.name.string_(), obj)
|
||||
yield container_of(device, 'struct gendisk', 'part0.__dev')
|
||||
|
||||
|
||||
def print_disks(prog):
|
||||
"""Print all of the disks in the system."""
|
||||
for disk in for_each_disk(prog):
|
||||
major = disk.major.value_()
|
||||
minor = disk.first_minor.value_()
|
||||
name = escape_string(disk_name(disk), escape_backslash=True)
|
||||
print(f'{major}:{minor} {name} ({disk.type_.type_name()})0x{disk.value_():x}')
|
||||
|
||||
|
||||
def part_devt(part):
|
||||
"""
|
||||
print_disks()
|
||||
.. c:function:: dev_t part_devt(struct hd_struct *part)
|
||||
|
||||
Print all of the disks in the system.
|
||||
Get a partition's device number.
|
||||
"""
|
||||
for major, minor, name, obj in for_each_disk(prog):
|
||||
name = escape_string(name, escape_backslash=True)
|
||||
print(f'{major}:{minor} {name} ({obj.type_.type_name()})0x{obj.value_():x}')
|
||||
return part.__dev.devt
|
||||
|
||||
|
||||
class Partition(typing.NamedTuple):
|
||||
"""A disk partition. hd_struct is a struct hd_struct * object."""
|
||||
major: int
|
||||
minor: int
|
||||
name: bytes
|
||||
hd_struct: Object
|
||||
def part_name(part):
|
||||
"""
|
||||
.. c:function:: char *part_name(struct hd_struct *part)
|
||||
|
||||
Get the name of a partition (e.g., ``sda1``).
|
||||
|
||||
:rtype: bytes
|
||||
"""
|
||||
return part.__dev.kobj.name.string_()
|
||||
|
||||
|
||||
def for_each_partition(prog):
|
||||
"""
|
||||
for_each_partition() -> Iterator[Partition]
|
||||
Iterate over all partitions in the system.
|
||||
|
||||
Return an iterator over all partitions in the system.
|
||||
:return: Iterator of ``struct hd_struct *`` objects.
|
||||
"""
|
||||
devices = prog['block_class'].p.klist_devices.k_list.address_of_()
|
||||
for device in list_for_each_entry('struct device', devices, 'knode_class.n_node'):
|
||||
obj = container_of(device, 'struct hd_struct', '__dev')
|
||||
dev = device.devt.value_()
|
||||
yield Partition(MAJOR(dev), MINOR(dev), device.kobj.name.string_(),
|
||||
obj)
|
||||
for device in list_for_each_entry('struct device', devices,
|
||||
'knode_class.n_node'):
|
||||
yield container_of(device, 'struct hd_struct', '__dev')
|
||||
|
||||
|
||||
def print_partitions(prog):
|
||||
"""
|
||||
print_partitions()
|
||||
|
||||
Print all of the partitions in the system.
|
||||
"""
|
||||
for major, minor, name, obj in for_each_partition(prog):
|
||||
name = escape_string(name, escape_backslash=True)
|
||||
print(f'{major}:{minor} {name} ({obj.type_.type_name()})0x{obj.value_():x}')
|
||||
"""Print all of the partitions in the system."""
|
||||
for part in for_each_partition(prog):
|
||||
devt = part_devt(part).value_()
|
||||
name = escape_string(part_name(part), escape_backslash=True)
|
||||
print(f'{MAJOR(devt)}:{MINOR(devt)} {name} ({part.type_.type_name()})0x{part.value_():x}')
|
||||
|
@ -1,10 +1,12 @@
|
||||
# Copyright 2018 - Omar Sandoval
|
||||
# Copyright 2018-2019 - Omar Sandoval
|
||||
# SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
"""
|
||||
Linux kernel CPU mask helpers
|
||||
CPU Masks
|
||||
---------
|
||||
|
||||
This module provides helpers for working with CPU masks from "linux/cpumask.h".
|
||||
The ``drgn.helpers.linux.cpumask`` module provides helpers for working with CPU
|
||||
masks from :linux:`include/linux/cpumask.h`.
|
||||
"""
|
||||
|
||||
__all__ = [
|
||||
@ -17,9 +19,11 @@ __all__ = [
|
||||
|
||||
def for_each_cpu(mask):
|
||||
"""
|
||||
for_each_cpu(struct cpumask)
|
||||
.. c:function:: for_each_cpu(struct cpumask mask)
|
||||
|
||||
Return an iterator over all of the CPUs in the given mask, as ints.
|
||||
Iterate over all of the CPUs in the given mask.
|
||||
|
||||
:rtype: Iterator[int]
|
||||
"""
|
||||
bits = mask.bits
|
||||
word_bits = 8 * bits.type_.type.sizeof()
|
||||
@ -32,26 +36,26 @@ def for_each_cpu(mask):
|
||||
|
||||
def for_each_possible_cpu(prog):
|
||||
"""
|
||||
for_each_possible_cpu()
|
||||
Iterate over all possible CPUs.
|
||||
|
||||
Return an iterator over all possible CPUs, as ints.
|
||||
:rtype: Iterator[int]
|
||||
"""
|
||||
return for_each_cpu(prog['__cpu_possible_mask'])
|
||||
|
||||
|
||||
def for_each_online_cpu(prog):
|
||||
"""
|
||||
for_each_online_cpu()
|
||||
Iterate over all online CPUs.
|
||||
|
||||
Return an iterator over all online CPUs, as ints.
|
||||
:rtype: Iterator[int]
|
||||
"""
|
||||
return for_each_cpu(prog['__cpu_online_mask'])
|
||||
|
||||
|
||||
def for_each_present_cpu(prog):
|
||||
"""
|
||||
for_each_present_cpu()
|
||||
Iterate over all present CPUs.
|
||||
|
||||
Return an iterator over all present CPUs, as ints.
|
||||
:rtype: Iterator[int]
|
||||
"""
|
||||
return for_each_cpu(prog['__cpu_present_mask'])
|
||||
|
@ -1,14 +1,15 @@
|
||||
# Copyright 2018 - Omar Sandoval
|
||||
# Copyright 2018-2019 - Omar Sandoval
|
||||
# SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
"""
|
||||
Linux kernel device helpers
|
||||
Devices
|
||||
-------
|
||||
|
||||
This module provides helpers for working with Linux devices, including the
|
||||
kernel encoding of dev_t.
|
||||
The ``drgn.helpers.linux.device`` module provides helpers for working with
|
||||
Linux devices, including the kernel encoding of ``dev_t``.
|
||||
"""
|
||||
|
||||
from drgn import cast, Object
|
||||
from drgn import Object, cast
|
||||
|
||||
__all__ = [
|
||||
'MAJOR',
|
||||
@ -24,9 +25,9 @@ _MINORMASK = ((1 << _MINORBITS) - 1)
|
||||
|
||||
def MAJOR(dev):
|
||||
"""
|
||||
unsigned int MAJOR(dev_t)
|
||||
.. c:function:: unsigned int MAJOR(dev_t dev)
|
||||
|
||||
Return the major ID of a kernel dev_t.
|
||||
Return the major ID of a kernel ``dev_t``.
|
||||
"""
|
||||
major = dev >> _MINORBITS
|
||||
if isinstance(major, Object):
|
||||
@ -36,9 +37,9 @@ def MAJOR(dev):
|
||||
|
||||
def MINOR(dev):
|
||||
"""
|
||||
unsigned int MINOR(dev_t)
|
||||
.. c:function:: unsigned int MINOR(dev_t dev)
|
||||
|
||||
Return the major ID of a kernel dev_t.
|
||||
Return the minor ID of a kernel ``dev_t``.
|
||||
"""
|
||||
minor = dev & _MINORMASK
|
||||
if isinstance(minor, Object):
|
||||
@ -48,9 +49,9 @@ def MINOR(dev):
|
||||
|
||||
def MKDEV(major, minor):
|
||||
"""
|
||||
dev_t MKDEV(unsigned int major, unsigned int minor)
|
||||
.. c:function:: dev_t MKDEV(unsigned int major, unsigned int minor)
|
||||
|
||||
Return a kernel dev_t from the major and minor IDs.
|
||||
Return a kernel ``dev_t`` from the major and minor IDs.
|
||||
"""
|
||||
dev = (major << _MINORBITS) | minor
|
||||
if isinstance(dev, Object):
|
||||
|
@ -1,26 +1,28 @@
|
||||
# Copyright 2018 - Omar Sandoval
|
||||
# Copyright 2018-2019 - Omar Sandoval
|
||||
# SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
"""
|
||||
Linux kernel filesystem helpers
|
||||
Virtual Filesystem Layer
|
||||
------------------------
|
||||
|
||||
This module provides helpers for working with the Linux virtual filesystem
|
||||
(VFS) layer, including mounts, dentries, and inodes.
|
||||
The ``drgn.helpers.linux.fs`` module provides helpers for working with the
|
||||
Linux virtual filesystem (VFS) layer, including mounts, dentries, and inodes.
|
||||
"""
|
||||
|
||||
import os
|
||||
import typing
|
||||
|
||||
from drgn import Object, Program, container_of
|
||||
from drgn.helpers import escape_string
|
||||
from drgn.helpers.linux.list import hlist_for_each_entry, list_for_each_entry
|
||||
|
||||
__all__ = [
|
||||
'Mount',
|
||||
'd_path',
|
||||
'dentry_path',
|
||||
'inode_path',
|
||||
'inode_paths',
|
||||
'mount_src',
|
||||
'mount_dst',
|
||||
'mount_fstype',
|
||||
'for_each_mount',
|
||||
'print_mounts',
|
||||
'fget',
|
||||
@ -31,10 +33,10 @@ __all__ = [
|
||||
|
||||
def d_path(path_or_vfsmnt, dentry=None):
|
||||
"""
|
||||
char *d_path(struct path *)
|
||||
char *d_path(struct vfsmount *, struct dentry *)
|
||||
.. c:function:: char *d_path(struct path *path)
|
||||
.. c:function:: char *d_path(struct vfsmount *vfsmnt, struct dentry *dentry)
|
||||
|
||||
Return the full path of a dentry given a struct path or a mount and a
|
||||
Return the full path of a dentry given a ``struct path *`` or a mount and a
|
||||
dentry.
|
||||
"""
|
||||
type_name = str(path_or_vfsmnt.type_.type_name())
|
||||
@ -72,7 +74,7 @@ def d_path(path_or_vfsmnt, dentry=None):
|
||||
|
||||
def dentry_path(dentry):
|
||||
"""
|
||||
char *dentry_path(struct dentry *)
|
||||
.. c:function:: char *dentry_path(struct dentry *dentry)
|
||||
|
||||
Return the path of a dentry from the root of its filesystem.
|
||||
"""
|
||||
@ -88,7 +90,7 @@ def dentry_path(dentry):
|
||||
|
||||
def inode_path(inode):
|
||||
"""
|
||||
char *inode_path(struct inode *)
|
||||
.. c:function:: char *inode_path(struct inode *inode)
|
||||
|
||||
Return any path of an inode from the root of its filesystem.
|
||||
"""
|
||||
@ -98,34 +100,70 @@ def inode_path(inode):
|
||||
|
||||
def inode_paths(inode):
|
||||
"""
|
||||
inode_paths(struct inode *)
|
||||
.. c:function:: inode_paths(struct inode *inode)
|
||||
|
||||
Return an iterator over all of the paths of an inode from the root of its
|
||||
filesystem.
|
||||
|
||||
:rtype: Iterator[bytes]
|
||||
"""
|
||||
return (
|
||||
dentry_path(dentry) for dentry in
|
||||
hlist_for_each_entry('struct dentry', inode.i_dentry.address_of_(), 'd_u.d_alias')
|
||||
hlist_for_each_entry('struct dentry', inode.i_dentry.address_of_(),
|
||||
'd_u.d_alias')
|
||||
)
|
||||
|
||||
|
||||
class Mount(typing.NamedTuple):
|
||||
"""A mounted filesystem. mount is a struct mount * object."""
|
||||
src: bytes
|
||||
dst: bytes
|
||||
fstype: bytes
|
||||
mount: Object
|
||||
def mount_src(mnt):
|
||||
"""
|
||||
.. c:function:: char *mount_src(struct mount *mnt)
|
||||
|
||||
Get the source device name for a mount.
|
||||
|
||||
:rtype: bytes
|
||||
"""
|
||||
return mnt.mnt_devname.string_()
|
||||
|
||||
|
||||
def mount_dst(mnt):
|
||||
"""
|
||||
.. c:function:: char *mount_dst(struct mount *mnt)
|
||||
|
||||
Get the path of a mount point.
|
||||
|
||||
:rtype: bytes
|
||||
"""
|
||||
return d_path(mnt.mnt.address_of_(), mnt.mnt.mnt_root)
|
||||
|
||||
|
||||
def mount_fstype(mnt):
|
||||
"""
|
||||
.. c:function:: char *mount_fstype(struct mount *mnt)
|
||||
|
||||
Get the filesystem type of a mount.
|
||||
|
||||
:rtype: bytes
|
||||
"""
|
||||
sb = mnt.mnt.mnt_sb.read_()
|
||||
fstype = sb.s_type.name.string_()
|
||||
subtype = sb.s_subtype.read_()
|
||||
if subtype:
|
||||
subtype = subtype.string_()
|
||||
if subtype:
|
||||
fstype += b'.' + subtype
|
||||
return fstype
|
||||
|
||||
|
||||
def for_each_mount(prog_or_ns, src=None, dst=None, fstype=None):
|
||||
"""
|
||||
for_each_mount(struct mnt_namespace *, char *src, char *dst,
|
||||
char *fstype) -> Iterator[Mount]
|
||||
.. c:function:: for_each_mount(struct mnt_namespace *ns, char *src, char *dst, char *fstype)
|
||||
|
||||
Return an iterator over all of the mounts in a given namespace. If given a
|
||||
Program object instead, the initial mount namespace is used. The returned
|
||||
Iterate over all of the mounts in a given namespace. If given a
|
||||
:class:`Program` instead, the initial mount namespace is used. returned
|
||||
mounts can be filtered by source, destination, or filesystem type, all of
|
||||
which are encoded using os.fsencode().
|
||||
which are encoded using :func:`os.fsencode()`.
|
||||
|
||||
:return: Iterator of ``struct mount *`` objects.
|
||||
"""
|
||||
if isinstance(prog_or_ns, Program):
|
||||
ns = prog_or_ns['init_task'].nsproxy.mnt_ns
|
||||
@ -139,57 +177,44 @@ def for_each_mount(prog_or_ns, src=None, dst=None, fstype=None):
|
||||
fstype = os.fsencode(fstype)
|
||||
for mnt in list_for_each_entry('struct mount', ns.list.address_of_(),
|
||||
'mnt_list'):
|
||||
mnt_src = mnt.mnt_devname.string_()
|
||||
if src is not None and mnt_src != src:
|
||||
continue
|
||||
mnt_dst = d_path(mnt.mnt.address_of_(), mnt.mnt.mnt_root)
|
||||
if dst is not None and mnt_dst != dst:
|
||||
continue
|
||||
sb = mnt.mnt.mnt_sb.read_()
|
||||
mnt_fstype = sb.s_type.name.string_()
|
||||
subtype = sb.s_subtype.read_()
|
||||
if subtype:
|
||||
subtype = subtype.string_()
|
||||
if subtype:
|
||||
mnt_fstype += b'.' + subtype
|
||||
if fstype is not None and mnt_fstype != fstype:
|
||||
continue
|
||||
yield Mount(mnt_src, mnt_dst, mnt_fstype, mnt)
|
||||
if ((src is None or mount_src(mnt) == src) and
|
||||
(dst is None or mount_dst(mnt) == dst) and
|
||||
(fstype is None or mount_fstype(mnt) == fstype)):
|
||||
yield mnt
|
||||
|
||||
|
||||
def print_mounts(prog_or_ns, src=None, dst=None, fstype=None):
|
||||
"""
|
||||
print_mounts(struct mnt_namespace *, char *src, char *dst, char *fstype)
|
||||
.. c:function:: print_mounts(struct mnt_namespace *ns, char *src, char *dst, char *fstype)
|
||||
|
||||
Print the mount table of a given namespace. The arguments are the same as
|
||||
for_each_mount(). The output format is similar to /proc/mounts but prints
|
||||
the value of each struct mount *.
|
||||
:func:`for_each_mount()`. The output format is similar to ``/proc/mounts``
|
||||
but prints the value of each ``struct mount *``.
|
||||
"""
|
||||
for mnt_src, mnt_dst, mnt_fstype, mnt in for_each_mount(prog_or_ns, src,
|
||||
dst, fstype):
|
||||
mnt_src = escape_string(mnt_src, escape_backslash=True)
|
||||
mnt_dst = escape_string(mnt_dst, escape_backslash=True)
|
||||
mnt_fstype = escape_string(mnt_fstype, escape_backslash=True)
|
||||
for mnt in for_each_mount(prog_or_ns, src, dst, fstype):
|
||||
mnt_src = escape_string(mount_src(mnt), escape_backslash=True)
|
||||
mnt_dst = escape_string(mount_dst(mnt), escape_backslash=True)
|
||||
mnt_fstype = escape_string(mount_fstype(mnt), escape_backslash=True)
|
||||
print(f'{mnt_src} {mnt_dst} {mnt_fstype} ({mnt.type_.type_name()})0x{mnt.value_():x}')
|
||||
|
||||
|
||||
def fget(task, fd):
|
||||
"""
|
||||
struct file *fget(struct task_struct *, int fd)
|
||||
.. c:function:: struct file *fget(struct task_struct *task, int fd)
|
||||
|
||||
Return the kernel file descriptor (struct file *) of the fd of a given
|
||||
task.
|
||||
Return the kernel file descriptor of the fd of a given task.
|
||||
"""
|
||||
return task.files.fdt.fd[fd]
|
||||
|
||||
|
||||
def for_each_file(task):
|
||||
"""
|
||||
for_each_file(struct task_struct *)
|
||||
.. c:function:: for_each_file(struct task_struct *task)
|
||||
|
||||
Return an iterator over all of the files open in a given task. The
|
||||
generated values are (fd, path, struct file *) tuples. The path is returned
|
||||
as bytes.
|
||||
Iterate over all of the files open in a given task.
|
||||
|
||||
:return: Iterator of (fd, ``struct file *``) tuples.
|
||||
:rtype: Iterator[tuple[int, Object]]
|
||||
"""
|
||||
fdt = task.files.fdt.read_()
|
||||
bits_per_long = 8 * fdt.open_fds.type_.type.size
|
||||
@ -199,16 +224,17 @@ def for_each_file(task):
|
||||
if word & (1 << j):
|
||||
fd = i * bits_per_long + j
|
||||
file = fdt.fd[fd].read_()
|
||||
yield fd, d_path(file.f_path), file
|
||||
yield fd, file
|
||||
|
||||
|
||||
def print_files(task):
|
||||
"""
|
||||
print_files(struct task_struct *)
|
||||
.. c:function:: print_files(struct task_struct *task)
|
||||
|
||||
Print the open files of a given task.
|
||||
"""
|
||||
for fd, path, file in for_each_file(task):
|
||||
for fd, file in for_each_file(task):
|
||||
path = d_path(file.f_path)
|
||||
if path is None:
|
||||
path = file.f_inode.i_sb.s_type.name.string_()
|
||||
path = escape_string(path, escape_backslash=True)
|
||||
|
@ -1,13 +1,14 @@
|
||||
# Copyright 2018 - Omar Sandoval
|
||||
# Copyright 2018-2019 - Omar Sandoval
|
||||
# SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
"""
|
||||
Linux kernel IDR helpers
|
||||
IDR
|
||||
---
|
||||
|
||||
This module provides helpers for working with the IDR data structure in
|
||||
"linux/idr.h". An IDR provides a mapping from an ID to a pointer. This
|
||||
currently only supports Linux v4.11+; before this, IDRs were not based on radix
|
||||
trees.
|
||||
The ``drgn.helpers.linux.idr`` module provides helpers for working with the IDR
|
||||
data structure in :linux:`include/linux/idr.h`. An IDR provides a mapping from
|
||||
an ID to a pointer. This currently only supports Linux v4.11+; before this,
|
||||
IDRs were not based on radix trees.
|
||||
"""
|
||||
|
||||
from drgn.helpers.linux.radixtree import radix_tree_for_each, radix_tree_lookup
|
||||
@ -21,10 +22,10 @@ __all__ = [
|
||||
|
||||
def idr_find(idr, id):
|
||||
"""
|
||||
void *idr_find(struct idr *, unsigned long id)
|
||||
.. c:function:: void *idr_find(struct idr *idr, unsigned long id)
|
||||
|
||||
Look up the entry with the given id in an IDR. If it is not found, this
|
||||
returns a NULL object.
|
||||
returns a ``NULL`` object.
|
||||
"""
|
||||
# idr_base was added in v4.16.
|
||||
try:
|
||||
@ -36,10 +37,12 @@ def idr_find(idr, id):
|
||||
|
||||
def idr_for_each(idr):
|
||||
"""
|
||||
idr_for_each(struct idr *)
|
||||
.. c:function:: idr_for_each(struct idr *idr)
|
||||
|
||||
Return an iterator over all of the entries in an IDR. The generated values
|
||||
are (index, entry) tuples.
|
||||
Iterate over all of the entries in an IDR.
|
||||
|
||||
:return: Iterator of (index, ``void *``) tuples.
|
||||
:rtype: Iterator[tuple[int, Object]]
|
||||
"""
|
||||
try:
|
||||
base = idr.idr_base.value_()
|
||||
|
@ -1,11 +1,13 @@
|
||||
# Copyright 2018 - Omar Sandoval
|
||||
# Copyright 2018-2019 - Omar Sandoval
|
||||
# SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
"""
|
||||
Linux kernel linked list helpers
|
||||
Linked Lists
|
||||
------------
|
||||
|
||||
This module provides helpers for working with the doubly-linked list
|
||||
implementations in "linux/list.h".
|
||||
The ``drgn.helpers.linux.list`` module provides helpers for working with the
|
||||
doubly-linked list implementations (``struct list_head`` and ``struct
|
||||
hlist_head``) in :linux:`include/linux/list.h`.
|
||||
"""
|
||||
|
||||
from drgn import container_of
|
||||
@ -26,7 +28,7 @@ __all__ = [
|
||||
|
||||
def list_empty(head):
|
||||
"""
|
||||
bool list_empty(struct list_head *)
|
||||
.. c:function:: bool list_empty(struct list_head *head)
|
||||
|
||||
Return whether a list is empty.
|
||||
"""
|
||||
@ -36,7 +38,7 @@ def list_empty(head):
|
||||
|
||||
def list_is_singular(head):
|
||||
"""
|
||||
bool list_is_singular(struct list_head *)
|
||||
.. c:function:: bool list_is_singular(struct list_head *head)
|
||||
|
||||
Return whether a list has only one element.
|
||||
"""
|
||||
@ -47,9 +49,11 @@ def list_is_singular(head):
|
||||
|
||||
def list_for_each(head):
|
||||
"""
|
||||
list_for_each(struct list_head *)
|
||||
.. c:function:: list_for_each(struct list_head *head)
|
||||
|
||||
Return an iterator over all of the nodes in a list.
|
||||
Iterate over all of the nodes in a list.
|
||||
|
||||
:return: Iterator of ``struct list_head *`` objects.
|
||||
"""
|
||||
head = head.read_()
|
||||
pos = head.next.read_()
|
||||
@ -60,9 +64,11 @@ def list_for_each(head):
|
||||
|
||||
def list_for_each_reverse(head):
|
||||
"""
|
||||
list_for_each_reverse(struct list_head *)
|
||||
.. c:function:: list_for_each_reverse(struct list_head *head)
|
||||
|
||||
Return an iterator over all of the nodes in a list in reverse order.
|
||||
Iterate over all of the nodes in a list in reverse order.
|
||||
|
||||
:return: Iterator of ``struct list_head *`` objects.
|
||||
"""
|
||||
head = head.read_()
|
||||
pos = head.prev.read_()
|
||||
@ -73,10 +79,12 @@ def list_for_each_reverse(head):
|
||||
|
||||
def list_for_each_entry(type, head, member):
|
||||
"""
|
||||
list_for_each_entry(type, struct list_head *, member)
|
||||
.. c:function:: list_for_each_entry(type, struct list_head *head, member)
|
||||
|
||||
Return an iterator over all of the entries in a list, given the type of the
|
||||
entry and the struct list_head member in that type.
|
||||
Iterate over all of the entries in a list, given the type of the entry and
|
||||
the ``struct list_head`` member in that type.
|
||||
|
||||
:return: Iterator of ``type *`` objects.
|
||||
"""
|
||||
for pos in list_for_each(head):
|
||||
yield container_of(pos, type, member)
|
||||
@ -84,10 +92,12 @@ def list_for_each_entry(type, head, member):
|
||||
|
||||
def list_for_each_entry_reverse(type, head, member):
|
||||
"""
|
||||
list_for_each_entry_reverse(type, struct list_head *, member)
|
||||
.. c:function:: list_for_each_entry_reverse(type, struct list_head *head, member)
|
||||
|
||||
Return an iterator over all of the entries in a list in reverse order,
|
||||
given the type of the entry and the struct list_head member in that type.
|
||||
Iterate over all of the entries in a list in reverse order, given the type
|
||||
of the entry and the ``struct list_head`` member in that type.
|
||||
|
||||
:return: Iterator of ``type *`` objects.
|
||||
"""
|
||||
for pos in list_for_each_reverse(head):
|
||||
yield container_of(pos, type, member)
|
||||
@ -95,7 +105,7 @@ def list_for_each_entry_reverse(type, head, member):
|
||||
|
||||
def hlist_empty(head):
|
||||
"""
|
||||
bool hlist_empty(struct hlist_head *)
|
||||
.. c:function:: bool hlist_empty(struct hlist_head *head)
|
||||
|
||||
Return whether a hash list is empty.
|
||||
"""
|
||||
@ -104,9 +114,11 @@ def hlist_empty(head):
|
||||
|
||||
def hlist_for_each(head):
|
||||
"""
|
||||
hlist_for_each(struct hlist_head *)
|
||||
.. c:function:: hlist_for_each(struct hlist_head *head)
|
||||
|
||||
Return an iterator over all of the nodes in a hash list.
|
||||
Iterate over all of the nodes in a hash list.
|
||||
|
||||
:return: Iterator of ``struct hlist_node *`` objects.
|
||||
"""
|
||||
pos = head.first.read_()
|
||||
while pos:
|
||||
@ -116,10 +128,12 @@ def hlist_for_each(head):
|
||||
|
||||
def hlist_for_each_entry(type, head, member):
|
||||
"""
|
||||
hlist_for_each_entry(type, struct hlist_head *, member)
|
||||
.. c:function:: hlist_for_each_entry(type, struct hlist_head *head, member)
|
||||
|
||||
Return an iterator over all of the entries in a has list, given the type of
|
||||
the entry and the struct hlist_node member in that type.
|
||||
Iterate over all of the entries in a has list, given the type of the entry
|
||||
and the ``struct hlist_node`` member in that type.
|
||||
|
||||
:return: Iterator of ``type *`` objects.
|
||||
"""
|
||||
for pos in hlist_for_each(head):
|
||||
yield container_of(pos, type, member)
|
||||
|
@ -1,14 +1,15 @@
|
||||
# Copyright 2018 - Omar Sandoval
|
||||
# Copyright 2018-2019 - Omar Sandoval
|
||||
# SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
"""
|
||||
Linux kernel memory management helpers
|
||||
Memory Management
|
||||
-----------------
|
||||
|
||||
This module provides helpers for working with the Linux memory management (mm)
|
||||
subsystem. Only x86-64 support is currently implemented.
|
||||
The ``drgn.helpers.linux.mm`` provides helpers for working with the Linux
|
||||
memory management (MM) subsystem. Only x86-64 support is currently implemented.
|
||||
"""
|
||||
|
||||
from drgn import cast, Object
|
||||
from drgn import Object, cast
|
||||
|
||||
|
||||
__all__ = [
|
||||
@ -42,9 +43,9 @@ def _page_offset(prog):
|
||||
|
||||
def for_each_page(prog):
|
||||
"""
|
||||
for_each_page()
|
||||
Iterate over all pages in the system.
|
||||
|
||||
Return an iterator over each struct page * in the system.
|
||||
:return: Iterator of ``struct page *`` objects.
|
||||
"""
|
||||
vmemmap = _vmemmap(prog)
|
||||
for i in range(prog['max_pfn']):
|
||||
@ -53,7 +54,7 @@ def for_each_page(prog):
|
||||
|
||||
def page_to_pfn(page):
|
||||
"""
|
||||
unsigned long page_to_pfn(struct page *)
|
||||
.. c:function:: unsigned long page_to_pfn(struct page *page)
|
||||
|
||||
Get the page frame number (PFN) of a page.
|
||||
"""
|
||||
@ -62,10 +63,10 @@ def page_to_pfn(page):
|
||||
|
||||
def pfn_to_page(prog_or_pfn, pfn=None):
|
||||
"""
|
||||
struct page *pfn_to_page(unsigned long)
|
||||
.. c:function:: struct page *pfn_to_page(unsigned long pfn)
|
||||
|
||||
Get the page with the given page frame number (PFN). This can take the PFN
|
||||
as an Object or a Program and the PFN as an int.
|
||||
as an :class:`Object`, or a :class:`Program` and the PFN as an ``int``.
|
||||
"""
|
||||
if pfn is None:
|
||||
prog = prog_or_pfn.prog_
|
||||
@ -77,26 +78,28 @@ def pfn_to_page(prog_or_pfn, pfn=None):
|
||||
|
||||
def virt_to_pfn(prog_or_addr, addr=None):
|
||||
"""
|
||||
unsigned long virt_to_pfn(void *)
|
||||
.. c:function:: unsigned long virt_to_pfn(void *addr)
|
||||
|
||||
Get the page frame number (PFN) of a directly mapped virtual address. This
|
||||
can take the address as an Object or a Program and the address as an int.
|
||||
can take the address as an :class:`Object`, or a :class:`Program` and the
|
||||
address as an ``int``.
|
||||
"""
|
||||
if addr is None:
|
||||
prog = prog_or_addr.prog_
|
||||
addr = prog_or_addr.value_()
|
||||
else:
|
||||
prog = prog_or_addr
|
||||
return Object(prog, 'unsigned long', value=(addr - _page_offset(prog)) >> 12)
|
||||
return Object(prog, 'unsigned long',
|
||||
value=(addr - _page_offset(prog)) >> 12)
|
||||
|
||||
|
||||
def pfn_to_virt(prog_or_pfn, pfn=None):
|
||||
"""
|
||||
void *pfn_to_virt(unsigned long)
|
||||
.. c:function:: void *pfn_to_virt(unsigned long pfn)
|
||||
|
||||
Get the directly mapped virtual address of the given page frame number
|
||||
(PFN). This can take the PFN as an Object or a Program and the PFN as an
|
||||
int.
|
||||
(PFN). This can take the PFN as an :class:`Object`, or a :class:`Program`
|
||||
and the PFN as an ``int``.
|
||||
"""
|
||||
if pfn is None:
|
||||
prog = prog_or_pfn.prog_
|
||||
@ -108,7 +111,7 @@ def pfn_to_virt(prog_or_pfn, pfn=None):
|
||||
|
||||
def page_to_virt(page):
|
||||
"""
|
||||
void *page_to_virt(struct page *)
|
||||
.. c:function:: void *page_to_virt(struct page *page)
|
||||
|
||||
Get the directly mapped virtual address of a page.
|
||||
"""
|
||||
@ -117,9 +120,10 @@ def page_to_virt(page):
|
||||
|
||||
def virt_to_page(prog_or_addr, addr=None):
|
||||
"""
|
||||
struct page *virt_to_page(void *)
|
||||
.. c:function:: struct page *virt_to_page(void *addr)
|
||||
|
||||
Get the page containing a directly mapped virtual address. This can take
|
||||
the address as an Object or a Program and the address as an int.
|
||||
the address as an :class:`Object`, or a :class:`Program` and the address as
|
||||
an ``int``.
|
||||
"""
|
||||
return pfn_to_page(virt_to_pfn(prog_or_addr, addr))
|
||||
|
@ -1,11 +1,13 @@
|
||||
# Copyright 2018 - Omar Sandoval
|
||||
# Copyright 2018-2019 - Omar Sandoval
|
||||
# SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
"""
|
||||
Linux kernel per-CPU helpers
|
||||
Per-CPU
|
||||
-------
|
||||
|
||||
This module provides helpers for working with per-CPU allocations from
|
||||
"linux/percpu.h" and per-CPU counters from "linux/percpu_counter.h".
|
||||
The ``drgn.helpers.linux.percpu`` module provides helpers for working with
|
||||
per-CPU allocations from :linux:`include/linux/percpu.h` and per-CPU counters
|
||||
from :linux:`include/linux/percpu_counter.h`.
|
||||
"""
|
||||
|
||||
from drgn import Object
|
||||
@ -20,7 +22,7 @@ __all__ = [
|
||||
|
||||
def per_cpu_ptr(ptr, cpu):
|
||||
"""
|
||||
type *per_cpu_ptr(type __percpu *ptr, int cpu)
|
||||
.. c:function:: type *per_cpu_ptr(type __percpu *ptr, int cpu)
|
||||
|
||||
Return the per-CPU pointer for a given CPU.
|
||||
"""
|
||||
@ -30,7 +32,7 @@ def per_cpu_ptr(ptr, cpu):
|
||||
|
||||
def percpu_counter_sum(fbc):
|
||||
"""
|
||||
s64 percpu_counter_sum(struct percpu_counter *fbc)
|
||||
.. c:function:: s64 percpu_counter_sum(struct percpu_counter *fbc)
|
||||
|
||||
Return the sum of a per-CPU counter.
|
||||
"""
|
||||
|
@ -1,13 +1,15 @@
|
||||
# Copyright 2018 - Omar Sandoval
|
||||
# Copyright 2018-2019 - Omar Sandoval
|
||||
# SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
"""
|
||||
Linux kernel process ID helpers
|
||||
Process IDS
|
||||
-----------
|
||||
|
||||
This module provides helpers for looking up process IDs.
|
||||
The ``drgn.helpers.linux.pid`` module provides helpers for looking up process
|
||||
IDs and processes.
|
||||
"""
|
||||
|
||||
from drgn import cast, container_of, NULL, Program
|
||||
from drgn import NULL, Program, cast, container_of
|
||||
from drgn.helpers.linux.idr import idr_find, idr_for_each
|
||||
from drgn.helpers.linux.list import hlist_for_each_entry
|
||||
|
||||
@ -22,10 +24,11 @@ __all__ = [
|
||||
|
||||
def find_pid(prog_or_ns, nr):
|
||||
"""
|
||||
struct pid *find_pid(struct pid_namespace *, int)
|
||||
.. c:function:: struct pid *find_pid(struct pid_namespace *ns, int nr)
|
||||
|
||||
Return the struct pid for the given PID in the given namespace. If given a
|
||||
Program object instead, the initial PID namespace is used.
|
||||
Return the ``struct pid *`` for the given PID number in the given
|
||||
namespace. If given a :class:`Program` instead, the initial PID namespace
|
||||
is used.
|
||||
"""
|
||||
if isinstance(prog_or_ns, Program):
|
||||
prog = prog_or_ns
|
||||
@ -53,11 +56,12 @@ def find_pid(prog_or_ns, nr):
|
||||
|
||||
def for_each_pid(prog_or_ns):
|
||||
"""
|
||||
for_each_pid(struct pid_namespace *)
|
||||
.. c:function:: for_each_pid(struct pid_namespace *ns)
|
||||
|
||||
Return an iterator over all of the PIDs in the given namespace. If given a
|
||||
Program object instead, the initial PID namespace is used. The generated
|
||||
values are struct pid * objects.
|
||||
Iterate over all of the PIDs in the given namespace. If given a
|
||||
:class:`Program` instead, the initial PID namespace is used.
|
||||
|
||||
:return: Iterator of ``struct pid *`` objects.
|
||||
"""
|
||||
if isinstance(prog_or_ns, Program):
|
||||
prog = prog_or_ns
|
||||
@ -81,10 +85,10 @@ def for_each_pid(prog_or_ns):
|
||||
|
||||
def pid_task(pid, pid_type):
|
||||
"""
|
||||
struct task_struct *pid_task(struct pid *, enum pid_type)
|
||||
.. c:function:: struct task_struct *pid_task(struct pid *pid, enum pid_type pid_type)
|
||||
|
||||
Return the struct task_struct containing the given struct pid of the given
|
||||
type.
|
||||
Return the ``struct task_struct *`` containing the given ``struct pid *``
|
||||
of the given type.
|
||||
"""
|
||||
if not pid:
|
||||
return NULL(pid.prog_, 'struct task_struct *')
|
||||
@ -101,10 +105,10 @@ def pid_task(pid, pid_type):
|
||||
|
||||
def find_task(prog_or_ns, pid):
|
||||
"""
|
||||
struct task_struct *find_task(struct pid_namespace *, int pid)
|
||||
.. c:function:: struct task_struct *find_task(struct pid_namespace *ns, int pid)
|
||||
|
||||
Return the task with the given PID in the given namespace. If given a
|
||||
Program object instead, the initial PID namespace is used.
|
||||
:class:`Program` instead, the initial PID namespace is used.
|
||||
"""
|
||||
if isinstance(prog_or_ns, Program):
|
||||
prog = prog_or_ns
|
||||
@ -115,11 +119,12 @@ def find_task(prog_or_ns, pid):
|
||||
|
||||
def for_each_task(prog_or_ns):
|
||||
"""
|
||||
for_each_task(struct pid_namespace *)
|
||||
.. c:function:: for_each_task(struct pid_namespace *ns)
|
||||
|
||||
Return an iterator over all of the tasks visible in the given namespace. If
|
||||
given a Program object instead, the initial PID namespace is used. The
|
||||
generated values are struct task_struct * objects.
|
||||
Iterate over all of the tasks visible in the given namespace. If given a
|
||||
:class:`Program` instead, the initial PID namespace is used.
|
||||
|
||||
:return: Iterator of ``struct task_struct *`` objects.
|
||||
"""
|
||||
if isinstance(prog_or_ns, Program):
|
||||
prog = prog_or_ns
|
||||
|
@ -1,14 +1,15 @@
|
||||
# Copyright 2018 - Omar Sandoval
|
||||
# Copyright 2018-2019 - Omar Sandoval
|
||||
# SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
"""
|
||||
Linux kernel radix tree helpers
|
||||
Radix Trees
|
||||
-----------
|
||||
|
||||
This module provides helpers for working with radix trees from
|
||||
"linux/radix-tree.h".
|
||||
The ``drgn.helpers.linux.radixtree`` module provides helpers for working with
|
||||
radix trees from :linux:`include/linux/radix-tree.h`.
|
||||
"""
|
||||
|
||||
from drgn import cast, Object
|
||||
from drgn import Object, cast
|
||||
|
||||
|
||||
__all__ = [
|
||||
@ -38,10 +39,10 @@ def _radix_tree_root_node(root):
|
||||
|
||||
def radix_tree_lookup(root, index):
|
||||
"""
|
||||
void *radix_tree_lookup(struct radix_tree_root *, unsigned long index)
|
||||
.. c:function:: void *radix_tree_lookup(struct radix_tree_root *root, unsigned long index)
|
||||
|
||||
Look up the entry at a given index in a radix tree. If it is not found,
|
||||
this returns a NULL object.
|
||||
this returns a ``NULL`` object.
|
||||
"""
|
||||
node, RADIX_TREE_INTERNAL_NODE = _radix_tree_root_node(root)
|
||||
RADIX_TREE_MAP_MASK = node.slots.type_.length - 1
|
||||
@ -56,10 +57,12 @@ def radix_tree_lookup(root, index):
|
||||
|
||||
def radix_tree_for_each(root):
|
||||
"""
|
||||
radix_tree_for_each(struct radix_tree_root *)
|
||||
.. c:function:: radix_tree_for_each(struct radix_tree_root *root)
|
||||
|
||||
Return an iterator over all of the entries in a radix tree. The generated
|
||||
values are (index, entry) tuples.
|
||||
Iterate over all of the entries in a radix tree.
|
||||
|
||||
:return: Iterator of (index, ``void *``) tuples.
|
||||
:rtype: Iterator[tuple[int, Object]]
|
||||
"""
|
||||
node, RADIX_TREE_INTERNAL_NODE = _radix_tree_root_node(root)
|
||||
def aux(node, index):
|
||||
|
@ -1,14 +1,15 @@
|
||||
# Copyright 2018 - Omar Sandoval
|
||||
# Copyright 2018-2019 - Omar Sandoval
|
||||
# SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
"""
|
||||
Linux kernel red-black tree helpers
|
||||
Red-Black Trees
|
||||
---------------
|
||||
|
||||
This module provides helpers for working with red-black trees from
|
||||
"linux/rbtree.h"
|
||||
The ``drgn.helpers.linux.rbtree`` module provides helpers for working with
|
||||
red-black trees from :linux:`include/linux/rbtree.h`.
|
||||
"""
|
||||
|
||||
from drgn import container_of, Object
|
||||
from drgn import Object, container_of
|
||||
|
||||
|
||||
__all__ = [
|
||||
@ -26,7 +27,7 @@ __all__ = [
|
||||
|
||||
def RB_EMPTY_NODE(node):
|
||||
"""
|
||||
bool RB_EMPTY_NODE(struct rb_node *)
|
||||
.. c:function:: bool RB_EMPTY_NODE(struct rb_node *node)
|
||||
|
||||
Return whether a red-black tree node is empty, i.e., not inserted in a
|
||||
tree.
|
||||
@ -36,7 +37,7 @@ def RB_EMPTY_NODE(node):
|
||||
|
||||
def rb_parent(node):
|
||||
"""
|
||||
struct rb_node *rb_parent(struct rb_node *)
|
||||
.. c:function:: struct rb_node *rb_parent(struct rb_node *node)
|
||||
|
||||
Return the parent node of a red-black tree node.
|
||||
"""
|
||||
@ -46,10 +47,10 @@ def rb_parent(node):
|
||||
|
||||
def rb_first(root):
|
||||
"""
|
||||
struct rb_node *rb_first(struct rb_root *)
|
||||
.. c:function:: struct rb_node *rb_first(struct rb_root *root)
|
||||
|
||||
Return the first node (in sort order) in a red-black tree, or a NULL object
|
||||
if the tree is empty.
|
||||
Return the first node (in sort order) in a red-black tree, or a ``NULL``
|
||||
object if the tree is empty.
|
||||
"""
|
||||
node = root.rb_node.read_()
|
||||
if not node:
|
||||
@ -63,10 +64,10 @@ def rb_first(root):
|
||||
|
||||
def rb_last(root):
|
||||
"""
|
||||
struct rb_node *rb_last(struct rb_root *)
|
||||
.. c:function:: struct rb_node *rb_last(struct rb_root *root)
|
||||
|
||||
Return the last node (in sort order) in a red-black tree, or a NULL object
|
||||
if the tree is empty.
|
||||
Return the last node (in sort order) in a red-black tree, or a ``NULL``
|
||||
object if the tree is empty.
|
||||
"""
|
||||
node = root.rb_node.read_()
|
||||
if not node:
|
||||
@ -80,9 +81,9 @@ def rb_last(root):
|
||||
|
||||
def rb_next(node):
|
||||
"""
|
||||
struct rb_node *rb_next(struct rb_node *)
|
||||
.. c:function:: struct rb_node *rb_next(struct rb_node *node)
|
||||
|
||||
Return the next node (in sort order) after a red-black node, or a NULL
|
||||
Return the next node (in sort order) after a red-black node, or a ``NULL``
|
||||
object if the node is the last node in the tree or is empty.
|
||||
"""
|
||||
node = node.read_()
|
||||
@ -108,10 +109,10 @@ def rb_next(node):
|
||||
|
||||
def rb_prev(node):
|
||||
"""
|
||||
struct rb_node *rb_prev(struct rb_node *)
|
||||
.. c:function:: struct rb_node *rb_prev(struct rb_node *node)
|
||||
|
||||
Return the previous node (in sort order) before a red-black node, or a NULL
|
||||
object if the node is the first node in the tree or is empty.
|
||||
Return the previous node (in sort order) before a red-black node, or a
|
||||
``NULL`` object if the node is the first node in the tree or is empty.
|
||||
"""
|
||||
node = node.read_()
|
||||
|
||||
@ -136,10 +137,11 @@ def rb_prev(node):
|
||||
|
||||
def rbtree_inorder_for_each(root):
|
||||
"""
|
||||
rbtree_inorder_for_each(struct rb_root *)
|
||||
.. c:function:: rbtree_inorder_for_each(struct rb_root *root)
|
||||
|
||||
Return an iterator over all of the nodes in a red-black tree, in sort
|
||||
order.
|
||||
Iterate over all of the nodes in a red-black tree, in sort order.
|
||||
|
||||
:return: Iterator of ``struct rb_node *`` objects.
|
||||
"""
|
||||
def aux(node):
|
||||
if node:
|
||||
@ -151,11 +153,13 @@ def rbtree_inorder_for_each(root):
|
||||
|
||||
def rbtree_inorder_for_each_entry(type, root, member):
|
||||
"""
|
||||
rbtree_inorder_for_each_entry(type, struct rb_root *, member)
|
||||
.. c:function:: rbtree_inorder_for_each_entry(type, struct rb_root *root, member)
|
||||
|
||||
Return an iterator over all of the entries in a red-black tree, given the
|
||||
type of the entry and the struct list_head member in that type. The entries
|
||||
are returned in sort order.
|
||||
Iterate over all of the entries in a red-black tree, given the type of the
|
||||
entry and the ``struct rb_node`` member in that type. The entries are
|
||||
returned in sort order.
|
||||
|
||||
:return: Iterator of ``type *`` objects.
|
||||
"""
|
||||
for node in rbtree_inorder_for_each(root):
|
||||
yield container_of(node, type, member)
|
||||
@ -163,13 +167,13 @@ def rbtree_inorder_for_each_entry(type, root, member):
|
||||
|
||||
def rb_find(type, root, member, key, cmp):
|
||||
"""
|
||||
type *rb_find(type, struct rb_root *, member,
|
||||
key_type key, int (*cmp)(key_type, type *))
|
||||
.. c:function:: type *rb_find(type, struct rb_root *root, member, key_type key, int (*cmp)(key_type, type *))
|
||||
|
||||
Find an entry in a red-black tree, given a key and a comparator function
|
||||
which takes the key and an entry. The comparator should return -1 if the
|
||||
key is less than the entry, 1 if it is greater than the entry, or 0 if it
|
||||
matches the entry. This returns a NULL object if no entry matches the key.
|
||||
which takes the key and an entry. The comparator should return < 0 if the
|
||||
key is less than the entry, > 0 if it is greater than the entry, or 0 if it
|
||||
matches the entry. This returns a ``NULL`` object if no entry matches the
|
||||
key.
|
||||
|
||||
Note that this function does not have an analogue in the Linux kernel
|
||||
source code, as tree searches are all open-coded.
|
||||
|
1
libdrgn/.gitignore
vendored
1
libdrgn/.gitignore
vendored
@ -14,3 +14,4 @@
|
||||
/html
|
||||
/libtool
|
||||
/python/constants.c
|
||||
/python/docstrings.c
|
||||
|
@ -55,7 +55,10 @@ if WITH_PYTHON
|
||||
noinst_LTLIBRARIES += _drgn.la
|
||||
endif
|
||||
|
||||
_drgn_la_SOURCES = python/drgnpy.h \
|
||||
CLEANFILES = python/constants.c python/docstrings.c
|
||||
|
||||
_drgn_la_SOURCES = python/docstrings.h \
|
||||
python/drgnpy.h \
|
||||
python/module.c \
|
||||
python/object.c \
|
||||
python/program.c \
|
||||
@ -63,17 +66,23 @@ _drgn_la_SOURCES = python/drgnpy.h \
|
||||
python/type.c \
|
||||
python/util.c
|
||||
|
||||
nodist__drgn_la_SOURCES = python/constants.c
|
||||
nodist__drgn_la_SOURCES = python/constants.c python/docstrings.c
|
||||
|
||||
_drgn_la_CFLAGS = $(PYTHON_CFLAGS) -D_GNU_SOURCE -fvisibility=hidden
|
||||
_drgn_la_LDFLAGS = -avoid-version -module -shared -rpath $(pkgpyexecdir) \
|
||||
-Wl,--exclude-libs,ALL
|
||||
_drgn_la_LIBADD = libdrgnimpl.la
|
||||
|
||||
$(top_builddir)/python/constants.c: $(top_srcdir)/drgn.h $(top_srcdir)/build-aux/gen_constants.py
|
||||
$(PYTHON) $(top_srcdir)/build-aux/gen_constants.py $(top_srcdir)/python < $< > $@
|
||||
GEN_CONSTANTS = $(top_srcdir)/build-aux/gen_constants.py
|
||||
GEN_DOCSTRINGS = $(top_srcdir)/build-aux/gen_docstrings.py
|
||||
|
||||
EXTRA_DIST = $(top_srcdir)/build-aux/gen_constants.py
|
||||
$(top_builddir)/python/constants.c: $(top_srcdir)/drgn.h $(GEN_CONSTANTS)
|
||||
$(PYTHON) $(GEN_CONSTANTS) $(top_srcdir)/python < $< > $@
|
||||
|
||||
$(top_builddir)/python/docstrings.c: $(top_srcdir)/../docs/api_reference.rst $(GEN_DOCSTRINGS)
|
||||
$(PYTHON) $(GEN_DOCSTRINGS) < $< > $@
|
||||
|
||||
EXTRA_DIST = $(GEN_CONSTANTS) $(GEN_DOCSTRINGS)
|
||||
|
||||
# This target lists all of the files that need to be distributed for setup.py
|
||||
# sdist. It is based on the automake distdir target.
|
||||
|
1
libdrgn/build-aux/.gitignore
vendored
1
libdrgn/build-aux/.gitignore
vendored
@ -1,4 +1,5 @@
|
||||
*
|
||||
!/.gitignore
|
||||
!/gen_constants.py
|
||||
!/gen_docstrings.py
|
||||
!/version.sh
|
||||
|
@ -1,3 +1,6 @@
|
||||
# Copyright 2018-2019 - Omar Sandoval
|
||||
# SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
import os.path
|
||||
import re
|
||||
import sys
|
||||
|
182
libdrgn/build-aux/gen_docstrings.py
Normal file
182
libdrgn/build-aux/gen_docstrings.py
Normal file
@ -0,0 +1,182 @@
|
||||
# Copyright 2018-2019 - Omar Sandoval
|
||||
# SPDX-License-Identifier: GPL-3.0+
|
||||
|
||||
import re
|
||||
import sys
|
||||
from types import SimpleNamespace
|
||||
|
||||
|
||||
def strictstartswith(a, b):
|
||||
return a.startswith(b) and a != b
|
||||
|
||||
|
||||
# Quick and dirty reStructuredText parser. It probably can't handle anything
|
||||
# other than the input in this repository.
|
||||
def parse_rst(input_file):
|
||||
stack = [
|
||||
SimpleNamespace(name='', state='CONTENT', lines=None,
|
||||
directive_indentation='', content_indentation='')
|
||||
]
|
||||
state = None
|
||||
for line in input_file:
|
||||
line = line.rstrip()
|
||||
indentation = re.match(r'\s*', line).group()
|
||||
while True:
|
||||
top = stack[-1]
|
||||
if top.state == 'DIRECTIVE':
|
||||
if not line:
|
||||
top.state = 'BLANK_LINE'
|
||||
break
|
||||
elif strictstartswith(indentation, top.directive_indentation):
|
||||
top.content_indentation = indentation
|
||||
top.state = 'OPTIONS'
|
||||
break
|
||||
elif top.state == 'BLANK_LINE':
|
||||
if not line:
|
||||
break
|
||||
elif strictstartswith(indentation, top.directive_indentation):
|
||||
top.content_indentation = indentation
|
||||
top.state = 'CONTENT'
|
||||
break
|
||||
elif top.state == 'OPTIONS':
|
||||
if not line:
|
||||
top.state = 'OPTIONS_BLANK_LINE'
|
||||
break
|
||||
elif indentation.startswith(top.content_indentation):
|
||||
break
|
||||
else:
|
||||
if top.state == 'OPTIONS_BLANK_LINE':
|
||||
top.state = 'CONTENT'
|
||||
assert top.state == 'CONTENT'
|
||||
if (not line or
|
||||
indentation.startswith(top.content_indentation)):
|
||||
break
|
||||
# The current line is indented less than the current indentation,
|
||||
# so pop the top directive.
|
||||
if top.lines is not None:
|
||||
yield top
|
||||
del stack[-1]
|
||||
|
||||
assert top is stack[-1]
|
||||
if top.state != 'CONTENT':
|
||||
continue
|
||||
|
||||
if line:
|
||||
assert line.startswith(top.content_indentation)
|
||||
line = line[len(top.content_indentation):]
|
||||
match = re.match(r'\s*..\s*(?:py:)?([-a-zA-Z0-9_+:.]+)::\s*(.*)', line)
|
||||
if match:
|
||||
directive = match.group(1)
|
||||
argument = match.group(2)
|
||||
if directive == 'module' or directive == 'currentmodule':
|
||||
stack[0].name = argument
|
||||
else:
|
||||
name = top.name
|
||||
if directive in {'attribute', 'class', 'exception', 'function',
|
||||
'method'}:
|
||||
lines = []
|
||||
paren = argument.find('(')
|
||||
if paren != -1:
|
||||
# If the argument includes a signature, add it along
|
||||
# with the signature end marker used by CPython.
|
||||
lines.append(argument)
|
||||
lines.append('--')
|
||||
lines.append('')
|
||||
argument = argument[:paren]
|
||||
if name:
|
||||
name += '.'
|
||||
name += argument
|
||||
else:
|
||||
lines = None
|
||||
entry = SimpleNamespace(name=name, state='DIRECTIVE',
|
||||
lines=lines,
|
||||
directive_indentation=indentation,
|
||||
content_indentation=None)
|
||||
stack.append(entry)
|
||||
elif top.lines is not None:
|
||||
top.lines.append(line)
|
||||
|
||||
while len(stack) > 1:
|
||||
entry = stack.pop()
|
||||
if entry.lines is not None:
|
||||
yield entry
|
||||
|
||||
|
||||
escapes = []
|
||||
for c in range(256):
|
||||
if c == 0:
|
||||
e = r'\0'
|
||||
elif c == 7:
|
||||
e = r'\a'
|
||||
elif c == 8:
|
||||
e = r'\b'
|
||||
elif c == 9:
|
||||
e = r'\t'
|
||||
elif c == 10:
|
||||
e = r'\n'
|
||||
elif c == 11:
|
||||
e = r'\v'
|
||||
elif c == 12:
|
||||
e = r'\f'
|
||||
elif c == 13:
|
||||
e = r'\r'
|
||||
elif c == 34:
|
||||
e = r'\"'
|
||||
elif c == 92:
|
||||
e = r'\\'
|
||||
elif 32 <= c <= 126:
|
||||
e = chr(c)
|
||||
else:
|
||||
e = f'\\x{c:02x}'
|
||||
escapes.append(e)
|
||||
|
||||
|
||||
def escape_string(s):
|
||||
return ''.join([escapes[c] for c in s.encode('utf-8')])
|
||||
|
||||
|
||||
def gen_docstrings(input_file, output_file, header=False):
|
||||
path = 'libdrgn/build-aux/gen_docstrings.py'
|
||||
if header:
|
||||
output_file.write(f"""\
|
||||
/*
|
||||
* Generated by {path} -H.
|
||||
*
|
||||
* Note that this is generated manually because automake and other build systems
|
||||
* have trouble with generated headers. Regenerate this if a new docstring is
|
||||
* added. The docstring contents themselves are automatically regenerated by the
|
||||
* build system.
|
||||
*
|
||||
* Before Python 3.7, various docstring fields were defined as char * (see
|
||||
* https://bugs.python.org/issue28761). We still want the strings to be
|
||||
* read-only, so just cast away the const.
|
||||
*/
|
||||
|
||||
""")
|
||||
else:
|
||||
output_file.write(f'/* Generated by {path}. */\n\n')
|
||||
directives = sorted(parse_rst(input_file), key=lambda x: x.name)
|
||||
for directive in directives:
|
||||
while directive.lines and not directive.lines[-1]:
|
||||
del directive.lines[-1]
|
||||
name = directive.name.replace('.', '_') + '_DOC'
|
||||
if header:
|
||||
output_file.write('extern ')
|
||||
output_file.write(f"const char {name}[]")
|
||||
if not header:
|
||||
output_file.write(' =')
|
||||
if directive.lines:
|
||||
for i, line in enumerate(directive.lines):
|
||||
output_file.write(f'\n\t"{escape_string(line)}')
|
||||
if i != len(directive.lines) - 1:
|
||||
output_file.write('\\n')
|
||||
output_file.write('"')
|
||||
else:
|
||||
output_file.write(' ""')
|
||||
output_file.write(';\n')
|
||||
if header:
|
||||
output_file.write(f'#define {name} (char *){name}\n')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
gen_docstrings(sys.stdin, sys.stdout, '-H' in sys.argv[1:])
|
179
libdrgn/python/docstrings.h
Normal file
179
libdrgn/python/docstrings.h
Normal file
@ -0,0 +1,179 @@
|
||||
/*
|
||||
* Generated by libdrgn/build-aux/gen_docstrings.py -H.
|
||||
*
|
||||
* Note that this is generated manually because automake and other build systems
|
||||
* have trouble with generated headers. Regenerate this if a new docstring is
|
||||
* added. The docstring contents themselves are automatically regenerated by the
|
||||
* build system.
|
||||
*
|
||||
* Before Python 3.7, various docstring fields were defined as char * (see
|
||||
* https://bugs.python.org/issue28761). We still want the strings to be
|
||||
* read-only, so just cast away the const.
|
||||
*/
|
||||
|
||||
extern const char drgn_FaultError_DOC[];
|
||||
#define drgn_FaultError_DOC (char *)drgn_FaultError_DOC
|
||||
extern const char drgn_FileFormatError_DOC[];
|
||||
#define drgn_FileFormatError_DOC (char *)drgn_FileFormatError_DOC
|
||||
extern const char drgn_NULL_DOC[];
|
||||
#define drgn_NULL_DOC (char *)drgn_NULL_DOC
|
||||
extern const char drgn_Object_DOC[];
|
||||
#define drgn_Object_DOC (char *)drgn_Object_DOC
|
||||
extern const char drgn_Object___getattribute___DOC[];
|
||||
#define drgn_Object___getattribute___DOC (char *)drgn_Object___getattribute___DOC
|
||||
extern const char drgn_Object___getitem___DOC[];
|
||||
#define drgn_Object___getitem___DOC (char *)drgn_Object___getitem___DOC
|
||||
extern const char drgn_Object___len___DOC[];
|
||||
#define drgn_Object___len___DOC (char *)drgn_Object___len___DOC
|
||||
extern const char drgn_Object_address__DOC[];
|
||||
#define drgn_Object_address__DOC (char *)drgn_Object_address__DOC
|
||||
extern const char drgn_Object_address_of__DOC[];
|
||||
#define drgn_Object_address_of__DOC (char *)drgn_Object_address_of__DOC
|
||||
extern const char drgn_Object_bit_field_size__DOC[];
|
||||
#define drgn_Object_bit_field_size__DOC (char *)drgn_Object_bit_field_size__DOC
|
||||
extern const char drgn_Object_bit_offset__DOC[];
|
||||
#define drgn_Object_bit_offset__DOC (char *)drgn_Object_bit_offset__DOC
|
||||
extern const char drgn_Object_byteorder__DOC[];
|
||||
#define drgn_Object_byteorder__DOC (char *)drgn_Object_byteorder__DOC
|
||||
extern const char drgn_Object_member__DOC[];
|
||||
#define drgn_Object_member__DOC (char *)drgn_Object_member__DOC
|
||||
extern const char drgn_Object_prog__DOC[];
|
||||
#define drgn_Object_prog__DOC (char *)drgn_Object_prog__DOC
|
||||
extern const char drgn_Object_read__DOC[];
|
||||
#define drgn_Object_read__DOC (char *)drgn_Object_read__DOC
|
||||
extern const char drgn_Object_string__DOC[];
|
||||
#define drgn_Object_string__DOC (char *)drgn_Object_string__DOC
|
||||
extern const char drgn_Object_type__DOC[];
|
||||
#define drgn_Object_type__DOC (char *)drgn_Object_type__DOC
|
||||
extern const char drgn_Object_value__DOC[];
|
||||
#define drgn_Object_value__DOC (char *)drgn_Object_value__DOC
|
||||
extern const char drgn_Program_DOC[];
|
||||
#define drgn_Program_DOC (char *)drgn_Program_DOC
|
||||
extern const char drgn_Program___getitem___DOC[];
|
||||
#define drgn_Program___getitem___DOC (char *)drgn_Program___getitem___DOC
|
||||
extern const char drgn_Program_byteorder_DOC[];
|
||||
#define drgn_Program_byteorder_DOC (char *)drgn_Program_byteorder_DOC
|
||||
extern const char drgn_Program_constant_DOC[];
|
||||
#define drgn_Program_constant_DOC (char *)drgn_Program_constant_DOC
|
||||
extern const char drgn_Program_flags_DOC[];
|
||||
#define drgn_Program_flags_DOC (char *)drgn_Program_flags_DOC
|
||||
extern const char drgn_Program_function_DOC[];
|
||||
#define drgn_Program_function_DOC (char *)drgn_Program_function_DOC
|
||||
extern const char drgn_Program_read_DOC[];
|
||||
#define drgn_Program_read_DOC (char *)drgn_Program_read_DOC
|
||||
extern const char drgn_Program_type_DOC[];
|
||||
#define drgn_Program_type_DOC (char *)drgn_Program_type_DOC
|
||||
extern const char drgn_Program_variable_DOC[];
|
||||
#define drgn_Program_variable_DOC (char *)drgn_Program_variable_DOC
|
||||
extern const char drgn_Program_word_size_DOC[];
|
||||
#define drgn_Program_word_size_DOC (char *)drgn_Program_word_size_DOC
|
||||
extern const char drgn_ProgramFlags_DOC[];
|
||||
#define drgn_ProgramFlags_DOC (char *)drgn_ProgramFlags_DOC
|
||||
extern const char drgn_ProgramFlags_IS_LINUX_KERNEL_DOC[];
|
||||
#define drgn_ProgramFlags_IS_LINUX_KERNEL_DOC (char *)drgn_ProgramFlags_IS_LINUX_KERNEL_DOC
|
||||
extern const char drgn_Qualifiers_DOC[];
|
||||
#define drgn_Qualifiers_DOC (char *)drgn_Qualifiers_DOC
|
||||
extern const char drgn_Qualifiers_ATOMIC_DOC[];
|
||||
#define drgn_Qualifiers_ATOMIC_DOC (char *)drgn_Qualifiers_ATOMIC_DOC
|
||||
extern const char drgn_Qualifiers_CONST_DOC[];
|
||||
#define drgn_Qualifiers_CONST_DOC (char *)drgn_Qualifiers_CONST_DOC
|
||||
extern const char drgn_Qualifiers_RESTRICT_DOC[];
|
||||
#define drgn_Qualifiers_RESTRICT_DOC (char *)drgn_Qualifiers_RESTRICT_DOC
|
||||
extern const char drgn_Qualifiers_VOLATILE_DOC[];
|
||||
#define drgn_Qualifiers_VOLATILE_DOC (char *)drgn_Qualifiers_VOLATILE_DOC
|
||||
extern const char drgn_Type_DOC[];
|
||||
#define drgn_Type_DOC (char *)drgn_Type_DOC
|
||||
extern const char drgn_Type_enumerators_DOC[];
|
||||
#define drgn_Type_enumerators_DOC (char *)drgn_Type_enumerators_DOC
|
||||
extern const char drgn_Type_is_complete_DOC[];
|
||||
#define drgn_Type_is_complete_DOC (char *)drgn_Type_is_complete_DOC
|
||||
extern const char drgn_Type_is_signed_DOC[];
|
||||
#define drgn_Type_is_signed_DOC (char *)drgn_Type_is_signed_DOC
|
||||
extern const char drgn_Type_is_variadic_DOC[];
|
||||
#define drgn_Type_is_variadic_DOC (char *)drgn_Type_is_variadic_DOC
|
||||
extern const char drgn_Type_kind_DOC[];
|
||||
#define drgn_Type_kind_DOC (char *)drgn_Type_kind_DOC
|
||||
extern const char drgn_Type_length_DOC[];
|
||||
#define drgn_Type_length_DOC (char *)drgn_Type_length_DOC
|
||||
extern const char drgn_Type_members_DOC[];
|
||||
#define drgn_Type_members_DOC (char *)drgn_Type_members_DOC
|
||||
extern const char drgn_Type_name_DOC[];
|
||||
#define drgn_Type_name_DOC (char *)drgn_Type_name_DOC
|
||||
extern const char drgn_Type_parameters_DOC[];
|
||||
#define drgn_Type_parameters_DOC (char *)drgn_Type_parameters_DOC
|
||||
extern const char drgn_Type_qualified_DOC[];
|
||||
#define drgn_Type_qualified_DOC (char *)drgn_Type_qualified_DOC
|
||||
extern const char drgn_Type_qualifiers_DOC[];
|
||||
#define drgn_Type_qualifiers_DOC (char *)drgn_Type_qualifiers_DOC
|
||||
extern const char drgn_Type_size_DOC[];
|
||||
#define drgn_Type_size_DOC (char *)drgn_Type_size_DOC
|
||||
extern const char drgn_Type_tag_DOC[];
|
||||
#define drgn_Type_tag_DOC (char *)drgn_Type_tag_DOC
|
||||
extern const char drgn_Type_type_DOC[];
|
||||
#define drgn_Type_type_DOC (char *)drgn_Type_type_DOC
|
||||
extern const char drgn_Type_type_name_DOC[];
|
||||
#define drgn_Type_type_name_DOC (char *)drgn_Type_type_name_DOC
|
||||
extern const char drgn_Type_unqualified_DOC[];
|
||||
#define drgn_Type_unqualified_DOC (char *)drgn_Type_unqualified_DOC
|
||||
extern const char drgn_TypeKind_DOC[];
|
||||
#define drgn_TypeKind_DOC (char *)drgn_TypeKind_DOC
|
||||
extern const char drgn_TypeKind_ARRAY_DOC[];
|
||||
#define drgn_TypeKind_ARRAY_DOC (char *)drgn_TypeKind_ARRAY_DOC
|
||||
extern const char drgn_TypeKind_BOOL_DOC[];
|
||||
#define drgn_TypeKind_BOOL_DOC (char *)drgn_TypeKind_BOOL_DOC
|
||||
extern const char drgn_TypeKind_COMPLEX_DOC[];
|
||||
#define drgn_TypeKind_COMPLEX_DOC (char *)drgn_TypeKind_COMPLEX_DOC
|
||||
extern const char drgn_TypeKind_ENUM_DOC[];
|
||||
#define drgn_TypeKind_ENUM_DOC (char *)drgn_TypeKind_ENUM_DOC
|
||||
extern const char drgn_TypeKind_FLOAT_DOC[];
|
||||
#define drgn_TypeKind_FLOAT_DOC (char *)drgn_TypeKind_FLOAT_DOC
|
||||
extern const char drgn_TypeKind_FUNCTION_DOC[];
|
||||
#define drgn_TypeKind_FUNCTION_DOC (char *)drgn_TypeKind_FUNCTION_DOC
|
||||
extern const char drgn_TypeKind_INT_DOC[];
|
||||
#define drgn_TypeKind_INT_DOC (char *)drgn_TypeKind_INT_DOC
|
||||
extern const char drgn_TypeKind_POINTER_DOC[];
|
||||
#define drgn_TypeKind_POINTER_DOC (char *)drgn_TypeKind_POINTER_DOC
|
||||
extern const char drgn_TypeKind_STRUCT_DOC[];
|
||||
#define drgn_TypeKind_STRUCT_DOC (char *)drgn_TypeKind_STRUCT_DOC
|
||||
extern const char drgn_TypeKind_TYPEDEF_DOC[];
|
||||
#define drgn_TypeKind_TYPEDEF_DOC (char *)drgn_TypeKind_TYPEDEF_DOC
|
||||
extern const char drgn_TypeKind_UNION_DOC[];
|
||||
#define drgn_TypeKind_UNION_DOC (char *)drgn_TypeKind_UNION_DOC
|
||||
extern const char drgn_TypeKind_VOID_DOC[];
|
||||
#define drgn_TypeKind_VOID_DOC (char *)drgn_TypeKind_VOID_DOC
|
||||
extern const char drgn_array_type_DOC[];
|
||||
#define drgn_array_type_DOC (char *)drgn_array_type_DOC
|
||||
extern const char drgn_bool_type_DOC[];
|
||||
#define drgn_bool_type_DOC (char *)drgn_bool_type_DOC
|
||||
extern const char drgn_cast_DOC[];
|
||||
#define drgn_cast_DOC (char *)drgn_cast_DOC
|
||||
extern const char drgn_complex_type_DOC[];
|
||||
#define drgn_complex_type_DOC (char *)drgn_complex_type_DOC
|
||||
extern const char drgn_container_of_DOC[];
|
||||
#define drgn_container_of_DOC (char *)drgn_container_of_DOC
|
||||
extern const char drgn_enum_type_DOC[];
|
||||
#define drgn_enum_type_DOC (char *)drgn_enum_type_DOC
|
||||
extern const char drgn_float_type_DOC[];
|
||||
#define drgn_float_type_DOC (char *)drgn_float_type_DOC
|
||||
extern const char drgn_function_type_DOC[];
|
||||
#define drgn_function_type_DOC (char *)drgn_function_type_DOC
|
||||
extern const char drgn_int_type_DOC[];
|
||||
#define drgn_int_type_DOC (char *)drgn_int_type_DOC
|
||||
extern const char drgn_pointer_type_DOC[];
|
||||
#define drgn_pointer_type_DOC (char *)drgn_pointer_type_DOC
|
||||
extern const char drgn_program_from_core_dump_DOC[];
|
||||
#define drgn_program_from_core_dump_DOC (char *)drgn_program_from_core_dump_DOC
|
||||
extern const char drgn_program_from_kernel_DOC[];
|
||||
#define drgn_program_from_kernel_DOC (char *)drgn_program_from_kernel_DOC
|
||||
extern const char drgn_program_from_pid_DOC[];
|
||||
#define drgn_program_from_pid_DOC (char *)drgn_program_from_pid_DOC
|
||||
extern const char drgn_reinterpret_DOC[];
|
||||
#define drgn_reinterpret_DOC (char *)drgn_reinterpret_DOC
|
||||
extern const char drgn_struct_type_DOC[];
|
||||
#define drgn_struct_type_DOC (char *)drgn_struct_type_DOC
|
||||
extern const char drgn_typedef_type_DOC[];
|
||||
#define drgn_typedef_type_DOC (char *)drgn_typedef_type_DOC
|
||||
extern const char drgn_union_type_DOC[];
|
||||
#define drgn_union_type_DOC (char *)drgn_union_type_DOC
|
||||
extern const char drgn_void_type_DOC[];
|
||||
#define drgn_void_type_DOC (char *)drgn_void_type_DOC
|
@ -9,6 +9,7 @@
|
||||
#include <Python.h>
|
||||
#include "structmember.h"
|
||||
|
||||
#include "docstrings.h"
|
||||
#include "../drgn.h"
|
||||
#include "../program.h"
|
||||
|
||||
@ -117,6 +118,7 @@ static inline DrgnObject *DrgnObject_alloc(Program *prog)
|
||||
|
||||
int Program_hold_type(Program *prog, DrgnType *type);
|
||||
|
||||
PyObject *DrgnObject_NULL(PyObject *self, PyObject *args, PyObject *kwds);
|
||||
DrgnObject *cast(PyObject *self, PyObject *args, PyObject *kwds);
|
||||
DrgnObject *reinterpret(PyObject *self, PyObject *args, PyObject *kwds);
|
||||
DrgnObject *DrgnObject_container_of(PyObject *self, PyObject *args,
|
||||
|
@ -58,163 +58,61 @@ DRGNPY_PUBLIC PyObject *set_drgn_error(struct drgn_error *err)
|
||||
}
|
||||
|
||||
static PyMethodDef drgn_methods[] = {
|
||||
{"NULL", (PyCFunction)DrgnObject_NULL, METH_VARARGS | METH_KEYWORDS,
|
||||
drgn_NULL_DOC},
|
||||
{"cast", (PyCFunction)cast, METH_VARARGS | METH_KEYWORDS,
|
||||
"cast(type: Union[str, Type], obj: Object) -> Object\n"
|
||||
"\n"
|
||||
"Return the value of the given object casted to another type.\n"
|
||||
"\n"
|
||||
"Objects with a scalar type (integer, boolean, enumerated,\n"
|
||||
"floating-point, or pointer) can be casted to a different scalar type.\n"
|
||||
"Other objects can only be casted to the same type. This always results\n"
|
||||
"in a value object. See also reinterpret()."},
|
||||
drgn_cast_DOC},
|
||||
{"reinterpret", (PyCFunction)reinterpret, METH_VARARGS | METH_KEYWORDS,
|
||||
"reinterpret(type: Union[str, Type], obj: Object,\n"
|
||||
" byteorder: Optional[str] = None) -> Object\n"
|
||||
"\n"
|
||||
"Return a copy of the given object reinterpreted as another type and/or\n"
|
||||
"byte order. If byte order is None, it defaults to the program byte\n"
|
||||
"order.\n"
|
||||
"\n"
|
||||
"This reinterprets the raw memory of the object, so an object can be\n"
|
||||
"reinterpreted as any other type. However, value objects with a scalar\n"
|
||||
"type cannot be reinterpreted, as their memory layout in the program is\n"
|
||||
"not known. Reinterpreting a reference results in a reference, and\n"
|
||||
"reinterpreting a value results in a value. See also cast()."},
|
||||
drgn_reinterpret_DOC},
|
||||
{"container_of", (PyCFunction)DrgnObject_container_of,
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
"container_of(ptr: Object, type: Union[str, Type], member: str) -> Object\n"
|
||||
"\n"
|
||||
"Return the containing object of the object pointed to by the given\n"
|
||||
"pointer object. The given type is the type of the containing object, and\n"
|
||||
"the given member is the name of the member in that type. This\n"
|
||||
"corresponds to the container_of() macro in C."},
|
||||
METH_VARARGS | METH_KEYWORDS, drgn_container_of_DOC},
|
||||
{"mock_program", (PyCFunction)mock_program,
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
"mock_program(word_size: int, byteorder: str,\n"
|
||||
" segments: Optional[Sequence[MockMemorySegment]] = None,\n"
|
||||
" types: Optional[Sequence[MockType]] = None,\n"
|
||||
" objects: Optional[Sequence[MockObject]] = None) -> Program\n"
|
||||
"mock_program(word_size, byteorder, segments=None, types=None, objects=None)\n"
|
||||
"--\n"
|
||||
"\n"
|
||||
"Return a \"mock\" Program from the given word size, byteorder, and lists\n"
|
||||
"of MockMemorySegment, MockType, and MockObject. This is usually used for\n"
|
||||
"testing."},
|
||||
"Create a mock :class:`Program` for testing.\n"
|
||||
"\n"
|
||||
":param int word_size: :attr:`Program.word_size`\n"
|
||||
":param str byteorder: :attr:`Program.byteorder`\n"
|
||||
":param segments: Memory segments.\n"
|
||||
":type segments: list[MockMemorySegment] or None\n"
|
||||
":param types: Type definitions.\n"
|
||||
":type types: list[MockType] or None\n"
|
||||
":param objects: Object definitions.\n"
|
||||
":type objects: list[MockObject] or None\n"
|
||||
":rtype: Program"},
|
||||
{"program_from_core_dump", (PyCFunction)program_from_core_dump,
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
"program_from_core_dump(path: str, verbose: bool = False) -> Program\n"
|
||||
"\n"
|
||||
"Create a Program from a core dump file. The type of program (e.g.,\n"
|
||||
"userspace or kernel) will be determined automatically.\n"
|
||||
"\n"
|
||||
"If verbose is True, this will print messages to stderr about not being\n"
|
||||
"able to find debugging symbols, etc."},
|
||||
METH_VARARGS | METH_KEYWORDS, drgn_program_from_core_dump_DOC},
|
||||
{"program_from_kernel", (PyCFunction)program_from_kernel,
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
"program_from_kernel(verbose: bool = False) -> Program\n"
|
||||
"\n"
|
||||
"Create a Program from the running operating system kernel. This requires\n"
|
||||
"root privileges.\n"
|
||||
"\n"
|
||||
"If verbose is True, this will print messages to stderr about not being\n"
|
||||
"able to find kernel modules, debugging symbols, etc."},
|
||||
METH_VARARGS | METH_KEYWORDS, drgn_program_from_kernel_DOC},
|
||||
{"program_from_pid", (PyCFunction)program_from_pid,
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
"program_from_pid(pid: int) -> Program\n"
|
||||
"\n"
|
||||
"Create a Program from a running program with the given PID. This\n"
|
||||
"requires appropriate permissions (on Linux, ptrace(2) attach\n"
|
||||
"permissions)."},
|
||||
METH_VARARGS | METH_KEYWORDS, drgn_program_from_pid_DOC},
|
||||
{"void_type", (PyCFunction)void_type, METH_VARARGS | METH_KEYWORDS,
|
||||
"void_type(qualifiers: int = 0) -> Type\n"
|
||||
"\n"
|
||||
"Return a new void type. It has kind TypeKind.VOID."},
|
||||
drgn_void_type_DOC},
|
||||
{"int_type", (PyCFunction)int_type, METH_VARARGS | METH_KEYWORDS,
|
||||
"int_type(name: str, size: int, is_signed: bool,\n"
|
||||
" qualifiers: int = 0) -> Type\n"
|
||||
"\n"
|
||||
"Return a new integer type. It has kind TypeKind.INT, a name, a size, and\n"
|
||||
"a signedness."},
|
||||
drgn_int_type_DOC},
|
||||
{"bool_type", (PyCFunction)bool_type, METH_VARARGS | METH_KEYWORDS,
|
||||
"bool_type(name: str, size: int, qualifiers: int = 0) -> Type\n"
|
||||
"\n"
|
||||
"Return a new boolean type. It has kind TypeKind.BOOL, a name, and a\n"
|
||||
"size."},
|
||||
drgn_bool_type_DOC},
|
||||
{"float_type", (PyCFunction)float_type, METH_VARARGS | METH_KEYWORDS,
|
||||
"float_type(name, size, qualifiers=0) -> new floating-point type\n"
|
||||
"\n"
|
||||
"Return a new floating-point type. It has kind TypeKind.FLOAT, a string\n"
|
||||
"name, and an integer size."},
|
||||
drgn_float_type_DOC},
|
||||
{"complex_type", (PyCFunction)complex_type,
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
"complex_type(name: str, size: int, type: Type,\n"
|
||||
" qualifiers: int = 0) -> Type\n"
|
||||
"\n"
|
||||
"Return a new complex type. It has kind TypeKind.COMPLEX, a name, a\n"
|
||||
"size, and a corresponding real type, which must be an unqualified\n"
|
||||
"floating-point or integer Type object."},
|
||||
METH_VARARGS | METH_KEYWORDS, drgn_complex_type_DOC},
|
||||
{"struct_type", (PyCFunction)struct_type, METH_VARARGS | METH_KEYWORDS,
|
||||
"struct_type(tag: Optional[str], size: int, members: Optional[Sequence],\n"
|
||||
" qualifiers: int = 0) -> Type\n"
|
||||
"\n"
|
||||
"Return a new structure type. It has kind TypeKind.STRUCT, a tag, a size,\n"
|
||||
"and a list of members. The tag may be None, which indicates an anonymous\n"
|
||||
"type. The members may be None, which indicates an incomplete type; in\n"
|
||||
"this case, the size must be zero. Otherwise, the members must be a list\n"
|
||||
"of (type, string name, integer bit offset, integer bit field size)\n"
|
||||
"tuples. The type of a member must be a Type object or a callable\n"
|
||||
"returning a Type object. In the latter case, the callable will be called\n"
|
||||
"the first time that the member is accessed. The name of a member may be\n"
|
||||
"None, which indicates an unnamed member. The bit field size should be\n"
|
||||
"non-zero for bit fields and zero otherwise. The name, bit offset, and\n"
|
||||
"bit field size can be omitted; the name defaults to None and the bit\n"
|
||||
"offset and bit field size default to zero."},
|
||||
drgn_struct_type_DOC},
|
||||
{"union_type", (PyCFunction)union_type, METH_VARARGS | METH_KEYWORDS,
|
||||
"union_type(tag: Optional[str], size: int, members: Optional[Sequence],\n"
|
||||
" qualifiers: int = 0) -> Type\n"
|
||||
"\n"
|
||||
"Return a new union type. It has kind TypeKind.UNION, a tag, a size, and\n"
|
||||
"a list of members. See struct_type()."},
|
||||
drgn_union_type_DOC},
|
||||
{"enum_type", (PyCFunction)enum_type, METH_VARARGS | METH_KEYWORDS,
|
||||
"enum_type(tag: Optional[str], type: Optional[Type],\n"
|
||||
" enumerators: Optional[Sequence[Tuple[str, int]],\n"
|
||||
" qualifiers: int = 0) -> Type\n"
|
||||
"\n"
|
||||
"Return a new enumerated type. It has kind TypeKind.ENUM, a tag, a\n"
|
||||
"compatible integer type, and a list of enumerators. The tag may be None,\n"
|
||||
"which indicates an anonymous type. The type and enumerators may be None,\n"
|
||||
"which indicates an incomplete type. Otherwise, the type must be an\n"
|
||||
"integer Type object and the enumerators must be a list of (string name,\n"
|
||||
"integer value) tuples."},
|
||||
drgn_enum_type_DOC},
|
||||
{"typedef_type", (PyCFunction)typedef_type,
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
"typedef_type(name: str, type: Type, qualifiers: int = 0) -> Type\n"
|
||||
"\n"
|
||||
"Return a new typedef type. It has kind TypeKind.TYPEDEF, a name, and an\n"
|
||||
"aliased type."},
|
||||
drgn_typedef_type_DOC},
|
||||
{"pointer_type", (PyCFunction)pointer_type,
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
"pointer_type(size: int, type: Type, qualifiers: int = 0) -> Type\n"
|
||||
"\n"
|
||||
"Return a new pointer type. It has kind TypeKind.POINTER, a size, and a\n"
|
||||
"referenced type."},
|
||||
METH_VARARGS | METH_KEYWORDS, drgn_pointer_type_DOC},
|
||||
{"array_type", (PyCFunction)array_type, METH_VARARGS | METH_KEYWORDS,
|
||||
"array_type(length: Optional[int], type: Type,\n"
|
||||
" qualifiers: int = 0) -> Type\n"
|
||||
"\n"
|
||||
"Return a new array type. It has kind TypeKind.ARRAY, a length, and an\n"
|
||||
"element type. The length may be None, which indicates an incomplete\n"
|
||||
"array type."},
|
||||
drgn_array_type_DOC},
|
||||
{"function_type", (PyCFunction)function_type,
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
"function_type(type: Type, parameters: Sequence,\n"
|
||||
" is_variadic: bool = False, qualifiers: int = 0) -> Type\n"
|
||||
"\n"
|
||||
"Return a new function type. It has kind TypeKind.FUNCTION, a return\n"
|
||||
"type, a list of parameters, and may be variadic. The parameters must be\n"
|
||||
"a list of (type, string name) tuples. Each parameter type must be a Type\n"
|
||||
"object or a callable returning a Type object. In the latter case, the\n"
|
||||
"callable will be called the first time that the parameter is accessed. A\n"
|
||||
"parameter name may be None, which indicates an unnamed parameter. The\n"
|
||||
"parameter name is optional and defaults to None."},
|
||||
METH_VARARGS | METH_KEYWORDS, drgn_function_type_DOC},
|
||||
{},
|
||||
};
|
||||
|
||||
@ -228,19 +126,6 @@ static struct PyModuleDef drgnmodule = {
|
||||
drgn_methods,
|
||||
};
|
||||
|
||||
#define FaultError_DOC \
|
||||
"Bad memory access.\n" \
|
||||
"\n" \
|
||||
"This error is raised when a memory access is attempted to an address\n" \
|
||||
"which is not valid in a program, or when accessing out of bounds of a\n" \
|
||||
"value object."
|
||||
|
||||
#define FileFormatError_DOC \
|
||||
"Invalid file.\n" \
|
||||
"\n" \
|
||||
"This is error raised when a file cannot be parsed according to its\n" \
|
||||
"expected format (e.g., ELF or DWARF)."
|
||||
|
||||
DRGNPY_PUBLIC PyMODINIT_FUNC PyInit__drgn(void)
|
||||
{
|
||||
PyObject *m;
|
||||
@ -260,14 +145,14 @@ DRGNPY_PUBLIC PyMODINIT_FUNC PyInit__drgn(void)
|
||||
PyModule_AddObject(m, "__version__", version);
|
||||
|
||||
FaultError = PyErr_NewExceptionWithDoc("_drgn.FaultError",
|
||||
FaultError_DOC, NULL, NULL);
|
||||
drgn_FaultError_DOC, NULL, NULL);
|
||||
if (!FaultError)
|
||||
goto err;
|
||||
PyModule_AddObject(m, "FaultError", FaultError);
|
||||
|
||||
FileFormatError = PyErr_NewExceptionWithDoc("_drgn.FileFormatError",
|
||||
FileFormatError_DOC, NULL,
|
||||
NULL);
|
||||
drgn_FileFormatError_DOC,
|
||||
NULL, NULL);
|
||||
if (!FileFormatError)
|
||||
goto err;
|
||||
PyModule_AddObject(m, "FileFormatError", FileFormatError);
|
||||
|
@ -464,7 +464,7 @@ static int DrgnObject_init(DrgnObject *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
static char *keywords[] = {
|
||||
"prog", "type", "value", "address", "byteorder",
|
||||
"bit_offset", "bit_field_size",NULL,
|
||||
"bit_offset", "bit_field_size", NULL,
|
||||
};
|
||||
struct drgn_error *err;
|
||||
Program *prog;
|
||||
@ -1691,96 +1691,40 @@ static PyObject *DrgnObject_dir(DrgnObject *self)
|
||||
}
|
||||
|
||||
static PyGetSetDef DrgnObject_getset[] = {
|
||||
{"prog_", (getter)DrgnObject_get_prog, NULL,
|
||||
"Program\n"
|
||||
"\n"
|
||||
"Program that this object is from"},
|
||||
{"type_", (getter)DrgnObject_get_type, NULL,
|
||||
"Type\n"
|
||||
"\n"
|
||||
"Type of this object"},
|
||||
{"prog_", (getter)DrgnObject_get_prog, NULL, drgn_Object_prog__DOC},
|
||||
{"type_", (getter)DrgnObject_get_type, NULL, drgn_Object_type__DOC},
|
||||
{"address_", (getter)DrgnObject_get_address, NULL,
|
||||
"Optional[int]\n"
|
||||
"\n"
|
||||
"Address of this object if it is a reference, None if it is a value"},
|
||||
drgn_Object_address__DOC},
|
||||
{"byteorder_", (getter)DrgnObject_get_byteorder, NULL,
|
||||
"Optional[str]\n"
|
||||
"\n"
|
||||
"Byte order of this object (either 'little' or 'big') if it is a\n"
|
||||
"reference or a non-primitive value, None otherwise"},
|
||||
drgn_Object_byteorder__DOC},
|
||||
{"bit_offset_", (getter)DrgnObject_get_bit_offset, NULL,
|
||||
"Optional[int]\n"
|
||||
"\n"
|
||||
"Offset in bits from this object's address to the beginning of the object\n"
|
||||
"if it is a reference or a non-primitive value, None otherwise"},
|
||||
drgn_Object_bit_offset__DOC},
|
||||
{"bit_field_size_", (getter)DrgnObject_get_bit_field_size, NULL,
|
||||
"Optional[int]\n"
|
||||
"\n"
|
||||
"Size in bits of this object if it is a bit field, None if not"},
|
||||
drgn_Object_bit_field_size__DOC},
|
||||
{},
|
||||
};
|
||||
|
||||
static PyMethodDef DrgnObject_methods[] = {
|
||||
{"__getitem__", (PyCFunction)DrgnObject_subscript,
|
||||
METH_O | METH_COEXIST,
|
||||
"__getitem__(self, idx) -> Object\n"
|
||||
"\n"
|
||||
"Implement self[idx]. Return an Object representing the array element at\n"
|
||||
"the given index.\n"
|
||||
"\n"
|
||||
"This is only valid for pointers and arrays."},
|
||||
METH_O | METH_COEXIST, drgn_Object___getitem___DOC},
|
||||
{"value_", (PyCFunction)DrgnObject_value, METH_NOARGS,
|
||||
"value_(self) -> Any\n"
|
||||
"\n"
|
||||
"Return the value of this object as a Python object.\n"
|
||||
"\n"
|
||||
"For basic types (int, bool, etc.), this returns an object of the\n"
|
||||
"directly corresponding Python type. For pointers, this returns the\n"
|
||||
"address value of the pointer. For enums, this returns an int. For\n"
|
||||
"structures and unions, this returns a dict of members. For arrays, this\n"
|
||||
"returns a list of values."},
|
||||
drgn_Object_value__DOC},
|
||||
{"string_", (PyCFunction)DrgnObject_string, METH_NOARGS,
|
||||
"string_(self) -> bytes\n"
|
||||
"\n"
|
||||
"Return the null-terminated string pointed to by this object as bytes.\n"
|
||||
"\n"
|
||||
"This is only valid for pointers and arrays."},
|
||||
drgn_Object_string__DOC},
|
||||
{"member_", (PyCFunction)DrgnObject_member,
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
"member_(self, name: str) -> Object\n"
|
||||
"\n"
|
||||
"Return an Object representing the given structure or union member.\n"
|
||||
"\n"
|
||||
"This is only valid for structs, unions, and pointers to either. Normally\n"
|
||||
"the dot operator (\".\") can be used to accomplish the same thing, but\n"
|
||||
"this method can be used if there is a name conflict with an Object\n"
|
||||
"member or method."},
|
||||
METH_VARARGS | METH_KEYWORDS, drgn_Object_member__DOC},
|
||||
{"address_of_", (PyCFunction)DrgnObject_address_of, METH_NOARGS,
|
||||
"address_of_(self) -> Object\n"
|
||||
"\n"
|
||||
"Return an Object pointing to this object.\n"
|
||||
"\n"
|
||||
"This corresponds to the address-of (\"&\") operator in C. It is only\n"
|
||||
"possible for reference objects, as value objects don't have an address\n"
|
||||
"in the program."},
|
||||
{"read_", (PyCFunction)DrgnObject_read,
|
||||
METH_NOARGS,
|
||||
"read_(self) -> Object\n"
|
||||
"\n"
|
||||
"Read this object (which may be a reference or a value) and return it as\n"
|
||||
"a value object. This is useful if the object can change in the running\n"
|
||||
"program (but of course nothing stops the program from modifying the\n"
|
||||
"object while it is being read)."},
|
||||
drgn_Object_address_of__DOC},
|
||||
{"read_", (PyCFunction)DrgnObject_read, METH_NOARGS,
|
||||
drgn_Object_read__DOC},
|
||||
{"__round__", (PyCFunction)DrgnObject_round,
|
||||
METH_VARARGS | METH_KEYWORDS},
|
||||
{"__trunc__", (PyCFunction)DrgnObject_trunc, METH_NOARGS},
|
||||
{"__floor__", (PyCFunction)DrgnObject_floor, METH_NOARGS},
|
||||
{"__ceil__", (PyCFunction)DrgnObject_ceil, METH_NOARGS},
|
||||
{"__dir__", (PyCFunction)DrgnObject_dir,
|
||||
METH_NOARGS,
|
||||
{"__dir__", (PyCFunction)DrgnObject_dir, METH_NOARGS,
|
||||
"dir() implementation which includes structure and union members."},
|
||||
{"__format__", (PyCFunction)DrgnObject_format,
|
||||
METH_O,
|
||||
{"__format__", (PyCFunction)DrgnObject_format, METH_O,
|
||||
"Object formatter."},
|
||||
{},
|
||||
};
|
||||
@ -1827,63 +1771,6 @@ static PyMappingMethods DrgnObject_as_mapping = {
|
||||
(binaryfunc)DrgnObject_subscript, /* mp_subscript */
|
||||
};
|
||||
|
||||
#define DrgnObject_DOC \
|
||||
"An Object represents a symbol or value in a program. The object may be\n" \
|
||||
"in the memory of the program (a \"reference\").\n" \
|
||||
"\n" \
|
||||
">>> Object(prog, 'int', address=0xffffffffc09031a0)\n" \
|
||||
"\n" \
|
||||
"It can also be a temporary computed value (a \"value\").\n" \
|
||||
"\n" \
|
||||
">>> Object(prog, 'int', value=4)\n" \
|
||||
"\n" \
|
||||
"All Object instances have two members: prog_, the program that the\n" \
|
||||
"object is from; and type_, the type of the object. Reference objects\n" \
|
||||
"have an address_ member. Objects may also have a byteorder_,\n" \
|
||||
"bit_offset_, and bit_field_size.\n" \
|
||||
"\n" \
|
||||
"repr() of an Object returns a Python representation of the object.\n" \
|
||||
"\n" \
|
||||
">>> print(repr(prog['jiffies']))\n" \
|
||||
"Object(prog, 'volatile long unsigned int', address=0xffffffffbf005000)\n" \
|
||||
"\n" \
|
||||
"str() returns a representation of the object in programming language\n" \
|
||||
"syntax.\n" \
|
||||
"\n" \
|
||||
">>> print(prog['jiffies'])\n" \
|
||||
"(volatile long unsigned int)4326237045\n" \
|
||||
"\n" \
|
||||
"Note that the drgn CLI is set up so that Objects are displayed with\n" \
|
||||
"str() instead of repr() (the latter is the default behavior of Python's\n" \
|
||||
"interactive mode). This means that in the drgn CLI, the call to print()\n" \
|
||||
"in the second example above is not necessary.\n" \
|
||||
"\n" \
|
||||
"Objects support their programming language's operators wherever\n" \
|
||||
"possible. E.g., structure members can be accessed with the dot (\".\")\n" \
|
||||
"operator, arrays can be subscripted with \"[]\", arithmetic can be\n" \
|
||||
"performed, and objects can be compared.\n" \
|
||||
"\n" \
|
||||
">>> print(prog['init_task'].pid)\n" \
|
||||
"(pid_t)0\n" \
|
||||
">>> print(prog['init_task'].comm[0])\n" \
|
||||
"(char)115\n" \
|
||||
">>> print(repr(prog['init_task'].nsproxy.mnt_ns.mounts + 1))\n" \
|
||||
"Object(prog, 'unsigned int', value=34)\n" \
|
||||
">>> prog['init_task'].nsproxy.mnt_ns.pending_mounts > 0\n" \
|
||||
"False\n" \
|
||||
"\n" \
|
||||
"Note that because the C structure dereference operator (\"->\") is not\n" \
|
||||
"valid syntax in Python, \".\" is also used to access members of pointers\n" \
|
||||
"to structures. Similarly, the indirection operator (\"*\") is not valid\n" \
|
||||
"syntax in Python, so pointers can be dereferenced with \"[0]\" (e.g.,\n" \
|
||||
"write \"p[0]\" instead of \"*p\"). The address-of operator (\"&\") is\n" \
|
||||
"available as the address_of_() method.\n" \
|
||||
"\n" \
|
||||
"Object members and methods are named with a trailing underscore to avoid\n" \
|
||||
"conflicting with structure or union members. The helper methods always\n" \
|
||||
"take precedence over structure members; use member_() if there is a\n" \
|
||||
"conflict."
|
||||
|
||||
PyTypeObject DrgnObject_type = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
"_drgn.Object", /* tp_name */
|
||||
@ -1905,7 +1792,7 @@ PyTypeObject DrgnObject_type = {
|
||||
NULL, /* tp_setattro */
|
||||
NULL, /* tp_as_buffer */
|
||||
Py_TPFLAGS_DEFAULT, /* tp_flags */
|
||||
DrgnObject_DOC, /* tp_doc */
|
||||
drgn_Object_DOC, /* tp_doc */
|
||||
NULL, /* tp_traverse */
|
||||
NULL, /* tp_clear */
|
||||
DrgnObject_richcompare, /* tp_richcompare */
|
||||
@ -1926,6 +1813,30 @@ PyTypeObject DrgnObject_type = {
|
||||
|
||||
};
|
||||
|
||||
PyObject *DrgnObject_NULL(PyObject *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
static char *keywords[] = {"prog", "type", NULL};
|
||||
PyObject *prog_obj, *type_obj;
|
||||
PyObject *a, *k, *ret;
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO:NULL", keywords,
|
||||
&prog_obj, &type_obj))
|
||||
return NULL;
|
||||
|
||||
a = Py_BuildValue("OO", prog_obj, type_obj);
|
||||
if (!a)
|
||||
return NULL;
|
||||
k = Py_BuildValue("{s:i}", "value", 0);
|
||||
if (!k) {
|
||||
Py_DECREF(a);
|
||||
return NULL;
|
||||
}
|
||||
ret = PyObject_Call((PyObject *)&DrgnObject_type, a, k);
|
||||
Py_DECREF(k);
|
||||
Py_DECREF(a);
|
||||
return ret;
|
||||
}
|
||||
|
||||
DrgnObject *cast(PyObject *self, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
static char *keywords[] = {"type", "obj", NULL};
|
||||
|
@ -291,103 +291,26 @@ static PyObject *Program_get_byteorder(Program *self, void *arg)
|
||||
|
||||
static PyMethodDef Program_methods[] = {
|
||||
{"__getitem__", (PyCFunction)Program_subscript, METH_O | METH_COEXIST,
|
||||
"__getitem__(self, name) -> Object\n"
|
||||
"\n"
|
||||
"Implement self[name]. Return an Object (variable, constant, or function)\n"
|
||||
"with the given name.\n"
|
||||
"\n"
|
||||
"If there are multiple objects with the same name, one is returned\n"
|
||||
"arbitrarily. In this case, the constant(), function(), or variable()\n"
|
||||
"methods can be used instead."},
|
||||
drgn_Program___getitem___DOC},
|
||||
{"read", (PyCFunction)Program_read, METH_VARARGS | METH_KEYWORDS,
|
||||
"read(self, address: int, size: int, physical: bool = False) -> bytes\n"
|
||||
"\n"
|
||||
"Return size bytes of memory starting at address in the program. The\n"
|
||||
"address may be virtual (the default) or physical if the program supports\n"
|
||||
"it.\n"
|
||||
"\n"
|
||||
">>> prog.read(0xffffffffbe012b40, 16)\n"
|
||||
"b'swapper/0\\x00\\x00\\x00\\x00\\x00\\x00\\x00'"},
|
||||
drgn_Program_read_DOC},
|
||||
{"type", (PyCFunction)Program_find_type, METH_VARARGS | METH_KEYWORDS,
|
||||
"type(self, name: str, filename: Optional[str] = None) -> Type\n"
|
||||
"\n"
|
||||
"Return a Type object for the type with the given name.\n"
|
||||
"\n"
|
||||
"If there are multiple types with the given name, they can be\n"
|
||||
"distinguished by passing the filename that the desired identifier was\n"
|
||||
"defined in. If no filename is given, it is undefined which one is\n"
|
||||
"returned.\n"
|
||||
"\n"
|
||||
"If no matches are found, this raises a LookupError.\n"
|
||||
"\n"
|
||||
">>> prog.type('long')\n"
|
||||
"int_type(name='long', size=8, is_signed=True)"},
|
||||
drgn_Program_type_DOC},
|
||||
{"constant", (PyCFunction)Program_constant,
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
"constant(self, name: str, filename: Optional[str] = None) -> Object\n"
|
||||
"\n"
|
||||
"Return an Object representing the constant (e.g., enumeration constant\n"
|
||||
"or macro) with the given name.\n"
|
||||
"\n"
|
||||
"If there are multiple constants with the given name, they can be\n"
|
||||
"distinguished by passing the filename that the desired constant was\n"
|
||||
"defined in. If no filename is given, it is undefined which one is\n"
|
||||
"returned.\n"
|
||||
"\n"
|
||||
"If no matches are found, this raises a LookupError.\n"
|
||||
"\n"
|
||||
"Note that support for macro constants is not yet implemented for DWARF\n"
|
||||
"files, and most compilers don't generate macro debugging information\n"
|
||||
"by default anyways.\n"
|
||||
"\n"
|
||||
">>> prog.constant('PIDTYPE_MAX')\n"
|
||||
"Object(prog, 'enum pid_type', value=4)"},
|
||||
METH_VARARGS | METH_KEYWORDS, drgn_Program_constant_DOC},
|
||||
{"function", (PyCFunction)Program_function,
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
"function(self, name: str, filename: Optional[str] = None) -> Object\n"
|
||||
"\n"
|
||||
"Return an Object representing the function with the given name.\n"
|
||||
"\n"
|
||||
"If there are multiple functions with the given name, they can be\n"
|
||||
"distinguished by passing the filename that the desired function was\n"
|
||||
"defined in. If no filename is given, it is undefined which one is\n"
|
||||
"returned.\n"
|
||||
"\n"
|
||||
"If no matches are found, this raises a LookupError.\n"
|
||||
"\n"
|
||||
">>> prog.function('schedule')\n"
|
||||
"Object(prog, 'void (void)', address=0xffffffff94392370)"},
|
||||
METH_VARARGS | METH_KEYWORDS, drgn_Program_function_DOC},
|
||||
{"variable", (PyCFunction)Program_variable,
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
"variable(self, name: str, filename: Optional[str] = None) -> Object\n"
|
||||
"\n"
|
||||
"Return an Object representing the variable with the given name.\n"
|
||||
"\n"
|
||||
"If there are multiple variables with the given name, they can be\n"
|
||||
"distinguished by passing the filename that the desired variable was\n"
|
||||
"defined in. If no filename is given, it is undefined which one is\n"
|
||||
"returned.\n"
|
||||
"\n"
|
||||
"If no matches are found, this raises a LookupError.\n"
|
||||
"\n"
|
||||
">>> prog.variable('jiffies')\n"
|
||||
"Object(prog, 'volatile unsigned long', address=0xffffffff94c05000)"},
|
||||
METH_VARARGS | METH_KEYWORDS, drgn_Program_variable_DOC},
|
||||
{},
|
||||
};
|
||||
|
||||
static PyGetSetDef Program_getset[] = {
|
||||
{"flags", (getter)Program_get_flags, NULL,
|
||||
"ProgramFlags\n"
|
||||
"\n"
|
||||
"flags which apply to this program"},
|
||||
{"flags", (getter)Program_get_flags, NULL, drgn_Program_flags_DOC},
|
||||
{"word_size", (getter)Program_get_word_size, NULL,
|
||||
"int\n"
|
||||
"\n"
|
||||
"size of a word in this program in bytes"},
|
||||
drgn_Program_word_size_DOC},
|
||||
{"byteorder", (getter)Program_get_byteorder, NULL,
|
||||
"str\n"
|
||||
"\n"
|
||||
"byte order in this program (either 'little' or 'big')"},
|
||||
drgn_Program_byteorder_DOC},
|
||||
{},
|
||||
};
|
||||
|
||||
@ -396,20 +319,6 @@ static PyMappingMethods Program_as_mapping = {
|
||||
(binaryfunc)Program_subscript, /* mp_subscript */
|
||||
};
|
||||
|
||||
#define Program_DOC \
|
||||
"A Program represents a crashed or running program. It can be used to lookup\n" \
|
||||
"type definitions, access variables, and read arbitrary memory.\n" \
|
||||
"\n" \
|
||||
"The main functionality of a Program is looking up objects (i.e.,\n" \
|
||||
"variables, constants, or functions). This is done with the \"[]\"\n" \
|
||||
"operator.\n" \
|
||||
"\n" \
|
||||
">>> print(prog['pid_max'])\n" \
|
||||
"(int)32768\n" \
|
||||
"\n" \
|
||||
"A Program cannot be constructed directly. Instead, use\n" \
|
||||
"program_from_core_dump(), program_from_kernel(), or program_from_pid()."
|
||||
|
||||
PyTypeObject Program_type = {
|
||||
PyVarObject_HEAD_INIT(NULL, 0)
|
||||
"_drgn.Program", /* tp_name */
|
||||
@ -431,7 +340,7 @@ PyTypeObject Program_type = {
|
||||
NULL, /* tp_setattro */
|
||||
NULL, /* tp_as_buffer */
|
||||
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
|
||||
Program_DOC, /* tp_doc */
|
||||
drgn_Program_DOC, /* tp_doc */
|
||||
(traverseproc)Program_traverse, /* tp_traverse */
|
||||
(inquiry)Program_clear, /* tp_clear */
|
||||
NULL, /* tp_richcompare */
|
||||
|
@ -551,72 +551,35 @@ static PyObject *DrgnType_getter(DrgnType *self, struct DrgnType_Attr *attr)
|
||||
|
||||
static PyGetSetDef DrgnType_getset[] = {
|
||||
{"_ptr", (getter)DrgnType_get_ptr, NULL,
|
||||
"int\n"
|
||||
"Address of underlying ``struct drgn_type``.\n"
|
||||
"\n"
|
||||
"address of underlying struct drgn_type"},
|
||||
"This is used for testing.\n"
|
||||
"\n"
|
||||
":vartype: int"},
|
||||
{"kind", (getter)DrgnType_getter, NULL,
|
||||
"TypeKind\n"
|
||||
"\n"
|
||||
"kind of this type", &DrgnType_attr_kind},
|
||||
{"qualifiers", (getter)DrgnType_getter, NULL,
|
||||
"Qualifiers\n"
|
||||
"\n"
|
||||
"bitmask of this type's qualifiers", &DrgnType_attr_qualifiers},
|
||||
{"name", (getter)DrgnType_getter, NULL,
|
||||
"str\n"
|
||||
"\n"
|
||||
"name of this integer, boolean, floating-point, complex, or typedef type\n",
|
||||
drgn_Type_kind_DOC, &DrgnType_attr_kind},
|
||||
{"qualifiers", (getter)DrgnType_getter, NULL, drgn_Type_qualifiers_DOC,
|
||||
&DrgnType_attr_qualifiers},
|
||||
{"name", (getter)DrgnType_getter, NULL, drgn_Type_name_DOC,
|
||||
&DrgnType_attr_name},
|
||||
{"tag", (getter)DrgnType_getter, NULL,
|
||||
"Optional[str]\n"
|
||||
"\n"
|
||||
"tag of this structure, union, or enumerated type or None if this is an\n"
|
||||
"anonymous type", &DrgnType_attr_tag},
|
||||
{"size", (getter)DrgnType_getter, NULL,
|
||||
"Optional[int]\n"
|
||||
"\n"
|
||||
"size in bytes of this integer, boolean, floating-point, complex,\n"
|
||||
"structure, union, or pointer type, or None if this is an incomplete\n"
|
||||
"structure or union type", &DrgnType_attr_size},
|
||||
{"length", (getter)DrgnType_getter, NULL,
|
||||
"Optional[int]\n"
|
||||
"\n"
|
||||
"number of elements in this array type or None if this is an incomplete\n"
|
||||
"array type", &DrgnType_attr_length},
|
||||
{"is_signed", (getter)DrgnType_getter, NULL,
|
||||
"bool\n"
|
||||
"\n"
|
||||
"whether this integer type is signed", &DrgnType_attr_is_signed},
|
||||
{"type", (getter)DrgnType_getter, NULL,
|
||||
"Optional[Type]\n"
|
||||
"\n"
|
||||
"type underlying this type (i.e., the type denoted by a typedef type, the\n"
|
||||
"compatible integer type of an enumerated type [which is None if this is\n"
|
||||
"an incomplete type], the type referenced by a pointer type, the element\n"
|
||||
"type of an array, or the return type of a function type)\n",
|
||||
{"tag", (getter)DrgnType_getter, NULL, drgn_Type_tag_DOC,
|
||||
&DrgnType_attr_tag},
|
||||
{"size", (getter)DrgnType_getter, NULL, drgn_Type_size_DOC,
|
||||
&DrgnType_attr_size},
|
||||
{"length", (getter)DrgnType_getter, NULL, drgn_Type_length_DOC,
|
||||
&DrgnType_attr_length},
|
||||
{"is_signed", (getter)DrgnType_getter, NULL, drgn_Type_is_signed_DOC,
|
||||
&DrgnType_attr_is_signed},
|
||||
{"type", (getter)DrgnType_getter, NULL, drgn_Type_type_DOC,
|
||||
&DrgnType_attr_type},
|
||||
{"members", (getter)DrgnType_getter, NULL,
|
||||
"Optional[List[Tuple[Type, Optional[str], int, int]]]\n"
|
||||
"\n"
|
||||
"list of members of this structure or union type as (type, name, bit\n"
|
||||
"offset, bit field size) tuples, or None if this is an incomplete type",
|
||||
{"members", (getter)DrgnType_getter, NULL, drgn_Type_members_DOC,
|
||||
&DrgnType_attr_members},
|
||||
{"enumerators", (getter)DrgnType_getter, NULL,
|
||||
"Optional[List[Tuple[str, int]]]\n"
|
||||
"\n"
|
||||
"list of enumeration constants of this enumerated type as (name, value)\n"
|
||||
"tuples, or None if this is an incomplete type",
|
||||
&DrgnType_attr_enumerators},
|
||||
{"parameters", (getter)DrgnType_getter, NULL,
|
||||
"List[Tuple[Type, Optional[str]]]\n"
|
||||
"\n"
|
||||
"list of parameters of this function type as (type, name) tuples",
|
||||
drgn_Type_enumerators_DOC, &DrgnType_attr_enumerators},
|
||||
{"parameters", (getter)DrgnType_getter, NULL, drgn_Type_parameters_DOC,
|
||||
&DrgnType_attr_parameters},
|
||||
{"is_variadic", (getter)DrgnType_getter, NULL,
|
||||
"bool\n"
|
||||
"\n"
|
||||
"whether this function type takes a variable number of arguments",
|
||||
&DrgnType_attr_is_variadic},
|
||||
drgn_Type_is_variadic_DOC, &DrgnType_attr_is_variadic},
|
||||
{},
|
||||
};
|
||||
|
||||
@ -979,23 +942,29 @@ static PyObject *DrgnType_is_complete(DrgnType *self)
|
||||
|
||||
static int qualifiers_converter(PyObject *arg, void *result)
|
||||
{
|
||||
PyObject *value;
|
||||
unsigned long qualifiers;
|
||||
|
||||
if (!PyObject_TypeCheck(arg, (PyTypeObject *)Qualifiers_class)) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"qualifiers must be Qualifiers");
|
||||
return 0;
|
||||
if (arg == Py_None) {
|
||||
qualifiers = 0;
|
||||
} else {
|
||||
PyObject *value;
|
||||
|
||||
if (!PyObject_TypeCheck(arg,
|
||||
(PyTypeObject *)Qualifiers_class)) {
|
||||
PyErr_SetString(PyExc_TypeError,
|
||||
"qualifiers must be Qualifiers or None");
|
||||
return 0;
|
||||
}
|
||||
|
||||
value = PyObject_GetAttrString(arg, "value");
|
||||
if (!value)
|
||||
return 0;
|
||||
|
||||
qualifiers = PyLong_AsUnsignedLong(value);
|
||||
Py_DECREF(value);
|
||||
if (qualifiers == (unsigned long)-1 && PyErr_Occurred())
|
||||
return 0;
|
||||
}
|
||||
|
||||
value = PyObject_GetAttrString(arg, "value");
|
||||
if (!value)
|
||||
return 0;
|
||||
|
||||
qualifiers = PyLong_AsUnsignedLong(value);
|
||||
Py_DECREF(value);
|
||||
if (qualifiers == (unsigned long)-1 && PyErr_Occurred())
|
||||
return 0;
|
||||
*(unsigned char *)result = qualifiers;
|
||||
return 1;
|
||||
}
|
||||
@ -1053,38 +1022,15 @@ static PyObject *DrgnType_richcompare(DrgnType *self, PyObject *other, int op)
|
||||
Py_RETURN_FALSE;
|
||||
}
|
||||
|
||||
#define DrgnType_DOC \
|
||||
"Type descriptor\n" \
|
||||
"\n" \
|
||||
"A Type object represents a type in a program. Each kind of type (e.g.,\n" \
|
||||
"integer, structure) has different descriptors (e.g., name, size). Types\n" \
|
||||
"can also have qualifiers (e.g., constant, atomic). Accessing a\n" \
|
||||
"descriptor which does not apply to a type raises an exception.\n" \
|
||||
"\n" \
|
||||
"This class cannot be constructed directly. Instead, use one of the\n" \
|
||||
"*_type() factory functions."
|
||||
|
||||
static PyMethodDef DrgnType_methods[] = {
|
||||
{"type_name", (PyCFunction)DrgnType_type_name, METH_NOARGS,
|
||||
"type_name(self) -> str\n"
|
||||
"\n"
|
||||
"Get a descriptive full name of this type."},
|
||||
drgn_Type_type_name_DOC},
|
||||
{"is_complete", (PyCFunction)DrgnType_is_complete, METH_NOARGS,
|
||||
"is_complete(self) -> bool\n"
|
||||
"\n"
|
||||
"Get whether this type is complete (i.e., the type definition is known).\n"
|
||||
"This is always False for void types. It may be False for structure,\n"
|
||||
"union, enumerated, and array types, as well as typedef types where the\n"
|
||||
"underlying type is one of those. Otherwise, it is always True."},
|
||||
drgn_Type_is_complete_DOC},
|
||||
{"qualified", (PyCFunction)DrgnType_qualified,
|
||||
METH_VARARGS | METH_KEYWORDS,
|
||||
"qualified(self, qualifiers: Qualifiers) -> Type\n"
|
||||
"\n"
|
||||
"Return a copy of this type with different qualifiers."},
|
||||
METH_VARARGS | METH_KEYWORDS, drgn_Type_qualified_DOC},
|
||||
{"unqualified", (PyCFunction)DrgnType_unqualified, METH_NOARGS,
|
||||
"unqualified(self) -> Type\n"
|
||||
"\n"
|
||||
"Return a copy of this type with no qualifiers."},
|
||||
drgn_Type_unqualified_DOC},
|
||||
{},
|
||||
};
|
||||
|
||||
@ -1116,7 +1062,7 @@ PyTypeObject DrgnType_type = {
|
||||
NULL, /* tp_setattro */
|
||||
NULL, /* tp_as_buffer */
|
||||
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,/* tp_flags */
|
||||
DrgnType_DOC, /* tp_doc */
|
||||
drgn_Type_DOC, /* tp_doc */
|
||||
(traverseproc)DrgnType_traverse, /* tp_traverse */
|
||||
(inquiry)DrgnType_clear, /* tp_clear */
|
||||
(richcmpfunc)DrgnType_richcompare, /* tp_richcompare */
|
||||
|
Loading…
Reference in New Issue
Block a user