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 }