File indexing completed on 2024-05-12 05:43:25

0001 /*
0002     Copyright (C) 2015 Volker Krause <vkrause@kde.org>
0003 
0004     This program is free software; you can redistribute it and/or modify it
0005     under the terms of the GNU Library General Public License as published by
0006     the Free Software Foundation; either version 2 of the License, or (at your
0007     option) any later version.
0008 
0009     This program is distributed in the hope that it will be useful, but WITHOUT
0010     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0011     FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Library General Public
0012     License for more details.
0013 
0014     You should have received a copy of the GNU General Public License
0015     along with this program.  If not, see <https://www.gnu.org/licenses/>.
0016 */
0017 
0018 #include "ldbenchmark.h"
0019 
0020 #include <elf/elffile.h>
0021 #include <elf/elffileset.h>
0022 
0023 #include <QDebug>
0024 #include <QProcess>
0025 
0026 #include <algorithm>
0027 #include <iostream>
0028 
0029 #include <cassert>
0030 
0031 static double median(QVector<double> data)
0032 {
0033     if (data.size() == 0)
0034         return 0.0;
0035     std::sort(data.begin(), data.end());
0036     return data.at(data.size() / 2);
0037 }
0038 
0039 static double min(const QVector<double> &data)
0040 {
0041     if (data.size() == 0)
0042         return 0.0;
0043     return *std::min_element(data.constBegin(), data.constEnd());
0044 }
0045 
0046 static double max(const QVector<double> &data)
0047 {
0048     if (data.size() == 0)
0049         return 0.0;
0050     return *std::max_element(data.constBegin(), data.constEnd());
0051 }
0052 
0053 
0054 void LDBenchmark::measureFileSet(ElfFileSet* fileSet)
0055 {
0056     m_fileSet = fileSet;
0057 
0058     m_results.clear();
0059     m_results.reserve(fileSet->size());
0060 
0061     m_args.reserve(fileSet->size() + 1);
0062     m_args.push_back(QString()); // placeholder for mode argument
0063 
0064     for (int i = fileSet->size() - 1; i >= 0; --i) {
0065         const auto fileName = fileSet->file(i)->fileName();
0066         m_args.push_back(fileName);
0067         Result r;
0068         r.fileName = fileName.toUtf8();
0069         m_results.push_back(r);
0070     }
0071 
0072     measure(LoadMode::None, 1); // avoid cold cache skewing the results
0073     measure(LoadMode::Lazy, 5);
0074     measure(LoadMode::Now, 5);
0075 }
0076 
0077 void LDBenchmark::measure(LDBenchmark::LoadMode mode, int iterations)
0078 {
0079     m_args[0] = mode == LoadMode::Lazy ? QStringLiteral("RTLD_LAZY") : QStringLiteral("RTLD_NOW");
0080     for (int i = 0; i < iterations; ++i) {
0081         QProcess proc;
0082         proc.setProcessChannelMode(QProcess::QProcess::ForwardedErrorChannel);
0083         proc.start(QStringLiteral("ldbenchmark-runner"), m_args); // TODO find in libexec
0084         proc.waitForFinished();
0085         if (proc.exitStatus() == QProcess::CrashExit)
0086             qWarning() << "Benchmark runner crashed!";
0087         readResults(&proc, mode);
0088     }
0089 }
0090 
0091 void LDBenchmark::readResults(QProcess* proc, LoadMode mode)
0092 {
0093     while(proc->canReadLine()) {
0094         const auto line = proc->readLine();
0095         if (!line.startsWith("LDBENCHMARKRUNNER\t")) {
0096             qDebug() << "target stdout:" << line;
0097             continue;
0098         }
0099         const auto index = line.lastIndexOf('\t');
0100         const auto fileName = line.mid(18, index - 18);
0101         const auto cost = line.mid(index).trimmed().toDouble();
0102         auto it = std::find_if(m_results.begin(), m_results.end(), [fileName](const Result &res) {
0103             return res.fileName == fileName;
0104         });
0105         assert(it != m_results.end());
0106 
0107         switch (mode) {
0108             case LoadMode::Lazy:
0109                 (*it).lazy.push_back(cost);
0110                 break;
0111             case LoadMode::Now:
0112                 (*it).now.push_back(cost);
0113                 break;
0114             case LoadMode::None:
0115                 return;
0116         }
0117     }
0118 }
0119 
0120 void LDBenchmark::writeCSV(const QString& fileName)
0121 {
0122     QFile f(fileName);
0123     if (!f.open(QFile::WriteOnly | QFile::Truncate)) {
0124         qWarning() << "Failed to open" << fileName;
0125         return;
0126     }
0127 
0128     for (int i = 0; i < m_results.size(); ++i) {
0129         const auto res = m_results.at(i);
0130         const auto file = m_fileSet->file(m_results.size() - 1 - i);
0131         f.write(file->displayName().toUtf8());
0132         f.write("\t");
0133         f.write(QByteArray::number(::median(res.lazy)));
0134         f.write("\t");
0135         f.write(QByteArray::number(::min(res.lazy)));
0136         f.write("\t");
0137         f.write(QByteArray::number(::max(res.lazy)));
0138         f.write("\t");
0139         f.write(QByteArray::number(::median(res.now)));
0140         f.write("\t");
0141         f.write(QByteArray::number(::min(res.now)));
0142         f.write("\t");
0143         f.write(QByteArray::number(::max(res.now)));
0144         f.write("\n");
0145     }
0146 }
0147 
0148 int LDBenchmark::size() const
0149 {
0150     return m_results.size();
0151 }
0152 
0153 double LDBenchmark::median(LoadMode mode, int index) const
0154 {
0155     const auto &res = m_results.at(index);
0156     return ::median(mode == LoadMode::Lazy ? res.lazy : res.now);
0157 }
0158 
0159 double LDBenchmark::min(LDBenchmark::LoadMode mode, int index) const
0160 {
0161     const auto &res = m_results.at(index);
0162     return ::min(mode == LoadMode::Lazy ? res.lazy : res.now);
0163 }
0164 
0165 ElfFile* LDBenchmark::file(int index) const
0166 {
0167     return m_fileSet->file(size() - index - 1);
0168 }