/* * Copyright (c) 2015, Technische Universität Kaiserslautern * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 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: * Janik Schlemminger * Robert Gernhardt * Matthias Jung * Felipe S. Prado * Derek Christ */ #include "pythoncaller.h" #include #include #include #include #include #include PythonCaller &PythonCaller::instance() { static PythonCaller instance; return instance; } PythonCaller::PythonCaller() : metricModuleName("metrics"), metricFunctionName("calculateMetrics"), getMetricFunctionName("getMetrics"), pathToScripts(QApplication::applicationDirPath().toStdString() + "/../../DRAMSys/traceAnalyzer/scripts/"), plotsModuleName("plots"), plotsFunctionName("generatePlots"), checkDependenciesModuleName("checkDependencies"), vcdExportModuleName("vcdExport"), vcdExportFunctionName("dumpVcd"), vcdExportDependenciesFunctionName("checkVcdExport") { Py_Initialize(); PyObject *sysPath = PySys_GetObject((char *)"path"); PyObject *path = PyUnicode_FromString(this->pathToScripts.c_str()); PyList_Insert(sysPath, 0, path); Py_DECREF(path); qDebug() << "Metric:" << metricModuleName.c_str() << metricFunctionName.c_str(); qDebug() << "Plot:" << plotsModuleName.c_str() << plotsFunctionName.c_str(); qDebug() << "VcdExport:" << vcdExportModuleName.c_str() << vcdExportFunctionName.c_str(); qDebug() << "Script: " << pathToScripts.c_str(); pCalculateMetricsFunction = loadFunctionFromModule(metricModuleName, metricFunctionName); pGenPlotsFunction = loadFunctionFromModule(plotsModuleName, plotsFunctionName); pGetMetricsFunction = loadFunctionFromModule(metricModuleName, getMetricFunctionName); pVcdExportDependenciesFunction = loadFunctionFromModule(checkDependenciesModuleName, vcdExportDependenciesFunctionName); if (vcdExportDependenciesAvailable()) pVcdExportFunction = loadFunctionFromModule(vcdExportModuleName, vcdExportFunctionName); else std::cerr << "Warning: Python module pyvcd or tqdm not installed! Exporting as VCD not possible." << std::endl; } //returns new reference to the function (see: http://edcjones.tripod.com/refcount.html for the difference between "new reference" and "borrowed reference") PyObject *PythonCaller::loadFunctionFromModule(std::string moduleName, std::string functionName) { PyObject *pModuleName = PyUnicode_FromString(moduleName.c_str()); PyObject *pModule = PyImport_Import(pModuleName); if (!pModule) { throw std::runtime_error(std::string("Could not load module " + moduleName)); } PyObject *pFunction = PyObject_GetAttrString(pModule, functionName.c_str()); if (!pFunction || !PyCallable_Check(pFunction)) { throw std::runtime_error( std::string("Could not load test function " + functionName + "in module " + moduleName)); } Py_DECREF(pModuleName); Py_DECREF(pModule); return pFunction; } PythonCaller::~PythonCaller() { Py_DECREF(pCalculateMetricsFunction); Py_DECREF(pGenPlotsFunction); Py_DECREF(pGetMetricsFunction); if (pVcdExportFunction) Py_DECREF(pVcdExportFunction); Py_DECREF(pVcdExportDependenciesFunction); Py_Finalize(); } PyObject *PythonCaller::callMetricsFunction(PyObject *function, QString argument, std::vector list) { assert(PyCallable_Check(function)); PyObject *pArgs = PyTuple_New(2); PyObject *pArgumentString = PyUnicode_FromString( argument.toStdString().c_str()); PyObject *pArgumentList = PyList_New(list.size()); for (size_t i = 0; i < list.size(); i++) { PyList_SetItem(pArgumentList, i, PyBool_FromLong(list[i])); } PyTuple_SetItem(pArgs, 0, pArgumentString); PyTuple_SetItem(pArgs, 1, pArgumentList); PyObject *pResult = PyObject_CallObject(function, pArgs); Py_DECREF(pArgs); if (!pResult) { PyErr_Print(); throw std::runtime_error( std::string("Error in calling " + metricFunctionName + " in module " + metricModuleName)); } return pResult; } //returns a new reference to result of function call PyObject *PythonCaller::callFunctionWithStringArgument(PyObject *function, QString argument) { assert(PyCallable_Check(function)); PyObject *pArgs = PyTuple_New(1); PyObject *pArgument = PyUnicode_FromString(argument.toStdString().c_str()); PyTuple_SetItem(pArgs, 0, pArgument); PyObject *pResult = PyObject_CallObject(function, pArgs); Py_DECREF(pArgument); if (!pResult) { PyErr_Print(); throw std::runtime_error(std::string("Error in calling function with string argument")); } return pResult; } PyObject *PythonCaller::callFunctionWithoutArguments(PyObject *function) { assert(PyCallable_Check(function)); PyObject *pResult = PyObject_CallObject(function, NULL); if (!pResult) { PyErr_Print(); throw std::runtime_error(std::string("Error in calling python function")); } return pResult; } TraceCalculatedMetrics PythonCaller::calculateMetricsOnTrace(QString pathToTrace, std::vector list) { TraceCalculatedMetrics result(QFileInfo(pathToTrace).baseName()); PyObject *pResult = callMetricsFunction(pCalculateMetricsFunction, pathToTrace, list); for (Py_ssize_t i = 0; i < PyList_Size(pResult); ++i) { PyObject *calculatedMetric = PyList_GetItem(pResult, i); QString metricName(PyUnicode_AsUTF8(PyTuple_GetItem(calculatedMetric, 0))); double value = PyFloat_AsDouble(PyTuple_GetItem(calculatedMetric, 1)); result.addCalculatedMetric(CalculatedMetric(metricName, value)); } Py_DECREF(pResult); return result; } std::vector PythonCaller::getMetrics(QString pathToTrace) { std::vector result; PyObject *pResult = callFunctionWithStringArgument(pGetMetricsFunction, pathToTrace); for (Py_ssize_t i = 0; i < PyList_Size(pResult); ++i) { PyObject *metric = PyList_GetItem(pResult, i); QString metricName(PyUnicode_AsUTF8(metric)); result.push_back(metricName.toStdString().c_str()); } Py_DECREF(pResult); return result; } QString PythonCaller::generatePlotsOnTrace(QString pathToTrace) { assert(PyCallable_Check(pGenPlotsFunction)); PyObject *pResult = callFunctionWithStringArgument(pGenPlotsFunction, pathToTrace); QString outputFiles (PyUnicode_AsUTF8(pResult)); Py_DECREF(pResult); return outputFiles; } QString PythonCaller::exportAsVcd(QString pathToTrace) { if (!pVcdExportFunction) return QString(); PyObject *pResult = callFunctionWithStringArgument(pVcdExportFunction, pathToTrace); QString dump(PyUnicode_AsUTF8(pResult)); Py_DECREF(pResult); return dump; } bool PythonCaller::vcdExportDependenciesAvailable() { PyObject *result = callFunctionWithoutArguments(pVcdExportDependenciesFunction); bool available = PyObject_IsTrue(result); Py_DECREF(result); return available; }