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.