File indexing completed on 2024-09-15 05:00:43
0001 /* 0002 SPDX-FileCopyrightText: 2019 David Faure <david.faure@kdab.com> 0003 0004 SPDX-License-Identifier: LGPL-2.1-or-later 0005 */ 0006 0007 #define DOCTEST_CONFIG_IMPLEMENT 0008 #include "3rdparty/doctest.h" 0009 0010 #include "analyze/suppressions.h" 0011 #include "locationdata.h" 0012 #include "parser.h" 0013 #include "treemodel.h" 0014 #include "tst_config.h" // for SRC_DIR 0015 0016 #include <KLocalizedString> 0017 0018 #include <QDebug> 0019 #include <QSignalSpy> 0020 0021 std::ostream& operator<<(std::ostream& os, const QString& value) 0022 { 0023 os << value.toStdString(); 0024 return os; 0025 } 0026 0027 struct TestParser 0028 { 0029 TestParser() 0030 : spySummary(&parser, &Parser::summaryAvailable) 0031 , spyCCD(&parser, &Parser::callerCalleeDataAvailable) 0032 , spyBottomUp(&parser, &Parser::bottomUpDataAvailable) 0033 , spyTopDown(&parser, &Parser::topDownDataAvailable) 0034 , spyFinished(&parser, &Parser::finished) 0035 { 0036 QObject::connect(&parser, &Parser::bottomUpDataAvailable, &parser, 0037 [this](const auto& data) { resultData = data.resultData; }); 0038 } 0039 0040 ~TestParser() 0041 { 0042 if (spyFinished.isEmpty()) 0043 REQUIRE(spyFinished.wait(20000)); 0044 } 0045 0046 CallerCalleeResults awaitCallerCallee() 0047 { 0048 if (spyCCD.isEmpty()) 0049 REQUIRE(spyCCD.wait(20000)); 0050 0051 auto ccr = spyCCD.at(0).at(0).value<CallerCalleeResults>(); 0052 REQUIRE(ccr.resultData); 0053 REQUIRE(ccr.resultData == resultData); 0054 return ccr; 0055 } 0056 0057 TreeData awaitBottomUp() 0058 { 0059 if (spyBottomUp.isEmpty()) 0060 REQUIRE(spyBottomUp.wait(20000)); 0061 0062 auto bottomUpData = spyBottomUp.at(0).at(0).value<TreeData>(); 0063 REQUIRE(bottomUpData.resultData); 0064 REQUIRE(bottomUpData.resultData == resultData); 0065 0066 if (qEnvironmentVariableIntValue("HEAPTRACK_DEBUG")) { 0067 qDebug() << "Bottom Up Data:"; 0068 for (const RowData& row : bottomUpData.rows) { 0069 qDebug() << symbolToString(row.symbol); 0070 } 0071 } 0072 0073 return bottomUpData; 0074 } 0075 0076 TreeData awaitTopDown() 0077 { 0078 if (spyTopDown.isEmpty()) 0079 REQUIRE(spyTopDown.wait(20000)); 0080 0081 auto topDownData = spyTopDown.at(0).at(0).value<TreeData>(); 0082 REQUIRE(topDownData.resultData); 0083 REQUIRE(topDownData.resultData == resultData); 0084 0085 if (qEnvironmentVariableIntValue("HEAPTRACK_DEBUG")) { 0086 qDebug() << "Top Down Data:"; 0087 for (const RowData& row : topDownData.rows) { 0088 qDebug() << symbolToString(row.symbol); 0089 } 0090 } 0091 0092 return topDownData; 0093 } 0094 0095 SummaryData awaitSummary() 0096 { 0097 if (spySummary.isEmpty()) 0098 REQUIRE(spySummary.wait(20000)); 0099 0100 return spySummary.at(0).at(0).value<SummaryData>(); 0101 } 0102 0103 Parser parser; 0104 std::shared_ptr<const ResultData> resultData; 0105 0106 QString symbolToString(const Symbol& sym) const 0107 { 0108 const auto module = resultData->string(sym.moduleId); 0109 return resultData->string(sym.functionId) + '|' + Util::basename(module) + '|' + module; 0110 } 0111 0112 QList<Symbol> sortedSymbols(const CallerCalleeResults& ccr) const 0113 { 0114 auto ccrSymbolList = ccr.entries.keys(); 0115 std::sort(ccrSymbolList.begin(), ccrSymbolList.end(), [&](const Symbol& lhs, const Symbol& rhs) { 0116 // keep unresolved functions up front 0117 auto sortable = [&](const Symbol& symbol) { 0118 auto str = [&](StringIndex stringId) { return ccr.resultData->string(stringId); }; 0119 return std::make_tuple(str(symbol.functionId), str(symbol.moduleId)); 0120 }; 0121 return sortable(lhs) < sortable(rhs); 0122 }); 0123 if (qEnvironmentVariableIntValue("HEAPTRACK_DEBUG")) { 0124 qDebug() << "Sorted Symbols"; 0125 int i = 0; 0126 for (const Symbol& sym : ccrSymbolList) { 0127 qDebug() << i++ << symbolToString(sym); 0128 } 0129 } 0130 return ccrSymbolList; 0131 } 0132 0133 private: 0134 QSignalSpy spySummary; 0135 QSignalSpy spyCCD; 0136 QSignalSpy spyBottomUp; 0137 QSignalSpy spyTopDown; 0138 QSignalSpy spyFinished; 0139 }; 0140 0141 TEST_CASE ("heaptrack.david.18594.gz") { 0142 TestParser parser; 0143 0144 FilterParameters params; 0145 bool parsedSuppressions = false; 0146 params.suppressions = parseSuppressions(SRC_DIR "/suppressions.txt", &parsedSuppressions); 0147 REQUIRE(parsedSuppressions); 0148 0149 parser.parser.parse(SRC_DIR "/heaptrack.david.18594.gz", QString(), params); 0150 0151 // ---- Check Caller Callee Data 0152 0153 const auto ccr = parser.awaitCallerCallee(); 0154 const auto ccrSymbolList = parser.sortedSymbols(ccr); 0155 0156 // Let's check a few items 0157 REQUIRE(parser.symbolToString(ccrSymbolList.at(0)) == "<unresolved function>||"); 0158 REQUIRE(parser.symbolToString(ccrSymbolList.at(1)) 0159 == "<unresolved function>|ld-linux-x86-64.so.2|/lib64/ld-linux-x86-64.so.2"); 0160 REQUIRE(parser.symbolToString(ccrSymbolList.at(25)) 0161 == "QByteArray::constData() const|libQt5Core.so.5|/d/qt/5/kde/build/qtbase/lib/libQt5Core.so.5"); 0162 const int lastIndx = ccrSymbolList.size() - 1; 0163 REQUIRE(parser.symbolToString(ccrSymbolList.at(lastIndx)) 0164 == "~QVarLengthArray|libQt5Core.so.5|/d/qt/5/kde/build/qtbase/lib/libQt5Core.so.5"); 0165 0166 REQUIRE(ccr.entries.count() == 365); 0167 REQUIRE(ccr.resultData->totalCosts().allocations == 2896); 0168 0169 // ---- Check Bottom Up Data 0170 0171 const auto bottomUpData = parser.awaitBottomUp(); 0172 0173 REQUIRE(bottomUpData.rows.size() == 54); 0174 REQUIRE(parser.symbolToString(bottomUpData.rows.at(3).symbol) 0175 == "<unresolved function>|libglib-2.0.so.0|/usr/lib64/libglib-2.0.so.0"); 0176 REQUIRE(bottomUpData.rows.at(3).children.size() == 2); 0177 REQUIRE(bottomUpData.rows.at(3).cost.allocations == 17); 0178 REQUIRE(bottomUpData.rows.at(3).cost.peak == 2020); 0179 REQUIRE(parser.symbolToString(bottomUpData.rows.at(53).symbol) 0180 == "QThreadPool::QThreadPool(QObject*)|libQt5Core.so.5|/d/qt/5/kde/build/qtbase/lib/libQt5Core.so.5"); 0181 0182 // ---- Check Top Down Data 0183 0184 const auto topDownData = parser.awaitTopDown(); 0185 REQUIRE(topDownData.rows.size() == 5); 0186 REQUIRE(parser.symbolToString(topDownData.rows.at(2).symbol) 0187 == "<unresolved function>|ld-linux-x86-64.so.2|/lib64/ld-linux-x86-64.so.2"); 0188 REQUIRE(topDownData.rows.at(2).children.size() == 1); 0189 REQUIRE(topDownData.rows.at(2).cost.allocations == 15); 0190 REQUIRE(topDownData.rows.at(2).cost.peak == 94496); 0191 0192 // ---- Check Summary 0193 0194 const auto summary = parser.awaitSummary(); 0195 REQUIRE(summary.debuggee == "./david"); 0196 REQUIRE(summary.cost.allocations == 2896); 0197 REQUIRE(summary.cost.temporary == 729); 0198 REQUIRE(summary.cost.leaked == 0); 0199 REQUIRE(summary.totalLeakedSuppressed == 30463); 0200 REQUIRE(summary.cost.peak == 996970); 0201 REQUIRE(summary.totalTime == 80); 0202 REQUIRE(summary.peakRSS == 76042240); 0203 REQUIRE(summary.peakTime == 0); 0204 REQUIRE(summary.totalSystemMemory == 16715239424); 0205 REQUIRE(summary.fromAttached == false); 0206 } 0207 0208 TEST_CASE ("heaptrack.embedded_lsan_suppressions.84207.zst") { 0209 TestParser parser; 0210 0211 parser.parser.parse(SRC_DIR "/heaptrack.embedded_lsan_suppressions.84207.zst", QString(), {}); 0212 0213 const auto summary = parser.awaitSummary(); 0214 REQUIRE(summary.debuggee == "./tests/manual/embedded_lsan_suppressions"); 0215 REQUIRE(summary.cost.allocations == 5); 0216 REQUIRE(summary.cost.temporary == 0); 0217 REQUIRE(summary.cost.leaked == 5); 0218 REQUIRE(summary.totalLeakedSuppressed == 5); 0219 REQUIRE(summary.cost.peak == 72714); 0220 REQUIRE(summary.totalSystemMemory == 33643876352); 0221 } 0222 0223 TEST_CASE ("heaptrack.embedded_lsan_suppressions.84207.zst without suppressions") { 0224 TestParser parser; 0225 0226 FilterParameters params; 0227 params.disableEmbeddedSuppressions = true; 0228 parser.parser.parse(SRC_DIR "/heaptrack.embedded_lsan_suppressions.84207.zst", QString(), params); 0229 0230 const auto summary = parser.awaitSummary(); 0231 REQUIRE(summary.debuggee == "./tests/manual/embedded_lsan_suppressions"); 0232 REQUIRE(summary.cost.allocations == 5); 0233 REQUIRE(summary.cost.leaked == 10); 0234 REQUIRE(summary.totalLeakedSuppressed == 0); 0235 } 0236 0237 TEST_CASE ("heaptrack.heaptrack_gui.99454.zst") { 0238 TestParser parser; 0239 0240 FilterParameters params; 0241 params.disableBuiltinSuppressions = true; 0242 0243 parser.parser.parse(SRC_DIR "/heaptrack.heaptrack_gui.99454.zst", QString(), params); 0244 0245 const auto summary = parser.awaitSummary(); 0246 REQUIRE(summary.debuggee == "heaptrack_gui heaptrack.trest_c.78689.zst"); 0247 REQUIRE(summary.cost.allocations == 278534); 0248 REQUIRE(summary.cost.temporary == 35481); 0249 REQUIRE(summary.cost.leaked == 1047379); 0250 REQUIRE(summary.cost.peak == 12222213); 0251 0252 const auto ccr = parser.awaitCallerCallee(); 0253 const auto sortedSymbols = parser.sortedSymbols(ccr); 0254 0255 const auto& sym = sortedSymbols[994]; 0256 REQUIRE(parser.symbolToString(sym) == "QHashData::allocateNode(int)|libQt5Core.so.5|/usr/lib/libQt5Core.so.5"); 0257 const auto& cost = ccr.entries[sym]; 0258 CHECK(cost.inclusiveCost.allocations == 5214); 0259 CHECK(cost.inclusiveCost.temporary == 0); 0260 CHECK(cost.inclusiveCost.leaked == 32); 0261 CHECK(cost.inclusiveCost.peak == 56152); 0262 CHECK(cost.selfCost.allocations == 5214); 0263 CHECK(cost.selfCost.temporary == 0); 0264 CHECK(cost.selfCost.leaked == 32); 0265 CHECK(cost.selfCost.peak == 56152); 0266 } 0267 0268 TEST_CASE ("heaptrack.heaptrack_gui.99529.zst") { 0269 TestParser parser; 0270 0271 FilterParameters params; 0272 params.disableBuiltinSuppressions = true; 0273 0274 parser.parser.parse(SRC_DIR "/heaptrack.heaptrack_gui.99529.zst", QString(), params); 0275 0276 const auto summary = parser.awaitSummary(); 0277 REQUIRE(summary.debuggee == "heaptrack_gui heaptrack.test_c.78689.zst"); 0278 REQUIRE(summary.cost.allocations == 315255); 0279 REQUIRE(summary.cost.temporary == 40771); 0280 REQUIRE(summary.cost.leaked == 1046377); 0281 REQUIRE(summary.cost.peak == 64840134); 0282 0283 const auto ccr = parser.awaitCallerCallee(); 0284 const auto sortedSymbols = parser.sortedSymbols(ccr); 0285 0286 const auto& sym = sortedSymbols[1103]; 0287 REQUIRE(parser.symbolToString(sym) == "QHashData::allocateNode(int)|libQt5Core.so.5|/usr/lib/libQt5Core.so.5"); 0288 const auto& cost = ccr.entries[sym]; 0289 CHECK(cost.inclusiveCost.allocations == 5559); 0290 CHECK(cost.inclusiveCost.temporary == 0); 0291 CHECK(cost.inclusiveCost.leaked == 32); 0292 CHECK(cost.inclusiveCost.peak == 68952); 0293 CHECK(cost.selfCost.allocations == 5559); 0294 CHECK(cost.selfCost.temporary == 0); 0295 CHECK(cost.selfCost.leaked == 32); 0296 CHECK(cost.selfCost.peak == 68952); 0297 } 0298 0299 TEST_CASE ("heaptrack.heaptrack_gui.{99454,99529}.zst diff") { 0300 TestParser parser; 0301 0302 parser.parser.parse(SRC_DIR "/heaptrack.heaptrack_gui.99529.zst", SRC_DIR "/heaptrack.heaptrack_gui.99454.zst", {}); 0303 0304 const auto summary = parser.awaitSummary(); 0305 REQUIRE(summary.debuggee == "heaptrack_gui heaptrack.test_c.78689.zst"); 0306 REQUIRE(summary.cost.allocations == 36721); 0307 REQUIRE(summary.cost.temporary == 5290); 0308 REQUIRE(summary.cost.leaked == -1002); 0309 REQUIRE(summary.cost.peak == 52617921); 0310 0311 const auto ccr = parser.awaitCallerCallee(); 0312 const auto sortedSymbols = parser.sortedSymbols(ccr); 0313 0314 const auto& sym = sortedSymbols[545]; 0315 REQUIRE(parser.symbolToString(sym) == "QHashData::allocateNode(int)|libQt5Core.so.5|/usr/lib/libQt5Core.so.5"); 0316 const auto& cost = ccr.entries[sym]; 0317 CHECK(cost.inclusiveCost.allocations == (5559 - 5214)); 0318 CHECK(cost.inclusiveCost.temporary == 0); 0319 CHECK(cost.inclusiveCost.leaked == 0); 0320 CHECK(cost.inclusiveCost.peak == (68952 - 56152)); 0321 CHECK(cost.selfCost.allocations == (5559 - 5214)); 0322 CHECK(cost.selfCost.temporary == 0); 0323 CHECK(cost.selfCost.leaked == 0); 0324 CHECK(cost.selfCost.peak == ((68952 - 56152))); 0325 } 0326 0327 int main(int argc, char** argv) 0328 { 0329 QCoreApplication app(argc, argv); 0330 0331 qRegisterMetaType<CallerCalleeResults>(); 0332 KLocalizedString::setApplicationDomain("heaptrack"); 0333 0334 doctest::Context context; 0335 context.applyCommandLine(argc, argv); 0336 0337 return context.run(); 0338 }