122 lines
3.4 KiB
C++
122 lines
3.4 KiB
C++
#include "perfjson.h"
|
|
|
|
#include <QHttpServer>
|
|
#include <QJsonObject>
|
|
#include <QDebug>
|
|
#include <QCoreApplication>
|
|
|
|
#include <unistd.h>
|
|
#include <sys/prctl.h>
|
|
#include <signal.h>
|
|
|
|
PerfJson::PerfJson(QHttpServer &server, QObject *parent) :
|
|
QObject{parent}
|
|
{
|
|
m_process.setProcessChannelMode(QProcess::MergedChannels);
|
|
|
|
m_process.setChildProcessModifier([] {
|
|
prctl(PR_SET_PDEATHSIG, SIGKILL);
|
|
});
|
|
|
|
connect(&m_process, &QProcess::started, this, &PerfJson::processStarted);
|
|
connect(&m_process, &QProcess::finished, this, &PerfJson::processFinished);
|
|
connect(&m_process, &QProcess::readyRead, this, &PerfJson::readyRead);
|
|
|
|
m_process.start("sudo", {"perf", "trace", "-e", "bcachefs:error_throw", "-e", "bcachefs:write_super"});
|
|
|
|
server.route("/perf_json", this, &PerfJson::getPerfJson);
|
|
}
|
|
|
|
void PerfJson::processStarted()
|
|
{
|
|
qDebug() << "started";
|
|
}
|
|
|
|
void PerfJson::processFinished(int exitCode, QProcess::ExitStatus exitStatus)
|
|
{
|
|
qDebug() << "finished exitCode =" << exitCode << "exitStatus =" << exitStatus;
|
|
QCoreApplication::exit(exitCode);
|
|
}
|
|
|
|
void PerfJson::readyRead()
|
|
{
|
|
while (m_process.canReadLine())
|
|
{
|
|
auto lineStr = m_process.readLine();
|
|
|
|
QByteArrayView line = lineStr;
|
|
line = line.trimmed();
|
|
|
|
//qDebug() << "received" << line;
|
|
|
|
qsizetype tsEnd = line.indexOf(' ');
|
|
QByteArrayView timestamp = line.left(tsEnd);
|
|
|
|
qsizetype lparen = line.indexOf('(', tsEnd + 1);
|
|
|
|
qsizetype funcStart = line.lastIndexOf(' ', lparen);
|
|
QByteArrayView function = line.mid(funcStart + 1, lparen - funcStart - 1);
|
|
|
|
QByteArrayView thread = line.mid(tsEnd + 1, funcStart - tsEnd - 1);
|
|
|
|
qsizetype rparen = line.lastIndexOf(')');
|
|
QByteArrayView args = line.mid(lparen + 1, rparen - lparen - 1);
|
|
|
|
//qDebug() << "timestamp =" << timestamp;
|
|
//qDebug() << "thread =" << thread;
|
|
//qDebug() << "function =" << function;
|
|
//qDebug() << "args =" << args;
|
|
|
|
QByteArrayView fs, str;
|
|
while (!args.isEmpty())
|
|
{
|
|
qsizetype colon = args.indexOf(':');
|
|
qsizetype quote1 = args.indexOf('"', colon);
|
|
qsizetype quote2 = args.indexOf('"', quote1 + 1);
|
|
|
|
QByteArrayView key = args.left(colon).trimmed();
|
|
QByteArrayView value = args.mid(quote1 + 1, quote2 - quote1 - 1);
|
|
|
|
//qDebug() << "key =" << key << "value =" << value;
|
|
|
|
if (key == "fs")
|
|
fs = value;
|
|
else if (key == "str")
|
|
str = value;
|
|
|
|
qsizetype next = args.indexOf(',', quote2);
|
|
if (next == -1) break;
|
|
args = args.mid(next + 1).trimmed();
|
|
}
|
|
|
|
if (fs.isEmpty())
|
|
continue;
|
|
|
|
counters[fs][function][str]++;
|
|
}
|
|
}
|
|
|
|
QJsonObject PerfJson::getPerfJson() const
|
|
{
|
|
auto filesystems = QJsonObject{};
|
|
|
|
for (auto iter = std::cbegin(counters); iter != std::cend(counters); iter++)
|
|
{
|
|
auto functions = QJsonObject{};
|
|
|
|
for (auto iter2 = std::cbegin(iter.value()); iter2 != std::cend(iter.value()); iter2++)
|
|
{
|
|
auto strs = QJsonObject{};
|
|
|
|
for (auto iter3 = std::cbegin(iter2.value()); iter3 != std::cend(iter2.value()); iter3++)
|
|
strs[iter3.key()] = iter3.value();
|
|
|
|
functions[iter2.key()] = std::move(strs);
|
|
}
|
|
|
|
filesystems[iter.key()] = std::move(functions);
|
|
}
|
|
|
|
return filesystems;
|
|
}
|