24.22.3.1. gem5 m5.objects module

All SimObjects seem to be automatically added to the m5.objects namespace, and this is done in a very convoluted way, let’s try to understand a bit:

src/python/m5/objects/__init__.py

contains:

modules = __loader__.modules

for module in modules.keys():
    if module.startswith('m5.objects.'):
        exec("from %s import *" % module)

And from IPDB we see that this appears to loop over every object string of type m5.objects.modulename.

This __init__ gets called from src/python/importer.py at the exec:

class CodeImporter(object):
    def load_module(self, fullname):
            override = os.environ.get('M5_OVERRIDE_PY_SOURCE', 'false').lower()
            if override in ('true', 'yes') and  os.path.exists(abspath):
                src = open(abspath, 'r').read()
                code = compile(src, abspath, 'exec')

            if os.path.basename(srcfile) == '__init__.py':
                mod.__path__ = fullname.split('.')
                mod.__package__ = fullname
            else:
                mod.__package__ = fullname.rpartition('.')[0]
            mod.__file__ = srcfile

            exec(code, mod.__dict__)

import sys
importer = CodeImporter()
add_module = importer.add_module
sys.meta_path.append(importer)

Here as a bonus here we also see how M5_OVERRIDE_PY_SOURCE works.

In src/SConscript we see that SimObject is just a PySource with module equals to m5.objects:

class SimObject(PySource):
    def __init__(self, source, tags=None, add_tags=None):
        '''Specify the source file and any tags (automatically in
        the m5.objects package)'''
        super(SimObject, self).__init__('m5.objects', source, tags, add_tags)

The add_module method seems to be doing the magic and is called from src/sim/init.cc:

bool
EmbeddedPython::addModule() const
{
    PyObject *code = getCode();
    PyObject *result = PyObject_CallMethod(importerModule, PyCC("add_module"),

which is called from:

int
EmbeddedPython::initAll()
{
    // Load the importer module
    PyObject *code = importer->getCode();
    importerModule = PyImport_ExecCodeModule(PyCC("importer"), code);
    if (!importerModule) {
        PyErr_Print();
        return 1;
    }

    // Load the rest of the embedded python files into the embedded
    // python importer
    list<EmbeddedPython *>::iterator i = getList().begin();
    list<EmbeddedPython *>::iterator end = getList().end();
    for (; i != end; ++i)
        if (!(*i)->addModule())

and getList comes from:

EmbeddedPython::EmbeddedPython(const char *filename, const char *abspath,
    const char *modpath, const unsigned char *code, int zlen, int len)
    : filename(filename), abspath(abspath), modpath(modpath), code(code),
      zlen(zlen), len(len)
{
    // if we've added the importer keep track of it because we need it
    // to bootstrap.
    if (string(modpath) == string("importer"))
        importer = this;
    else
        getList().push_back(this);
}

list<EmbeddedPython *> &
EmbeddedPython::getList()
{
    static list<EmbeddedPython *> the_list;
    return the_list;
}

and the constructor in turn gets called from per SimObject autogenerated files such as e.g. dev/storage/Ide.py.cc for src/dev/storage/Ide.py:

EmbeddedPython embedded_m5_objects_Ide(
    "m5/objects/Ide.py",
    "/home/ciro/bak/git/linux-kernel-module-cheat/data/gem5/master4/src/dev/storage/Ide.py",
    "m5.objects.Ide",
    data_m5_objects_Ide,
    947,
    2099);

} // anonymous namespace

which get autogenerated at src/SConscript:

def embedPyFile(target, source, env):

for source in PySource.all:
    base_py_env.Command(source.cpp, [ py_marshal, source.tnode ],
                        MakeAction(embedPyFile, Transform("EMBED PY")))

where the PySource.all thing as you might expect is a static list of all PySource source files as they get updated in the constructor.

Tested in gem5 d9cb548d83fa81858599807f54b52e5be35a6b03.