I'm trying to implement a simple QtScript performance profiler via QScriptEngineAgent
, by catching function entries and exits. I successfully subscribed to QScriptEngineAgent::functionEntry()
callback. Now, is it possible to obtain the name (as string) of the function which is being called, inside this callback?
Even though I'm aware that not all script functions need to have a name, even in the most simple cases it doesn't seem to work. QScriptContextInfo
provides facilities for that, but it seems to fail. Then I'm trying to obtain the value of arguments.callee.name
property, but it fails as well.
Here's a rough overview of my attempts to implement that, which I'm trying to run on qt-5.3.2/linux.
tmp.pro
:
TEMPLATE = app
TARGET = tmp
INCLUDEPATH += .
QT += core script
SOURCES += main.cpp
main.cpp
:
#include <QCoreApplication>
#include <QScriptEngine>
#include <QScriptEngineAgent>
#include <QScriptContextInfo>
#include <QDebug>
class MyAgent: public QScriptEngineAgent {
private:
void functionEntry(qint64 scriptId) {
qDebug() << "functionEntry" << scriptId;
QScriptContext *context = engine()->currentContext();
// QScriptContextInfo should have function name, by design, afaik
QScriptContextInfo contextInfo(context);
qDebug() << contextInfo.functionName();
// probably my typical js-side arguments.callee.name would work?
QScriptValue callee = context->callee();
qDebug() << callee.property("name").toString();
// function.toString() should have at least something (?)
qDebug() << callee.toString();
// hmm. what's our context, anyway?
qDebug() << context->toString();
}
public:
MyAgent(QScriptEngine *eng) : QScriptEngineAgent(eng) {
qDebug() << "engine" << eng;
}
};
int main(int argc, char **argv) {
QCoreApplication app(argc, argv);
QScriptEngine eng;
MyAgent agent(&eng);
eng.setAgent(&agent);
qDebug() << "agent " << eng.agent();
eng.evaluate("function foo() { return 6 * 7; }"
"function bar() { return foo(); }");
QScriptValue bar = eng.globalObject().property("bar");
// See? Here the callee is printed as expected.
qDebug() << "call " << bar.property("name").toString() << bar.toString();
QScriptValue ret = bar.call();
qDebug() << "answer" << ret.toNumber();
return 0;
}
Output sample, which I'm not satisfied with, as I'm expecting to see "foo" and "bar" instead of some of the empty strings:
engine QScriptEngine(0x7fffc55c4560)
agent 0x7fffc55c4570
functionEntry 140300485581200
""
""
""
"<global>() at -1"
functionEntry -1
""
""
""
"<global>() at -1"
call "bar" "function bar() { return foo(); }"
functionEntry 140300485581200
""
""
""
"<global>() at -1"
functionEntry 140300485581200
""
""
""
"<global>() at -1"
answer 42
It seems that a correct context is only available in positionChange. Luckily functionEntry is called with a scriptID > 0 whenever a true context-change occurs. Grabbing the context in the next positionChange works for me:
To use the agent you simply set it on the engine with
engine.setAgent(new ScriptProfilerAgent());
. After execution my poor man's profiler can give you a list of function/stackpathnames with cumulated execution times and number of calls:Cheers,
Johannes