2014-11-22 41 views
0

我试图通过捕获函数条目并退出,通过QScriptEngineAgent实现简单的QtScript性能分析器。我成功订阅了QScriptEngineAgent::functionEntry()回调。现在,是否有可能获得被调用的函数的名称(字符串),在这个回调中?QScriptEngineAgent - 获取被调用者名称

即使我知道并非所有的脚本函数都需要有一个名字,即使在最简单的情况下它似乎也不起作用。 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; 
} 

输出样本,这是我很不满意,因为我期待看到 “富” 与 “酒吧”,而不是一些空字符串:

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 
+0

嗯。 http://qt-project.org/wiki/Qt_Modules_Maturity_Level “QScriptEngineAgent和相关类 状态:已弃用 推理:有缺陷的设计,被更好的设计取代。” – NoQ 2014-11-23 11:36:46

回答

0

看起来,正确的上下文只适用于pos itionChange。幸运的是,每当发生真正的上下文更改时,都会使用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());使用您只需将其代理的引擎。执行后,我的可怜人分析器可以给你的函数/ stackpathnames与累积执行时间和呼叫次数的列表:

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 
.... 

干杯,

约翰内斯

+0

非常感谢大家的回答,我现在有点忙,但我确定我会尽快检查解决方案,并且很可能将答案标记为已接受:) – NoQ 2015-06-25 15:09:41

相关问题