Skip to content
Snippets Groups Projects
Commit 0b529cc6 authored by Mitchel Humpherys's avatar Mitchel Humpherys
Browse files

lrdp-v2: docs: Improve existing documentation

Some of our module documentation is a bit lacking.  Fix this by filling
in some docstrings and add then adding some pointers to existing
modules/functions in the docs.

Change-Id: Ibf714225f1fc185be916e514861540fd98cb2de5
parent 43bbd160
No related branches found
No related tags found
No related merge requests found
GDBMI
=====
The ``gdbmi`` module provides the low-level interface to gdb's
`gdbmi <https://sourceware.org/gdb/onlinedocs/gdb/GDB_002fMI.html>`_.
.. automodule:: gdbmi
:members:
:undoc-members:
......@@ -7,6 +7,9 @@ looking to add their own parser plugin to the LRDP.
.. toctree::
:maxdepth: 2
writing_parsers
ramdump
gdbmi
parser_util
register
sizes
parser_util
.. _`RamDump`:
RamDump
=======
.. automodule:: ramdump
:members:
.. _`register`:
register
========
......
.. _`Sizes`:
Sizes
=====
.. automodule:: sizes
:members:
:undoc-members:
.. _`Writing Parsers`:
Writing Parsers
===============
.. autofunction:: parser_util.register_parser
:noindex:
......@@ -48,6 +48,13 @@ class GdbMIException(Exception):
class GdbMI(object):
"""Interface to the ``gdbmi`` subprocess. This should generally be
used as a context manager (using Python's ``with`` statement),
like so::
>>> with GdbMI(gdb_path, elf) as g:
print('GDB Version: ' + g.version())
"""
def __init__(self, gdb_path, elf):
self.gdb_path = gdb_path
......@@ -56,6 +63,10 @@ class GdbMI(object):
self._gdbmi = None
def open(self):
"""Open the connection to the ``gdbmi`` backend. Not needed if using
``gdbmi`` as a context manager (recommended).
"""
self._gdbmi = subprocess.Popen(
[self.gdb_path, '--interpreter=mi2', self.elf],
stdin=subprocess.PIPE,
......@@ -64,6 +75,10 @@ class GdbMI(object):
self._flush_gdbmi()
def close(self):
"""Close the connection to the ``gdbmi`` backend. Not needed if using
``gdbmi`` as a context manager (recommended).
"""
self._gdbmi.communicate('quit')
def __enter__(self):
......@@ -147,11 +162,13 @@ class GdbMI(object):
>>> gdbmi.field_offset("struct ion_buffer", "heap")
20
- `the_type': struct or type (note that if it's a struct you
should include the word "struct" (e.g.: "struct
ion_buffer"))
``the_type``
struct or type (note that if it's a struct you should
include the word ``"struct"`` (e.g.: ``"struct
ion_buffer"``))
- `field': the field whose offset we want to return
``field``
the field whose offset we want to return
"""
cmd = 'print /x (int)&(({0} *)0)->{1}'.format(the_type, field)
......@@ -159,6 +176,7 @@ class GdbMI(object):
return gdb_hex_to_dec(result)
def container_of(self, ptr, the_type, member):
"""Like ``container_of`` from the kernel."""
return ptr - self.field_offset(the_type, member)
def sibling_field_addr(self, ptr, parent_type, member, sibling):
......@@ -167,35 +185,40 @@ class GdbMI(object):
Example:
Given:
Given a dump containing an instance of the following struct::
struct pizza {
int price;
int *qty;
int qty;
};
int quanitity = 42;
struct pizza mypizza = {.price = 10, .qty = &quanitity};
qtyp = dump.address_of('quantity')
price = dump.read_int(gdbmi.sibling_field_addr(qtyp, 'struct pizza', 'qty', 'price'))
If you have a pointer to qty, you can get a pointer to price with:
>>> addr = sibling_field_addr(qty, 'struct pizza', 'qty', 'price')
>>> price = dump.read_int(addr)
>>> price
10
"""
return self.container_of(ptr, parent_type, member) + \
self.field_offset(parent_type, sibling)
def sizeof(self, the_type):
"""Returns the size of the type specified by `the_type'."""
"""Returns the size of the type specified by ``the_type``."""
result = self._run_for_one('print /x sizeof({0})'.format(the_type))
return gdb_hex_to_dec(result)
def address_of(self, symbol):
"""Returns the address of the specified symbol."""
"""Returns the address of the specified symbol.
>>> hex(dump.address_of('linux_banner'))
'0xc0b0006a'
"""
result = self._run_for_one('print /x &{0}'.format(symbol))
return int(result.split(' ')[-1], 16)
def get_symbol_info(self, address):
"""Returns a GdbSymbol representing the nearest symbol found at
`address'."""
``address``."""
result = self._run_for_one('info symbol ' + hex(address))
parts = result.split(' ')
if len(parts) < 2:
......@@ -205,11 +228,25 @@ class GdbMI(object):
return GdbSymbol(symbol, section, address)
def symbol_at(self, address):
"""Get the symbol at the specified address (using `get_symbol_info')"""
"""Get the symbol at the given address (using ``get_symbol_info``)"""
return self.get_symbol_info(address).symbol
def get_enum_lookup_table(self, enum, upperbound):
"""Return a table translating enum values to human readable strings."""
"""Return a table translating enum values to human readable
strings.
>>> dump.gdbmi.get_enum_lookup_table('ion_heap_type', 10)
['ION_HEAP_TYPE_SYSTEM',
'ION_HEAP_TYPE_SYSTEM_CONTIG',
'ION_HEAP_TYPE_CARVEOUT',
'ION_HEAP_TYPE_CHUNK',
'ION_HEAP_TYPE_CUSTOM',
'ION_NUM_HEAPS',
'6',
'7',
'8',
'9']
"""
table = []
for i in range(0, upperbound):
result = self._run_for_first(
......@@ -223,8 +260,13 @@ class GdbMI(object):
return table
def get_func_info(self, address):
"""Returns the function info at a particular address, specifically line
and file."""
"""Returns the function info at a particular address, specifically
line and file.
>>> dump.gdbmi.get_func_info(dump.gdbmi.address_of('panic'))
'Line 78 of \\"kernel/kernel/panic.c\\"'
"""
result = self._run_for_one('info line *0x{0:x}'.format(address))
m = re.search(r'(Line \d+ of \\?\".*\\?\")', result)
if m is not None:
......
# Copyright (c) 2013-2014, The Linux Foundation. All rights reserved.
# Copyright (c) 2013-2015, The Linux Foundation. All rights reserved.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 and
......@@ -39,34 +39,47 @@ def cleanupString(unclean_str):
def register_parser(longopt, desc, shortopt=None, optional=False):
"""Decorator to register a parser class (a class that inherits from
RamParser) with the parsing framework. By using this decorator
``RamParser``) with the parsing framework. By using this decorator
your parser will automatically be hooked up to the command-line
parsing code.
This makes it very easy and clean to add a new parser:
o Drop a new file in parsers that defines a class that inherits
from RamParser
1. Drop a new file in the ``parsers/`` directory that defines a
class that inherits from ``RamParser``
o Decorate your class with @register_parser
2. Decorate your class with ``@register_parser``
o Define a `parse' method for your class
3. Define a ``parse`` method for your class
All of the command line argument handling and invoking the parse
method of your parser will then be handled automatically.
Example::
# file: parsers/my_banner.py
@register_parser('--banner', 'Print the kernel banner')
class BannerParser(RamParser):
def parse(self):
print self.ramdump.read_cstring('linux_banner', 256, False)
Required arguments:
- longopt:: The longopt command line switch for this parser
``longopt``
The longopt command line switch for this parser
- desc:: A short description of the parser (also shown in the
help-text associated with the longopt)
``desc``
A short description of the parser (also shown in the help-text
associated with the longopt)
Optional arguments:
- shortopt:: The shortopt command line switch for this parser
``shortopt``
The shortopt command line switch for this parser
- optional:: Indicates the parser is optional and should not be run with
``optional``
Indicates the parser is optional and should not be run with
--everything
"""
......@@ -79,11 +92,11 @@ def register_parser(longopt, desc, shortopt=None, optional=False):
def get_parsers():
"""Imports everyone under the `parsers' directory. It is expected that
"""Imports everyone under the ``parsers`` directory. It is expected that
the parsers under the parsers directory will be a collection of
classes that subclass RamParser and use the register_parser
decorator to register themselves with the parser
framework. Therefore, importing all the modules under `parsers'
framework. Therefore, importing all the modules under ``parsers``
should have the side-effect of populating the (internal to
parser_util) _parsers list with the discovered parsers.
......@@ -111,7 +124,7 @@ def get_parsers():
class RamParser(object):
"""Base class for implementing ramdump parsers. New parsers should inherit
from this class and define a `parse' method.
from this class and define a ``parse`` method.
Interesting properties that will be set for usage in derived
classes:
......@@ -180,7 +193,7 @@ def _xxd_line(addr, data):
)
def xxd(address, data, file_object=None):
"""Dumps data to `file_object' or stdout, in the format of `xxd'. data
"""Dumps data to ``file_object`` or stdout, in the format of ``xxd``. data
should be a list of integers.
>>> xxd(0x1000, [0xde, 0xad, 0xbe, 0xef, 112, 105, 122, 122, 97, 0, 0, 42, 43, 44, 45, 90])
......
......@@ -46,6 +46,7 @@ extra_mem_file_names = ['EBI1CS1.BIN', 'DDRCS1.BIN', 'ebi1_cs1.bin', 'DDRCS0_1.B
class RamDump():
"""The main interface to the RAM dump"""
class Unwinder ():
......@@ -555,6 +556,13 @@ class RamDump():
self.gdbmi.close()
def open_file(self, file_name, mode='wb'):
"""Open a file in the out directory.
Example:
>>> with self.ramdump.open_file('pizza.txt') as p:
p.write('Pizza is the best\\n')
"""
file_path = os.path.join(self.outdir, file_name)
f = None
try:
......@@ -598,6 +606,13 @@ class RamDump():
return True
def get_config_val(self, config):
"""Gets the value of a kernel config option.
Example:
>>> va_bits = int(dump.get_config_val("CONFIG_ARM64_VA_BITS"))
39
"""
return self.config_dict.get(config)
def is_config_defined(self, config):
......@@ -982,6 +997,13 @@ class RamDump():
stream.close()
def address_of(self, symbol):
"""Returns the address of a symbol.
Example:
>>> hex(dump.address_of('linux_banner'))
'0xffffffc000c7a0a8L'
"""
try:
return self.gdbmi.address_of(symbol)
except gdbmi.GdbMIException:
......@@ -1000,40 +1022,52 @@ class RamDump():
pass
def array_index(self, addr, the_type, index):
"""Index into the array of type `the_type' located at `addr'.
"""Index into the array of type ``the_type`` located at ``addr``.
I.e.:
Given:
I.e., given::
int my_arr[3];
my_arr[2] = 42;
You could do the following:
The following:
my_arr_addr = dump.address_of("my_arr")
dump.read_word(dump.array_index(my_arr_addr, "int", 2))
will return 42.
>>> addr = dump.address_of("my_arr")
>>> dump.read_word(dump.array_index(addr, "int", 2))
42
"""
offset = self.gdbmi.sizeof(the_type) * index
return addr + offset
def field_offset(self, the_type, field):
"""Gets the offset of a field from the base of its containing struct.
This can be useful when reading struct fields, although you should
consider using :func:`~read_structure_field` if
you're reading a word-sized value.
Example:
>>> dump.field_offset('struct device', 'bus')
168
"""
try:
return self.gdbmi.field_offset(the_type, field)
except gdbmi.GdbMIException:
pass
def container_of(self, ptr, the_type, member):
"""Like ``container_of`` in the kernel."""
try:
return self.gdbmi.container_of(ptr, the_type, member)
except gdbmi.GdbMIException:
pass
def sibling_field_addr(self, ptr, parent_type, member, sibling):
"""Gets the address of a sibling structure field.
Given the address of some field within a structure, returns the
address of the requested sibling field.
"""
try:
return self.gdbmi.sibling_field_addr(ptr, parent_type, member, sibling)
except gdbmi.GdbMIException:
......@@ -1111,10 +1145,12 @@ class RamDump():
return s[0] if s is not None else None
def read_byte(self, addr_or_name, virtual=True, cpu=None):
"""Reads a single byte."""
s = self.read_string(addr_or_name, '<B', virtual, cpu)
return s[0] if s is not None else None
def read_bool(self, addr_or_name, virtual=True, cpu=None):
"""Reads a bool."""
s = self.read_string(addr_or_name, '<?', virtual, cpu)
return s[0] if s is not None else None
......@@ -1134,7 +1170,7 @@ class RamDump():
return s[0] if s is not None else None
def read_int(self, addr_or_name, virtual=True, cpu=None):
"""Alias for `read_u32'"""
"""Alias for :func:`~read_u32`"""
return self.read_u32(addr_or_name, virtual, cpu)
def read_u16(self, addr_or_name, virtual=True, cpu=None):
......@@ -1143,7 +1179,7 @@ class RamDump():
return s[0] if s is not None else None
def read_pointer(self, addr_or_name, virtual=True, cpu=None):
"""Reads `addr_or_name' as a pointer variable.
"""Reads ``addr_or_name`` as a pointer variable.
The read length is either 32-bit or 64-bit depending on the
architecture. This returns the *value* of the pointer variable
......@@ -1167,8 +1203,8 @@ class RamDump():
def read_structure_cstring(self, addr_or_name, struct_name, field,
max_length=100):
"""reads a C string from a structure field. The C string field will be
dereferenced before reading, so it should be a `char *', not a
`char []'.
dereferenced before reading, so it should be a ``char *``, not a
``char []``.
"""
virt = self.resolve_virt(addr_or_name)
cstring_addr = virt + self.field_offset(struct_name, field)
......@@ -1176,6 +1212,7 @@ class RamDump():
def read_cstring(self, addr_or_name, max_length=100, virtual=True,
cpu=None):
"""Reads a C string."""
addr = addr_or_name
if virtual:
if cpu is not None:
......@@ -1213,9 +1250,9 @@ class RamDump():
return struct.unpack(format_string, s)
def hexdump(self, addr_or_name, length, virtual=True, file_object=None):
"""Returns a string with a hexdump (in the format of `xxd').
"""Returns a string with a hexdump (in the format of ``xxd``).
`length' is in bytes.
``length`` is in bytes.
Example (intentionally not in doctest format since it would require
a specific dump to be loaded to pass as a doctest):
......@@ -1251,11 +1288,19 @@ class RamDump():
return self.read_word(per_cpu_offset_addr_indexed)
def get_num_cpus(self):
"""Gets the number of CPUs in the system."""
cpu_present_bits_addr = self.address_of('cpu_present_bits')
cpu_present_bits = self.read_word(cpu_present_bits_addr)
return bin(cpu_present_bits).count('1')
def iter_cpus(self):
"""Returns an iterator over all CPUs in the system.
Example:
>>> list(dump.iter_cpus())
[0, 1, 2, 3]
"""
return xrange(self.get_num_cpus())
def thread_saved_field_common_32(self, task, reg_offset):
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment