1 /** 2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 #ifndef PANDA_RUNTIME_DEBUG_TEST_TEST_UTIL_H 16 #define PANDA_RUNTIME_DEBUG_TEST_TEST_UTIL_H 17 18 #include <cstdlib> 19 #include <climits> 20 #include <unordered_set> 21 #include <unordered_map> 22 #include <string> 23 24 #include "runtime/tests/tooling/api_test.h" 25 #include "runtime/tests/tooling/test_extractor.h" 26 #include "runtime/include/mtmanaged_thread.h" 27 #include "runtime/include/tooling/pt_location.h" 28 #include "runtime/include/tooling/pt_thread.h" 29 #include "libpandabase/os/mutex.h" 30 31 namespace panda::tooling::test { 32 using TestMap = std::unordered_map<panda_file::SourceLang, std::unordered_map<const char *, std::unique_ptr<ApiTest>>>; 33 34 class TestUtil { 35 public: RegisterTest(panda_file::SourceLang language,const char * testName,std::unique_ptr<ApiTest> test)36 static void RegisterTest(panda_file::SourceLang language, const char *testName, std::unique_ptr<ApiTest> test) 37 { 38 auto it = testMap_.find(language); 39 if (it == testMap_.end()) { 40 std::unordered_map<const char *, std::unique_ptr<ApiTest>> entry; 41 auto res = testMap_.emplace(language, std::move(entry)); 42 it = res.first; 43 } 44 it->second.insert({testName, std::move(test)}); 45 } 46 SetExtractorFactory(TestExtractorFactory * factory)47 static void SetExtractorFactory(TestExtractorFactory *factory) 48 { 49 extractorFactory_ = factory; 50 } 51 GetTest(const char * name)52 static ApiTest *GetTest(const char *name) 53 { 54 for (const auto &[lang, internal_map] : testMap_) { 55 (void)lang; 56 auto internalIt = std::find_if(internal_map.begin(), internal_map.end(), 57 [name](auto &iterator) { return !::strcmp(iterator.first, name); }); 58 if (internalIt != internal_map.end()) { 59 return internalIt->second.get(); 60 } 61 } 62 LOG(FATAL, DEBUGGER) << "Test " << name << " not found"; 63 return nullptr; 64 } 65 WaitForBreakpoint(PtLocation location)66 static PtThread WaitForBreakpoint(PtLocation location) 67 { 68 PtThread stoppedThread(PtThread::NONE); 69 auto predicate = [&location]() REQUIRES(eventMutex_) { return lastEventLocation_ == location; }; 70 auto onSuccess = [&stoppedThread]() REQUIRES(eventMutex_) { 71 stoppedThread = lastEventThread_; 72 73 // Need to reset location, because we might want to stop at the same point 74 lastEventLocation_ = PtLocation("", EntityId(0), 0); 75 }; 76 77 WaitForEvent(DebugEvent::BREAKPOINT, predicate, onSuccess); 78 return stoppedThread; 79 } 80 WaitForExit()81 static bool WaitForExit() 82 { 83 return WaitForEvent( 84 DebugEvent::VM_DEATH, []() REQUIRES(eventMutex_) { return lastEvent_ == DebugEvent::VM_DEATH; }, [] {}); 85 } 86 WaitForInit()87 static bool WaitForInit() 88 { 89 return WaitForEvent( 90 DebugEvent::VM_INITIALIZATION, []() REQUIRES(eventMutex_) { return initialized_; }, [] {}); 91 } 92 93 static void Event(DebugEvent event, PtThread thread = PtThread::NONE, 94 PtLocation location = PtLocation("", EntityId(0), 0)) 95 { 96 LOG(DEBUG, DEBUGGER) << "Occured event " << event << " in thread with id " << thread.GetId(); 97 os::memory::LockHolder holder(eventMutex_); 98 lastEvent_ = event; 99 lastEventThread_ = thread; 100 lastEventLocation_ = location; 101 if (event == DebugEvent::VM_INITIALIZATION) { 102 initialized_ = true; 103 } 104 eventCv_.Signal(); 105 } 106 Reset()107 static void Reset() 108 { 109 os::memory::LockHolder lock(eventMutex_); 110 initialized_ = false; 111 lastEvent_ = DebugEvent::VM_START; 112 } 113 GetTests()114 static TestMap &GetTests() 115 { 116 return testMap_; 117 } 118 IsTestFinished()119 static bool IsTestFinished() 120 { 121 os::memory::LockHolder lock(eventMutex_); 122 return lastEvent_ == DebugEvent::VM_DEATH; 123 } 124 GetLocation(const char * sourceFile,uint32_t line,const char * pandaFile)125 static PtLocation GetLocation(const char *sourceFile, uint32_t line, const char *pandaFile) 126 { 127 std::unique_ptr<const panda_file::File> uFile = panda_file::File::Open(pandaFile); 128 const panda_file::File *pf = uFile.get(); 129 if (pf == nullptr) { 130 return PtLocation("", EntityId(0), 0); 131 } 132 133 auto extractor = extractorFactory_->MakeTestExtractor(pf); 134 auto [id, offset] = extractor->GetBreakpointAddress({sourceFile, line}); 135 return PtLocation(pandaFile, id, offset); 136 } 137 138 static std::vector<panda_file::LocalVariableInfo> GetVariables(Method *method, uint32_t offset); 139 140 static int32_t GetValueRegister(Method *method, const char *varName, uint32_t offset = 0); 141 SuspendUntilContinue(DebugEvent reason,PtThread thread,PtLocation location)142 static bool SuspendUntilContinue(DebugEvent reason, PtThread thread, PtLocation location) 143 { 144 { 145 os::memory::LockHolder lock(suspendMutex_); 146 suspended_ = true; 147 } 148 149 // Notify the debugger thread about the suspend event 150 Event(reason, thread, location); 151 152 // Wait for continue 153 { 154 os::memory::LockHolder lock(suspendMutex_); 155 while (suspended_) { 156 suspendCv_.Wait(&suspendMutex_); 157 } 158 } 159 160 return true; 161 } 162 Continue()163 static bool Continue() 164 { 165 os::memory::LockHolder lock(suspendMutex_); 166 suspended_ = false; 167 suspendCv_.Signal(); 168 return true; 169 } 170 GetUserThreadList(DebugInterface * debugInterface,PandaVector<PtThread> * threadList)171 static bool GetUserThreadList(DebugInterface *debugInterface, PandaVector<PtThread> *threadList) 172 { 173 PandaVector<PtThread> threads; 174 debugInterface->GetThreadList(&threads); 175 176 for (auto &thread : threads) { 177 ManagedThread *managedThread = thread.GetManagedThread(); 178 if (MTManagedThread::ThreadIsMTManagedThread(managedThread)) { 179 auto mtManagedThread = MTManagedThread::CastFromThread(managedThread); 180 if (mtManagedThread->IsDaemon()) { 181 continue; 182 } 183 } 184 threadList->push_back(thread); 185 } 186 return true; 187 } 188 189 private: 190 template <class Predicate, class OnSuccessAction> WaitForEvent(DebugEvent event,Predicate predicate,OnSuccessAction action)191 static bool WaitForEvent(DebugEvent event, Predicate predicate, OnSuccessAction action) 192 { 193 os::memory::LockHolder holder(eventMutex_); 194 while (!predicate()) { 195 if (lastEvent_ == DebugEvent::VM_DEATH) { 196 return false; 197 } 198 constexpr uint64_t TIMEOUT_MSEC = 100000U; 199 bool timeExceeded = eventCv_.TimedWait(&eventMutex_, TIMEOUT_MSEC); 200 if (timeExceeded) { 201 LOG(FATAL, DEBUGGER) << "Time limit exceeded while waiting " << event; 202 return false; 203 } 204 } 205 action(); 206 return true; 207 } 208 209 static TestMap testMap_; 210 static os::memory::Mutex eventMutex_; 211 static os::memory::ConditionVariable eventCv_ GUARDED_BY(eventMutex_); 212 static DebugEvent lastEvent_ GUARDED_BY(eventMutex_); 213 static PtThread lastEventThread_ GUARDED_BY(eventMutex_); 214 static PtLocation lastEventLocation_ GUARDED_BY(eventMutex_); 215 static os::memory::Mutex suspendMutex_; 216 static os::memory::ConditionVariable suspendCv_ GUARDED_BY(suspendMutex_); 217 static bool suspended_ GUARDED_BY(suspendMutex_); 218 static bool initialized_ GUARDED_BY(eventMutex_); 219 static TestExtractorFactory *extractorFactory_; 220 }; 221 222 // Some toolchains have << overloading for std::nullptr_t 223 // NOTE(asoldatov): Find a better workaround, distro-specifc define seems too intrusive. 224 #if (!defined PANDA_TARGET_MOBILE) && (!defined PANDA_TARGET_LINUX_UBUNTU_20_04) 225 std::ostream &operator<<(std::ostream &out, std::nullptr_t); 226 #endif 227 228 // NOLINTBEGIN(cppcoreguidelines-macro-usage) 229 #define ASSERT_FAIL_IMPL(val1, val2, strval1, strval2, msg) \ 230 std::cerr << "Assertion failed at " << __FILE__ << ':' << __LINE__ << std::endl; \ 231 std::cerr << "Expected that " strval1 " is " << (msg) << " " strval2 << std::endl; \ 232 std::cerr << "\t" strval1 ": " << (val1) << std::endl; \ 233 std::cerr << "\t" strval2 ": " << (val2) << std::endl; \ 234 std::abort() 235 236 #define ASSERT_TRUE(cond) \ 237 do { \ 238 auto res = (cond); \ 239 if (!res) { \ 240 ASSERT_FAIL_IMPL(res, true, #cond, "true", "equal to"); \ 241 } \ 242 } while (0) 243 244 #define ASSERT_FALSE(cond) \ 245 do { \ 246 auto res = (cond); \ 247 if (res) { \ 248 ASSERT_FAIL_IMPL(res, false, #cond, "false", "equal to"); \ 249 } \ 250 } while (0) 251 252 #define ASSERT_EQ(lhs, rhs) \ 253 do { \ 254 auto res1 = (lhs); \ 255 decltype(res1) res2 = (rhs); \ 256 if (res1 != res2) { \ 257 ASSERT_FAIL_IMPL(res1, res2, #lhs, #rhs, "equal to"); \ 258 } \ 259 } while (0) 260 261 #define ASSERT_NE(lhs, rhs) \ 262 do { \ 263 auto res1 = (lhs); \ 264 decltype(res1) res2 = (rhs); \ 265 if (res1 == res2) { \ 266 ASSERT_FAIL_IMPL(res1, res2, #lhs, #rhs, "not equal to"); \ 267 } \ 268 } while (0) 269 270 #define ASSERT_STREQ(lhs, rhs) \ 271 do { \ 272 auto res1 = (lhs); \ 273 decltype(res1) res2 = (rhs); \ 274 if (::strcmp(res1, res2) != 0) { \ 275 ASSERT_FAIL_IMPL(res1, res2, #lhs, #rhs, "equal to"); \ 276 } \ 277 } while (0) 278 279 #define ASSERT_SUCCESS(api_call) \ 280 do { \ 281 auto error = api_call; \ 282 if (error) { \ 283 ASSERT_FAIL_IMPL(error.value().GetMessage(), "Success", "API call result", "Expected", ""); \ 284 } \ 285 } while (0) 286 287 #define ASSERT_EXITED() \ 288 do { \ 289 bool res = TestUtil::WaitForExit(); \ 290 if (!res) { \ 291 ASSERT_FAIL_IMPL(TestUtil::IsTestFinished(), true, "TestUtil::IsTestFinished()", "true", ""); \ 292 } \ 293 } while (0) 294 295 #define ASSERT_LOCATION_EQ(lhs, rhs) \ 296 do { \ 297 ASSERT_STREQ((lhs).GetPandaFile(), (rhs).GetPandaFile()); \ 298 ASSERT_EQ((lhs).GetMethodId().GetOffset(), (rhs).GetMethodId().GetOffset()); \ 299 ASSERT_EQ((lhs).GetBytecodeOffset(), (rhs).GetBytecodeOffset()); \ 300 } while (0) 301 302 #define ASSERT_THREAD_VALID(thread) \ 303 do { \ 304 ASSERT_NE((thread).GetId(), PtThread::NONE.GetId()); \ 305 } while (0) 306 307 #define ASSERT_BREAKPOINT_SUCCESS(location) \ 308 do { \ 309 PtThread suspended = TestUtil::WaitForBreakpoint(location); \ 310 ASSERT_THREAD_VALID(suspended); \ 311 } while (0) 312 // NOLINTEND(cppcoreguidelines-macro-usage) 313 314 } // namespace panda::tooling::test 315 316 #endif // PANDA_RUNTIME_DEBUG_TEST_TEST_UTIL_H 317