Switch to pybind11
With the switch to pybind11, the complexity of the Python integration in the TraceAnalyzer can be greatly reduced. The new code is much easier to understand and fixes a number of bugs regarding the Python integration.
This commit is contained in:
@@ -41,205 +41,78 @@
|
||||
#include <exception>
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <QFileInfo>
|
||||
#include <QDebug>
|
||||
#include <QApplication>
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
|
||||
PythonCaller::PythonCaller() :
|
||||
metricModuleName("metrics"),
|
||||
metricFunctionName("calculateMetrics"),
|
||||
getMetricFunctionName("getMetrics"),
|
||||
|
||||
pathToScripts(QApplication::applicationDirPath().toStdString() +
|
||||
"/../../extensions/apps/traceAnalyzer/scripts/"),
|
||||
plotsModuleName("plots"),
|
||||
plotsFunctionName("generatePlots"),
|
||||
checkDependenciesModuleName("checkDependencies"),
|
||||
vcdExportModuleName("vcdExport"),
|
||||
vcdExportFunctionName("dumpVcd"),
|
||||
vcdExportDependenciesFunctionName("checkVcdExport")
|
||||
std::string PythonCaller::generatePlots(std::string_view pathToTrace)
|
||||
{
|
||||
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));
|
||||
try
|
||||
{
|
||||
pybind11::module_ metricsModule = pybind11::module_::import("plots");
|
||||
auto result = metricsModule.attr("generatePlots")(pathToTrace).cast<std::string>();
|
||||
return result;
|
||||
}
|
||||
catch (std::exception const &err)
|
||||
{
|
||||
std::cout << err.what() << std::endl;
|
||||
}
|
||||
|
||||
PyObject *pFunction = PyObject_GetAttrString(pModule, functionName.c_str());
|
||||
return {};
|
||||
}
|
||||
|
||||
if (!pFunction || !PyCallable_Check(pFunction)) {
|
||||
throw std::runtime_error(
|
||||
std::string("Could not load test function " + functionName + "in module " + moduleName));
|
||||
std::vector<std::string> PythonCaller::availableMetrics(std::string_view pathToTrace)
|
||||
{
|
||||
try
|
||||
{
|
||||
pybind11::module_ metricsModule = pybind11::module_::import("metrics");
|
||||
pybind11::list result = metricsModule.attr("getMetrics")(pathToTrace);
|
||||
return result.cast<std::vector<std::string>>();
|
||||
}
|
||||
catch (std::exception const &err)
|
||||
{
|
||||
std::cout << err.what() << std::endl;
|
||||
}
|
||||
|
||||
Py_DECREF(pModuleName);
|
||||
Py_DECREF(pModule);
|
||||
return pFunction;
|
||||
return {};
|
||||
}
|
||||
|
||||
PythonCaller::~PythonCaller()
|
||||
TraceCalculatedMetrics PythonCaller::evaluateMetrics(std::string_view pathToTrace, std::vector<long> selectedMetrics)
|
||||
{
|
||||
Py_DECREF(pCalculateMetricsFunction);
|
||||
Py_DECREF(pGenPlotsFunction);
|
||||
Py_DECREF(pGetMetricsFunction);
|
||||
TraceCalculatedMetrics metrics(pathToTrace.data());
|
||||
|
||||
if (pVcdExportFunction)
|
||||
Py_DECREF(pVcdExportFunction);
|
||||
try
|
||||
{
|
||||
pybind11::module_ metricsModule = pybind11::module_::import("metrics");
|
||||
pybind11::list result = metricsModule.attr("calculateMetrics")(pathToTrace, selectedMetrics);
|
||||
auto metricList = result.cast<std::vector<pybind11::tuple>>();
|
||||
|
||||
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]));
|
||||
for (auto metricPair : metricList)
|
||||
{
|
||||
std::string name = metricPair[0].cast<std::string>();
|
||||
double value = metricPair[1].cast<double>();
|
||||
metrics.addCalculatedMetric({name, value});
|
||||
}
|
||||
}
|
||||
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));
|
||||
catch (std::exception const &err)
|
||||
{
|
||||
std::cout << err.what() << std::endl;
|
||||
}
|
||||
|
||||
return pResult;
|
||||
return metrics;
|
||||
}
|
||||
|
||||
//returns a new reference to result of function call
|
||||
PyObject *PythonCaller::callFunctionWithStringArgument(PyObject *function,
|
||||
QString argument)
|
||||
std::string PythonCaller::dumpVcd(std::string_view pathToTrace)
|
||||
{
|
||||
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"));
|
||||
try
|
||||
{
|
||||
pybind11::module_ vcdModule = pybind11::module_::import("vcdExport");
|
||||
pybind11::str result = vcdModule.attr("dumpVcd")(pathToTrace);
|
||||
return result.cast<std::string>();
|
||||
}
|
||||
catch (std::exception const &err)
|
||||
{
|
||||
std::cout << err.what() << std::endl;
|
||||
}
|
||||
|
||||
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;
|
||||
return {};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user