base: Tag API methods in amo.hh
[gem5.git] / src / sim / init.cc
index 66eddfb6fa1dc919b930f8051717d14e6224cd10..7b2e7e4a86a43105287df9daf4c865f674be0f1b 100644 (file)
@@ -1,4 +1,16 @@
 /*
+ * Copyright (c) 2012, 2017 ARM Limited
+ * All rights reserved
+ *
+ * The license below extends only to copyright in the software and shall
+ * not be construed as granting a license to any other intellectual
+ * property including but not limited to intellectual property relating
+ * to a hardware implementation of the functionality of the software
+ * licensed hereunder.  You may use the software subject to the license
+ * terms below provided that you ensure that this notice is replicated
+ * unmodified and in its entirety in all distributions of the software,
+ * modified or unmodified, in source code or in binary form.
+ *
  * Copyright (c) 2000-2005 The Regents of The University of Michigan
  * Copyright (c) 2008 The Hewlett-Packard Development Company
  * All rights reserved.
  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * Authors: Nathan Binkert
  */
 
 #include <Python.h>
+
+#include "sim/init.hh"
+
 #include <marshal.h>
-#include <signal.h>
+#include <zlib.h>
 
 #include <iostream>
+#include <list>
 #include <string>
-#include <zlib.h>
+#include <vector>
 
 #include "base/cprintf.hh"
-#include "base/misc.hh"
+#include "base/logging.hh"
+#include "base/types.hh"
+#include "config/have_protobuf.hh"
+#include "python/pybind11/pybind.hh"
 #include "sim/async.hh"
 #include "sim/core.hh"
-#include "sim/host.hh"
-#include "sim/init.hh"
 
-using namespace std;
+#if HAVE_PROTOBUF
+#include <google/protobuf/stubs/common.h>
 
-/// Stats signal handler.
-void
-dumpStatsHandler(int sigtype)
-{
-    async_event = true;
-    async_statdump = true;
-}
+#endif
 
-void
-dumprstStatsHandler(int sigtype)
-{
-    async_event = true;
-    async_statdump = true;
-    async_statreset = true;
-}
+using namespace std;
+namespace py = pybind11;
 
-/// Exit signal handler.
-void
-exitNowHandler(int sigtype)
-{
-    async_event = true;
-    async_exit = true;
-}
+// The python library is totally messed up with respect to constness,
+// so make a simple macro to make life a little easier
+#define PyCC(x) (const_cast<char *>(x))
 
-/// Abort signal handler.
-void
-abortHandler(int sigtype)
+EmbeddedPython *EmbeddedPython::importer = NULL;
+PyObject *EmbeddedPython::importerModule = NULL;
+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)
 {
-    ccprintf(cerr, "Program aborted at cycle %d\n", curTick);
+    // 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);
 }
 
-/*
- * M5 can do several special things when various signals are sent.
- * None are mandatory.
- */
-void
-initSignals()
+list<EmbeddedPython *> &
+EmbeddedPython::getList()
 {
-    // Floating point exceptions may happen on misspeculated paths, so
-    // ignore them
-    signal(SIGFPE, SIG_IGN);
-
-    // We use SIGTRAP sometimes for debugging
-    signal(SIGTRAP, SIG_IGN);
-
-    // Dump intermediate stats
-    signal(SIGUSR1, dumpStatsHandler);
-
-    // Dump intermediate stats and reset them
-    signal(SIGUSR2, dumprstStatsHandler);
-
-    // Exit cleanly on Interrupt (Ctrl-C)
-    signal(SIGINT, exitNowHandler);
-
-    // Print out cycle number on abort
-    signal(SIGABRT, abortHandler);
+    static list<EmbeddedPython *> the_list;
+    return the_list;
 }
 
 /*
  * Uncompress and unmarshal the code object stored in the
- * EmbeddedPyModule
+ * EmbeddedPython
  */
 PyObject *
-getCode(const EmbeddedPyModule *pymod)
+EmbeddedPython::getCode() const
 {
-    assert(pymod->zlen == pymod->code_end - pymod->code);
-    Bytef *marshalled = new Bytef[pymod->mlen];
-    uLongf unzlen = pymod->mlen;
-    int ret = uncompress(marshalled, &unzlen, (const Bytef *)pymod->code,
-        pymod->zlen);
+    Bytef marshalled[len];
+    uLongf unzlen = len;
+    int ret = uncompress(marshalled, &unzlen, (const Bytef *)code, zlen);
     if (ret != Z_OK)
         panic("Could not uncompress code: %s\n", zError(ret));
-    assert(unzlen == pymod->mlen);
+    assert(unzlen == (uLongf)len);
 
-    return PyMarshal_ReadObjectFromString((char *)marshalled, pymod->mlen);
+    return PyMarshal_ReadObjectFromString((char *)marshalled, len);
 }
 
-// The python library is totally messed up with respect to constness,
-// so make a simple macro to make life a little easier
-#define PyCC(x) (const_cast<char *>(x))
+bool
+EmbeddedPython::addModule() const
+{
+    PyObject *code = getCode();
+    PyObject *result = PyObject_CallMethod(importerModule, PyCC("add_module"),
+        PyCC("sssO"), filename, abspath, modpath, code);
+    if (!result) {
+        PyErr_Print();
+        return false;
+    }
+
+    Py_DECREF(result);
+    return true;
+}
 
 /*
- * Load and initialize all of the python parts of M5, including Swig
- * and the embedded module importer.
+ * Load and initialize all of the python parts of M5.
  */
 int
-initM5Python()
+EmbeddedPython::initAll()
 {
-    extern void initSwig();
-
-    // initialize SWIG modules.  initSwig() is autogenerated and calls
-    // all of the individual swig initialization functions.
-    initSwig();
-
     // Load the importer module
-    PyObject *code = getCode(&embeddedPyImporter);
-    PyObject *module = PyImport_ExecCodeModule(PyCC("importer"), code);
-    if (!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
-    const EmbeddedPyModule *pymod = &embeddedPyModules[0];
-    while (pymod->filename) {
-        PyObject *code = getCode(pymod);
-        PyObject *result = PyObject_CallMethod(module, PyCC("add_module"),
-            PyCC("ssO"), pymod->filename, pymod->modpath, code);
-        if (!result) {
-            PyErr_Print();
+    list<EmbeddedPython *>::iterator i = getList().begin();
+    list<EmbeddedPython *>::iterator end = getList().end();
+    for (; i != end; ++i)
+        if (!(*i)->addModule())
             return 1;
+
+    return 0;
+}
+
+EmbeddedPyBind::EmbeddedPyBind(const char *_name,
+                               void (*init_func)(py::module &),
+                               const char *_base)
+    : initFunc(init_func), registered(false), name(_name), base(_base)
+{
+    getMap()[_name] = this;
+}
+
+EmbeddedPyBind::EmbeddedPyBind(const char *_name,
+                               void (*init_func)(py::module &))
+    : initFunc(init_func), registered(false), name(_name), base("")
+{
+    getMap()[_name] = this;
+}
+
+void
+EmbeddedPyBind::init(py::module &m)
+{
+    if (!registered) {
+        initFunc(m);
+        registered = true;
+    } else {
+        cprintf("Warning: %s already registered.\n", name);
+    }
+}
+
+bool
+EmbeddedPyBind::depsReady() const
+{
+    return base.empty() || getMap()[base]->registered;
+}
+
+std::map<std::string, EmbeddedPyBind *> &
+EmbeddedPyBind::getMap()
+{
+    static std::map<std::string, EmbeddedPyBind *> objs;
+    return objs;
+}
+
+#if PY_MAJOR_VERSION >= 3
+PyObject *
+#else
+void
+#endif
+EmbeddedPyBind::initAll()
+{
+    std::list<EmbeddedPyBind *> pending;
+
+    py::module m_m5 = py::module("_m5");
+    m_m5.attr("__package__") = py::cast("_m5");
+
+    pybind_init_core(m_m5);
+    pybind_init_debug(m_m5);
+
+    pybind_init_event(m_m5);
+    pybind_init_stats(m_m5);
+
+    for (auto &kv : getMap()) {
+        auto &obj = kv.second;
+        if (obj->base.empty()) {
+            obj->init(m_m5);
+        } else {
+            pending.push_back(obj);
         }
-        Py_DECREF(result);
-        ++pymod;
     }
 
-    return 0;
+    while (!pending.empty()) {
+        for (auto it = pending.begin(); it != pending.end(); ) {
+            EmbeddedPyBind &obj = **it;
+            if (obj.depsReady()) {
+                obj.init(m_m5);
+                it = pending.erase(it);
+            } else {
+                ++it;
+            }
+        }
+    }
+
+#if PY_MAJOR_VERSION >= 3
+    return m_m5.ptr();
+#endif
+}
+
+void
+registerNativeModules()
+{
+    auto result = PyImport_AppendInittab("_m5", EmbeddedPyBind::initAll);
+    if (result == -1)
+        panic("Failed to add _m5 to Python's inittab\n");
 }
 
+/*
+ * Make the commands array weak so that they can be overridden (used
+ * by unit tests to specify a different python main function.
+ */
+const char * __attribute__((weak)) m5MainCommands[] = {
+    "import m5",
+    "m5.main()",
+    0 // sentinel is required
+};
+
 /*
  * Start up the M5 simulator.  This mostly vectors into the python
  * main function.
  */
 int
-m5Main(int argc, char **argv)
+m5Main(int argc, char **_argv)
 {
+#if HAVE_PROTOBUF
+    // Verify that the version of the protobuf library that we linked
+    // against is compatible with the version of the headers we
+    // compiled against.
+    GOOGLE_PROTOBUF_VERIFY_VERSION;
+#endif
+
+
+#if PY_MAJOR_VERSION >= 3
+    typedef std::unique_ptr<wchar_t[], decltype(&PyMem_RawFree)> WArgUPtr;
+    std::vector<WArgUPtr> v_argv;
+    std::vector<wchar_t *> vp_argv;
+    v_argv.reserve(argc);
+    vp_argv.reserve(argc);
+    for (int i = 0; i < argc; i++) {
+        v_argv.emplace_back(Py_DecodeLocale(_argv[i], NULL), &PyMem_RawFree);
+        vp_argv.emplace_back(v_argv.back().get());
+    }
+
+    wchar_t **argv = vp_argv.data();
+#else
+    char **argv = _argv;
+#endif
+
     PySys_SetArgv(argc, argv);
 
     // We have to set things up in the special __main__ module
@@ -183,27 +292,24 @@ m5Main(int argc, char **argv)
 
     // import the main m5 module
     PyObject *result;
-    result = PyRun_String("import m5", Py_file_input, dict, dict);
-    if (!result) {
-        PyErr_Print();
-        return 1;
-    }
-    Py_DECREF(result);
+    const char **command = m5MainCommands;
 
-    // Start m5
-    result = PyRun_String("m5.main()", Py_file_input, dict, dict);
-    if (!result) {
-        PyErr_Print();
-        return 1;
+    // evaluate each command in the m5MainCommands array (basically a
+    // bunch of python statements.
+    while (*command) {
+        result = PyRun_String(*command, Py_file_input, dict, dict);
+        if (!result) {
+            PyErr_Print();
+            return 1;
+        }
+        Py_DECREF(result);
+
+        command++;
     }
-    Py_DECREF(result);
 
-    return 0;
-}
+#if HAVE_PROTOBUF
+    google::protobuf::ShutdownProtobufLibrary();
+#endif
 
-PyMODINIT_FUNC
-initm5(void)
-{
-    initM5Python();
-    PyImport_ImportModule(PyCC("m5"));
+    return 0;
 }