/* * Copyright (c) 2015, University of 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 */ #include "pythoncaller.h" #include #include #include #include #include #include using namespace std; PythonCaller::PythonCaller() : testModuleName("tests"), testFunctionName("runTests"), metricModuleName("metrics"), metricFunctionName("calculateMetrics"), getMetricFunctionName("getMetrics"), pathToScripts(QApplication::applicationDirPath().toStdString() + "/../../DRAMSys/analyzer/scripts/"), plotsModuleName("plots"), plotsFunctionName("generatePlots") { 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() << "Test:" << testModuleName.c_str() << testFunctionName.c_str(); qDebug() << "Metric:" << metricModuleName.c_str() << metricFunctionName.c_str(); qDebug() << "Plot:" << plotsModuleName.c_str() << plotsFunctionName.c_str(); qDebug() << "Script: " << pathToScripts.c_str(); pRunTestsFunction = loadFunctionFromModule(testModuleName, testFunctionName); pCalculateMetricsFunction = loadFunctionFromModule(metricModuleName, metricFunctionName); pGenPlotsFunction = loadFunctionFromModule(plotsModuleName, plotsFunctionName); pGetMetricsFunction = loadFunctionFromModule(metricModuleName, getMetricFunctionName); } //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(string moduleName,string functionName) { PyObject* pModuleName = PyUnicode_FromString(moduleName.c_str()); PyObject* pModule = PyImport_Import(pModuleName); if(!pModule) { throw runtime_error(string("Could not load module "+ moduleName)); } PyObject* pFunction = PyObject_GetAttrString(pModule, functionName.c_str()); if(!pFunction || !PyCallable_Check(pFunction)) { throw runtime_error(string("Could not load test function " + functionName + "in module " + moduleName)); } Py_DECREF(pModuleName); Py_DECREF(pModule); return pFunction; } PythonCaller::~PythonCaller() { Py_DECREF(pRunTestsFunction); Py_DECREF(pCalculateMetricsFunction); Py_DECREF(pGenPlotsFunction); Py_DECREF(pGetMetricsFunction); Py_Finalize(); } PyObject* PythonCaller::callMetricsFunction(PyObject* function, QString argument, 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 runtime_error(string("Error in calling " + testFunctionName + " in module " + testModuleName)); } 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 runtime_error(string("Error in calling " + testFunctionName + " in module " + testModuleName)); } return pResult; } TraceTestResults PythonCaller::runTestsOnTrace(QString pathToTrace) { TraceTestResults traceTestResult(QFileInfo(pathToTrace).baseName()); PyObject *pResult = callFunctionWithStringArgument(pRunTestsFunction, pathToTrace); for(Py_ssize_t i= 0; i < PyList_Size(pResult); ++i) { PyObject* currentTestResult = PyList_GetItem(pResult,i); QString testName(PyUnicode_AsUTF8(PyTuple_GetItem(currentTestResult,0))); bool testPassed = (Py_True == PyTuple_GetItem(currentTestResult,1)); QString message(PyUnicode_AsUTF8(PyTuple_GetItem(currentTestResult,2))); traceTestResult.addTestResult(TestResult(testName,testPassed,message)); } Py_DECREF(pResult); return traceTestResult; } TraceCalculatedMetrics PythonCaller::calculateMetricsOnTrace(QString pathToTrace, 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; } vector PythonCaller::getMetrics(QString pathToTrace) { 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; }