Files
DRAMSys/extensions/apps/traceAnalyzer/businessObjects/pythoncaller.cpp

252 lines
8.8 KiB
C++

/*
* 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 <exception>
#include <string>
#include <iostream>
#include <QFileInfo>
#include <QDebug>
#include <QApplication>
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<long> 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<long> 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<std::string> PythonCaller::getMetrics(QString pathToTrace)
{
std::vector<std::string> 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;
}