File indexing completed on 2023-10-03 08:42:55
0001 /* 0002 SPDX-FileCopyrightText: 2014-2017 Milian Wolff <mail@milianw.de> 0003 0004 SPDX-License-Identifier: LGPL-2.1-or-later 0005 */ 0006 0007 #define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN 0008 #include "3rdparty/doctest.h" 0009 0010 #include "track/trace.h" 0011 #include "track/tracetree.h" 0012 0013 #include "interpret/dwarfdiecache.h" 0014 0015 #include <elfutils/libdwelf.h> 0016 0017 #include <algorithm> 0018 #include <future> 0019 #include <thread> 0020 0021 #include <link.h> 0022 0023 using namespace std; 0024 0025 namespace { 0026 bool __attribute__((noinline)) fill(Trace& trace, int depth, int skip) 0027 { 0028 if (!depth) { 0029 return trace.fill(skip); 0030 } else { 0031 return fill(trace, depth - 1, skip); 0032 } 0033 } 0034 0035 bool bar(Trace& trace, int depth); 0036 0037 inline bool __attribute__((always_inline)) asdf(Trace& trace, int depth) 0038 { 0039 return bar(trace, depth - 1); 0040 } 0041 0042 inline bool __attribute__((always_inline)) foo(Trace& trace, int depth) 0043 { 0044 return asdf(trace, depth); 0045 } 0046 0047 bool __attribute__((noinline)) bar(Trace& trace, int depth) 0048 { 0049 if (!depth) { 0050 return trace.fill(0); 0051 } else { 0052 return foo(trace, depth); 0053 } 0054 } 0055 0056 void validateTrace(const Trace& trace, int expectedSize) 0057 { 0058 SUBCASE("validate the trace size") 0059 { 0060 REQUIRE(trace.size() == expectedSize); 0061 REQUIRE(distance(trace.begin(), trace.end()) == trace.size()); 0062 } 0063 SUBCASE("validate trace contents") 0064 { 0065 REQUIRE(find(trace.begin(), trace.end(), Trace::ip_t(0)) == trace.end()); 0066 } 0067 } 0068 } 0069 0070 TEST_CASE ("getting backtrace traces") { 0071 Trace trace; 0072 validateTrace(trace, 0); 0073 0074 SUBCASE("fill without skipping") 0075 { 0076 REQUIRE(trace.fill(0)); 0077 const auto offset = trace.size(); 0078 REQUIRE(offset > 1); 0079 validateTrace(trace, offset); 0080 0081 SUBCASE("fill with skipping") 0082 { 0083 for (auto skip : {0, 1, 2}) { 0084 for (int i = 0; i < 2 * Trace::MAX_SIZE; ++i) { 0085 REQUIRE(fill(trace, i, skip)); 0086 const auto expectedSize = min(i + offset + 1 - skip, static_cast<int>(Trace::MAX_SIZE) - skip); 0087 validateTrace(trace, expectedSize); 0088 } 0089 } 0090 } 0091 } 0092 } 0093 0094 TEST_CASE ("tracetree indexing") { 0095 TraceTree tree; 0096 0097 std::mutex mutex; 0098 const auto numTasks = std::thread::hardware_concurrency(); 0099 std::vector<std::future<void>> tasks(numTasks); 0100 0101 struct IpToParent 0102 { 0103 uintptr_t ip; 0104 uint32_t parentIndex; 0105 }; 0106 std::vector<IpToParent> ipsToParent; 0107 0108 struct IndexedTrace 0109 { 0110 Trace trace; 0111 uint32_t index; 0112 }; 0113 std::vector<IndexedTrace> traces; 0114 0115 // fill the tree 0116 for (auto i = 0u; i < numTasks; ++i) { 0117 tasks[i] = std::async(std::launch::async, [&mutex, &tree, &ipsToParent, &traces, i]() { 0118 Trace trace; 0119 0120 const auto leaf = uintptr_t((i + 1) * 100); 0121 0122 for (int k = 0; k < 100; ++k) { 0123 uint32_t lastIndex = 0; 0124 for (uintptr_t j = 0; j < 32; ++j) { 0125 trace.fillTestData(j, leaf); 0126 REQUIRE(trace.size() == j + 1); 0127 0128 const std::lock_guard<std::mutex> guard(mutex); 0129 uint32_t lastParent = 0; 0130 auto index = 0131 tree.index(trace, [k, j, leaf, &lastParent, &ipsToParent](uintptr_t ip, uint32_t parentIndex) { 0132 // for larger k, the trace is known and thus we won't hit this branch 0133 REQUIRE(k == 0); 0134 0135 REQUIRE(ip > 0); 0136 REQUIRE((ip <= (j + 1) || ip == leaf)); 0137 REQUIRE(((!lastParent && !parentIndex) || parentIndex > lastParent)); 0138 REQUIRE(parentIndex <= ipsToParent.size()); 0139 lastParent = parentIndex; 0140 0141 ipsToParent.push_back({ip, parentIndex}); 0142 return true; 0143 }); 0144 REQUIRE(index > lastIndex); 0145 REQUIRE(index <= ipsToParent.size()); 0146 0147 if (k == 0) { 0148 traces.push_back({trace, index}); 0149 } 0150 } 0151 } 0152 }); 0153 } 0154 0155 // wait for threads to finish 0156 for (auto& task : tasks) { 0157 task.get(); 0158 } 0159 0160 // verify that we can rebuild the traces 0161 for (const auto& trace : traces) { 0162 uint32_t index = trace.index; 0163 int i = 0; 0164 while (index) { 0165 REQUIRE(i < trace.trace.size()); 0166 REQUIRE(index > 0); 0167 REQUIRE(index <= ipsToParent.size()); 0168 0169 auto map = ipsToParent[index - 1]; 0170 REQUIRE(map.ip == reinterpret_cast<uintptr_t>(trace.trace[i])); 0171 0172 index = map.parentIndex; 0173 ++i; 0174 } 0175 } 0176 } 0177 0178 struct CallbackData 0179 { 0180 Dwfl* dwfl = nullptr; 0181 Dwfl_Module* mod = nullptr; 0182 }; 0183 static int dl_iterate_phdr_dwfl_report_callback(struct dl_phdr_info* info, size_t /*size*/, void* data) 0184 { 0185 const char* fileName = info->dlpi_name; 0186 if (!fileName || !fileName[0]) { 0187 auto callbackData = reinterpret_cast<CallbackData*>(data); 0188 callbackData->mod = dwfl_report_elf(callbackData->dwfl, "tst_trace", "/proc/self/exe", -1, info->dlpi_addr, false); 0189 REQUIRE(callbackData->mod); 0190 } 0191 0192 return 0; 0193 } 0194 0195 TEST_CASE ("symbolizing") { 0196 Trace trace; 0197 0198 REQUIRE(bar(trace, 5)); 0199 REQUIRE(trace.size() >= 6); 0200 0201 Dwfl_Callbacks callbacks = { 0202 &dwfl_build_id_find_elf, 0203 &dwfl_standard_find_debuginfo, 0204 &dwfl_offline_section_address, 0205 nullptr, 0206 }; 0207 0208 auto dwfl = std::unique_ptr<Dwfl, void (*)(Dwfl*)>(dwfl_begin(&callbacks), &dwfl_end); 0209 REQUIRE(dwfl); 0210 0211 dwfl_report_begin(dwfl.get()); 0212 CallbackData data = { dwfl.get(), nullptr }; 0213 dl_iterate_phdr(&dl_iterate_phdr_dwfl_report_callback, &data); 0214 dwfl_report_end(dwfl.get(), nullptr, nullptr); 0215 0216 REQUIRE(data.mod); 0217 0218 DwarfDieCache cache(data.mod); 0219 uint j = 0; 0220 for (uint i = 0; i < 6 + j; ++i) { 0221 auto addr = reinterpret_cast<Dwarf_Addr>(trace[i]); 0222 0223 auto cuDie = cache.findCuDie(addr); 0224 REQUIRE(cuDie); 0225 0226 auto offset = addr - cuDie->bias(); 0227 auto die = cuDie->findSubprogramDie(offset); 0228 REQUIRE(die); 0229 0230 auto dieName = cuDie->dieName(die->die()); 0231 auto isDebugBuild = i == 0 && dieName == "Trace::unwind(void**)"; 0232 if (i == 0 + j) { 0233 if (!isDebugBuild) 0234 REQUIRE(dieName == "Trace::fill(int)"); 0235 } else { 0236 REQUIRE(dieName == "bar"); 0237 } 0238 0239 auto scopes = findInlineScopes(die->die(), offset); 0240 if (i <= 1 + j) { 0241 REQUIRE(scopes.size() == 0); 0242 } else { 0243 REQUIRE(scopes.size() == 2); 0244 0245 Dwarf_Files* files = nullptr; 0246 dwarf_getsrcfiles(cuDie->cudie(), &files, nullptr); 0247 REQUIRE(files); 0248 0249 REQUIRE(cuDie->dieName(&scopes[0]) == "foo"); 0250 auto loc = callSourceLocation(&scopes[0], files, cuDie->cudie()); 0251 // called from bar 0252 REQUIRE(loc.line == 52); 0253 0254 REQUIRE(cuDie->dieName(&scopes[1]) == "asdf"); 0255 loc = callSourceLocation(&scopes[1], files, cuDie->cudie()); 0256 // called from foo 0257 REQUIRE(loc.line == 44); 0258 } 0259 0260 if (isDebugBuild) { 0261 ++j; 0262 } 0263 } 0264 }