Framework - pykdump/ directory

Program developers import everything as:

from pykdump.API import *

This document describes the submodules included in pykdump package, in particular logical separation of functionality between submodules

Directory Structure

pykdump/
        __init__.py         - check versions of Python code and C-module
        API.py              - main module
        ASCII_Art.py        - ASCII-art for tree-like structures
        datatypes.py        - mapping C types to Python types
        dlkmload.py         - loading DLKM debuginfo
        Generic.py          - generic useful subroutines/classes
        highlevel.py        - high-level readers for data
        logging.py          - log messages to be printed as summary
        lowlevel.py         - low-level readers for data
        memocaches.py       - memoization of types and data
        tparser.py*         - parse pieces of text in C syntax
        vmcorearch.py       - get vmcore arch specific info

Modules Interdependecy

In general, it is a bad idea for two modules to import each other as this can lead to circular import. Search in Google for ‘python circular imports’ to find many articles describing what could go wrong. The best approach is to combine two such modules into a single one. But this is not always desirable:

  • the resulting file will be bigger, more difficult to read and understand

  • it is not a good programming practice to put completely unrelated classes/subroutines into a single sourcefile - this makes maintenance difficult. If two developers work on logically different things but sourcecode is in one file, this is not convenient for commits/merges

It is OK to have some interdependencies if they are limited and implemented properly.

API.py

This is a main module to be used by program developers. It does several things:

  • imports all needed subroutines/classes/variables from other modules so that there is no need to do this manually

  • parses global options (such as timeout value for crash-builtins) and processes them as needed. These options are stripped from argument list passed to programs

  • re-initializes logging before each program run and prints summary of logged messages when program exits

__init__.py

This module contains version number of Python API and specifies a minimal version of C-module needed for this API to work.

If you added a new subroutine to C-module and this subroutine is used in your updated Python code, you cannot use an old C-module.

This is unimportant for end-users - those who rely on binary mpykdump.so module. But developers might pull new commits and try to run Python code without rebuilding C-module - and this can create problems.

To check for such problems, we compare C-module version (specififed in C-sources) and Python-API version (specified in __init__.py).

datatypes.py

This module defines classes used to represent information about types, structs, enumerations etc. to be used by high-level subroutines

We extract symbolic data using Python bindings to GDB internals (implemented in crash module, written in C) and then we need to convert this information to objects suitable for Python.

This modules mainly defines classes and some auxiliary subroutines; conversion from GDB data to instances of these classes is implemented in lowelevel.py

dlkmload.py

To access kernel symbols/structs defined in DLKMs, we need to load debuginfo as needed.

Depending on your distribution, these files can have different suffixes:

  • .ko.debug

  • .o.debug

  • .ko

  • .o

and they can be located in different directories. In addition, loading DLKM debuginfo might invalidate PyKdump caches (e.g. if struct with the same name is present both in DLKM and kernel).

This module provides a number of helper subroutines to load/unload this debuginfos for DLKMs

Generic.py

Useful subroutines not directly related to vmcore analysis: lazy evaluation, containers, registering handlers for module-level debugging

highlevel.py

File highlevel.py contains code that will be used by developers of programs. It is imported by API.py.

There are several logical groups of subroutines:

  • read data at a specific address (both virtual and physical)

  • read a specific global symbol

  • subroutines to work with lists

  • obtaining information about structs (e.g. member offsets)

  • executing built-in GDB and crash commands

logging.py

If your program produces lots of output, it is difficult to quickly find the important things. It usually makes sense to display a summary of all “important” findings (such as critical errors) after the end of normal output, when program exits.

logging.py implements PyLog class. It is a singleton, so doing:

pylog = PyLog()

in any of your own modules will use the same underlying data. Logging is reinitialized every time when you start a program and on program exit summary is displayed.

lowlevel.py

This module contains code to construct instances of classes defined in datatypes.py. This module is rather low-level, used internally by framework but not developers of programs. The contents of this module is used byt highlevel.py.

So highlevel.py imports from lowlevel.py but not vice versa.

For objects representing the contents of struct/union we need to implement struct field access/dereference.

struct/union fields can be of different type, so to implement such access we need to analyze the type of each field and use an appropriate subroutine. In PyKdump sources such subroutines are called readers. During analysis of specific struct/union type, we create and store readers for each field, so that they will be used for all structs of this type (results of analysis are cached).

Readers are implemented as closures, to preserve information about extra specifiers of field type. For example, for arrays the reader needs to take into account array dimensions. Factory functions for readers at this moment are:

  • ptrReader - reading pointers

  • suReader - reading structs/unions

  • ti_boolReader - reading booleans

  • ti_enumReader - reading enumerations

  • ti_intReader - reading all integer types

memocaches.py

Memoization classes and decorators, subroutines for caches maintenance

Some operations are quite CPU-expensive - for example, obtaining and analyzing symbolic info about structs/unions.

To improve the performance, it makes sense to cache the results, so that we would not repeat the expensive computations again and again.

Another group of CPU-intensive operations is related to executing build-in crash commands (e.g. kmem -s). Once again, it makes sense to cache the results.

Caching depends on whether we are running a live session or using vmcore (if we are using a live kernel, some things change with time).

Loading DLKM debuginfo might change structs definitions, so some caches should be invalidated after such operations.

ASCII-Art.py

ASCII-art for displaying tree-like structures.

tparser.py

In some cases, we cannot extract the needed information from debuginfo. This module implements simple parsers for C-text, so that we can copy definition from kernel sources (C) and convert it to format used by PyKdump. In particular, we can copy a block of #define statements and convert it to a dictionary.

vmcorearch.py

C-language definitions for integers are rather ambiguous - the size of long int can be either 4 bytes or 8 bytes. This depends on hardware used to run Linux, and we need this information to be able to read integers (and pointers) properly.

This module extracts from vmcore basic data needed to do the analysis:

  • arch-specific data (integers sizes etc.)

  • HZ, PAGESIZE, PAGE_CACHE_SHIFT, CPUS

  • kernel revision and directory of the vmcore

  • standard directories used for DLKM debuginfo search

  • checks whether this is vmcore or we are running on a live kernel