aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/qml/jit/qv4assembler.cpp47
-rw-r--r--tests/auto/qml/qjsengine/tst_qjsengine.cpp68
2 files changed, 109 insertions, 6 deletions
diff --git a/src/qml/jit/qv4assembler.cpp b/src/qml/jit/qv4assembler.cpp
index 186e5952da..72b057b2bc 100644
--- a/src/qml/jit/qv4assembler.cpp
+++ b/src/qml/jit/qv4assembler.cpp
@@ -38,6 +38,7 @@
****************************************************************************/
#include <QBuffer>
+#include <QFile>
#include "qv4engine_p.h"
#include "qv4assembler_p.h"
@@ -1367,6 +1368,17 @@ static void printDisassembledOutputWithCalls(QByteArray processedOutput,
qDebug("%s", processedOutput.constData());
}
+static QByteArray functionName(Function *function)
+{
+ QByteArray name = function->name()->toQString().toUtf8();
+ if (name.isEmpty()) {
+ name = QByteArray::number(reinterpret_cast<quintptr>(function), 16);
+ name.prepend("QV4::Function(0x");
+ name.append(')');
+ }
+ return name;
+}
+
void Assembler::link(Function *function)
{
for (const auto &jumpTarget : pasm()->patches)
@@ -1388,12 +1400,7 @@ void Assembler::link(Function *function)
buf.open(QIODevice::WriteOnly);
WTF::setDataFile(new QIODevicePrintStream(&buf));
- QByteArray name = function->name()->toQString().toUtf8();
- if (name.isEmpty()) {
- name = QByteArray::number(quintptr(function), 16);
- name.prepend("QV4::Function(0x");
- name.append(')');
- }
+ QByteArray name = functionName(function);
codeRef = linkBuffer.finalizeCodeWithDisassembly("%s", name.data());
WTF::setDataFile(stderr);
@@ -1404,6 +1411,34 @@ void Assembler::link(Function *function)
function->codeRef = new JSC::MacroAssemblerCodeRef(codeRef);
function->jittedCode = reinterpret_cast<Function::JittedCode>(function->codeRef->code().executableAddress());
+
+#if defined(Q_OS_LINUX)
+ // This implements writing of JIT'd addresses so that perf can find the
+ // symbol names.
+ //
+ // Perf expects the mapping to be in a certain place and have certain
+ // content, for more information, see:
+ // https://github.com/torvalds/linux/blob/master/tools/perf/Documentation/jit-interface.txt
+ static bool doProfile = !qEnvironmentVariableIsEmpty("QV4_PROFILE_WRITE_PERF_MAP");
+ if (doProfile) {
+ static QFile perfMapFile(QString::fromLatin1("/tmp/perf-%1.map")
+ .arg(QCoreApplication::applicationPid()));
+ static const bool isOpen = perfMapFile.open(QIODevice::WriteOnly);
+ if (!isOpen) {
+ qWarning("QV4::JIT::Assembler: Cannot write perf map file.");
+ doProfile = false;
+ } else {
+ perfMapFile.write(QByteArray::number(reinterpret_cast<quintptr>(
+ codeRef.code().executableAddress()), 16));
+ perfMapFile.putChar(' ');
+ perfMapFile.write(QByteArray::number(static_cast<qsizetype>(codeRef.size()), 16));
+ perfMapFile.putChar(' ');
+ perfMapFile.write(functionName(function));
+ perfMapFile.putChar('\n');
+ perfMapFile.flush();
+ }
+ }
+#endif
}
void Assembler::addLabel(int offset)
diff --git a/tests/auto/qml/qjsengine/tst_qjsengine.cpp b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
index 8f3011001b..8ffaa96569 100644
--- a/tests/auto/qml/qjsengine/tst_qjsengine.cpp
+++ b/tests/auto/qml/qjsengine/tst_qjsengine.cpp
@@ -202,6 +202,7 @@ private slots:
void malformedExpression();
void scriptScopes();
+ void perfMapFile();
signals:
void testSignal();
@@ -4130,6 +4131,73 @@ void tst_QJSEngine::scriptScopes()
QCOMPARE(use.toInt(), 42);
}
+static const char *perfMapKey = "QV4_PROFILE_WRITE_PERF_MAP";
+static const char *jitCallKey = "QV4_JIT_CALL_THRESHOLD";
+
+struct EnvironmentModifier {
+ const bool hasPerfMap = false;
+ const bool hasJitCall = false;
+ const QByteArray perfMap;
+ const QByteArray jitCall;
+
+ EnvironmentModifier() :
+ hasPerfMap(qEnvironmentVariableIsSet(perfMapKey)),
+ hasJitCall(qEnvironmentVariableIsSet(jitCallKey)),
+ perfMap(qgetenv(perfMapKey)),
+ jitCall(qgetenv(jitCallKey))
+ {
+ qputenv(perfMapKey, "1");
+ qputenv(jitCallKey, "0");
+ }
+
+ ~EnvironmentModifier()
+ {
+ if (hasPerfMap)
+ qputenv(perfMapKey, perfMap);
+ else
+ qunsetenv(perfMapKey);
+
+ if (hasJitCall)
+ qputenv(jitCallKey, jitCall);
+ else
+ qunsetenv(jitCallKey);
+ }
+};
+
+void tst_QJSEngine::perfMapFile()
+{
+#if !defined(Q_OS_LINUX)
+ QSKIP("perf map files are only generated on linux");
+#else
+ EnvironmentModifier modifier;
+ Q_UNUSED(modifier);
+ QJSEngine engine;
+ QJSValue def = engine.evaluate("'use strict'; function foo() { return 42 }");
+ QVERIFY(!def.isError());
+ QJSValue use = engine.evaluate("'use strict'; foo()");
+ QVERIFY(use.isNumber());
+ QFile file(QString::fromLatin1("/tmp/perf-%1.map").arg(QCoreApplication::applicationPid()));
+ QVERIFY(file.exists());
+ QVERIFY(file.open(QIODevice::ReadOnly));
+ QList<QByteArray> functions;
+ while (!file.atEnd()) {
+ const QByteArray contents = file.readLine();
+ QVERIFY(contents.endsWith('\n'));
+ QList<QByteArray> fields = contents.split(' ');
+ QCOMPARE(fields.length(), 3);
+ bool ok = false;
+ const qulonglong address = fields[0].toULongLong(&ok, 16);
+ QVERIFY(ok);
+ QVERIFY(address > 0);
+ const ulong size = fields[1].toULong(&ok, 16);
+ QVERIFY(ok);
+ QVERIFY(size > 0);
+ functions.append(fields[2]);
+ }
+ QVERIFY(functions.contains("foo\n"));
+#endif
+}
+
QTEST_MAIN(tst_QJSEngine)
#include "tst_qjsengine.moc"