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 - 获取被调用者名称的主要内容,如果未能解决你的问题,请参考以下文章

php获取当前被调函数的参数列表

C/C++主调函数从被调函数中获取(各种类型)数据内容方式的梳理归纳

关于同步,异步,阻塞,非阻塞

被调用者分配被调用者释放

IO模型

NSManagedObjectContextDidSaveNotification 是不是会被调用以进行瞬态属性更新?