QScriptEngineAgent - 获取被调用者名称
Posted
技术标签:
【中文标题】QScriptEngineAgent - 获取被调用者名称【英文标题】:QScriptEngineAgent - obtaining callee name 【发布时间】:2014-11-22 07:50:34 【问题描述】:我正在尝试通过QScriptEngineAgent
实现一个简单的QtScript 性能分析器,方法是捕获函数入口和出口。我成功订阅了QScriptEngineAgent::functionEntry()
回调。现在,是否可以在此回调中获取正在调用的函数的名称(作为字符串)?
尽管我知道并非所有脚本函数都需要有一个名称,即使在最简单的情况下它似乎也不起作用。 QScriptContextInfo
为此提供了便利,但似乎失败了。然后我试图获取arguments.callee.name
属性的值,但它也失败了。
这是我尝试实现它的粗略概述,我试图在 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;
我不满意的输出样本,因为我希望看到“foo”和“bar”而不是一些空字符串:
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
【问题讨论】:
嗯。 qt-project.org/wiki/Qt_Modules_Maturity_Level "QScriptEngineAgent 和相关类状态:已弃用推理:有缺陷的设计,正在被更好的设计所取代。" 【参考方案1】:似乎正确的上下文仅在 positionChange 中可用。幸运的是,只要发生真正的上下文更改,就会使用 scriptID > 0 调用 functionEntry。在下一个 positionChange 中抓取上下文对我有用:
class ScriptProfilerAgent: public QScriptEngineAgent
public:
ScriptProfilerAgent(QScriptEngine *eng)
: QScriptEngineAgent(eng), m_stackChange(false)
protected:
void scriptLoad(qint64 id, const QString &program, const QString &fileName, int baseLineNumber)
Q_UNUSED(id);Q_UNUSED(program);Q_UNUSED(fileName);Q_UNUSED(baseLineNumber);
m_timer.start();
void functionEntry(qint64 scriptId)
if (scriptId > -1)
// arm the stack-tracker => positionChange
m_stackChange = true;
void positionChange(qint64 scriptId, int lineNumber, int columnNumber)
Q_UNUSED(scriptId);Q_UNUSED(lineNumber);Q_UNUSED(columnNumber);
if (m_stackChange)
QString fn = functionNameEx();
m_curCallStack.push(qMakePair(fn,m_timer.elapsed()));
// reset stack tracking
m_stackChange = false;
QString functionNameEx()
QScriptContext* sc = engine()->currentContext();
QScriptContextInfo info(sc);
QString functionName = info.functionName();
if (functionName.isEmpty())
if (sc->parentContext())
switch (info.functionType())
case QScriptContextInfo::ScriptFunction : return QLatin1String("anonymous");
case QScriptContextInfo::QtFunction : return QLatin1String("qtfunction");
case QScriptContextInfo::QtPropertyFunction : return QLatin1String("qtproperty");
case QScriptContextInfo::NativeFunction : return QLatin1String("native");
else
return QLatin1String("global");
else
return functionName;
return "??";
void functionExit(qint64 scriptId, const QScriptValue &returnValue)
Q_UNUSED(returnValue);
if (scriptId > -1)
if (!m_curCallStack.isEmpty())
// store current fn information
QPair<QString,qint64> f = m_curCallStack.pop();
qint64 execTime = m_timer.elapsed()-f.second;
FunctionStats& stats = m_callStatsByName[f.first];
stats.calls++;
stats.execTimeSum += execTime;
// build full callstack-path
QStringList path;path.reserve(m_curCallStack.size());
for (int i=0;i<m_curCallStack.size();++i)
path.append(m_curCallStack.at(i).first);
path.append(f.first);
FunctionStats& stats = m_callStatsByPath[path.join(">")];
stats.calls++;
stats.execTimeSum += execTime;
else
qDebug() << "Something is very wrong with the call stack!";
public:
QString toString() const
QStringList result;
result.append("Function\tTotal Time (ms)\tCalls");
// function name
QStringList fnsByExecTimeSum = m_callStatsByName.keys();
// sorted by execTimeSum
std::sort(fnsByExecTimeSum.begin(),fnsByExecTimeSum.end(),
[this](const QString& a,const QString& b) -> bool return (m_callStatsByName[a].execTimeSum) > (m_callStatsByName[b].execTimeSum);
);
for (QString fn : fnsByExecTimeSum)
result.append(QString("%1\t%2ms\t%3").arg(fn).arg(m_callStatsByName[fn].execTimeSum).arg(m_callStatsByName[fn].calls));
result.append("");
result.append("FN-Path\tTotal Time (ms)\tCalls");
// function path = call stack
QStringList fns = m_callStatsByPath.keys();
// sorted by key
fns.sort();
for (QString fn : fns)
result.append(QString("%1\t%2ms\t%3").arg(fn).arg(m_callStatsByPath[fn].execTimeSum).arg(m_callStatsByPath[fn].calls));
return result.join("\n");
void reset()
m_callStatsByPath.clear();
m_callStatsByName.clear();
private:
QStack<QPair<QString,qint64> > m_curCallStack;
bool m_stackChange;
struct FunctionStats
FunctionStats() : calls(0),execTimeSum(0)
int calls;
qint64 execTimeSum; // ms
;
QElapsedTimer m_timer;
QHash<QString, FunctionStats> m_callStatsByPath;
QHash<QString, FunctionStats> m_callStatsByName;
;
要使用代理,您只需使用engine.setAgent(new ScriptProfilerAgent());
在引擎上设置它。执行后,我可怜的分析器可以为您提供函数/堆栈路径名称列表,其中包含累积的执行时间和调用次数:
QString ScriptEngine::getProfilingResults()
ScriptProfilerAgent* spa = dynamic_cast<ScriptProfilerAgent*>(agent());
if (spa)
QString result = spa->toString();
spa->reset();
return result;
return "<no profiler agent>";
FN-Path Total Time (ms) Calls
global 235969ms 7
global>anonymous 0ms 38
global>run 235969ms 1
global>run>doConcept 206444ms 21603
global>run>doConcept>genData 200765ms 21603
global>run>doConcept>genData>genArticleData 198861ms 21603
global>run>doConcept>genData>genArticleData>buildInnerhtmls 163558ms 21603
global>run>doConcept>genData>genArticleData>buildInnerHTMLs>anonymous 87582ms 1823593
....
干杯,
约翰内斯
【讨论】:
非常感谢您的回答,我现在有点忙,但我确信我会尽快检查尝试解决方案,并且很可能将答案标记为已接受: )以上是关于QScriptEngineAgent - 获取被调用者名称的主要内容,如果未能解决你的问题,请参考以下文章