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 = test_map_.find(language); 39 if (it == test_map_.end()) { 40 std::unordered_map<const char *, std::unique_ptr<ApiTest>> entry; 41 auto res = test_map_.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 extractor_factory_ = factory; 50 } 51 GetTest(const char * name)52 static ApiTest *GetTest(const char *name) 53 { 54 for (auto iter = test_map_.begin(); iter != test_map_.end(); ++iter) { 55 auto &internalMap = iter->second; 56 auto internalIt = std::find_if(internalMap.begin(), internalMap.end(), 57 [name](auto &iterator) { return !::strcmp(iterator.first, name); }); 58 if (internalIt != internalMap.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(event_mutex_) { return last_event_location_ == location; }; 70 auto onSuccess = [&stoppedThread]() REQUIRES(event_mutex_) { 71 stoppedThread = last_event_thread_; 72 73 // Need to reset location, because we might want to stop at the same point 74 last_event_location_ = 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(event_mutex_) { return last_event_ == DebugEvent::VM_DEATH; }, [] {}); 85 } 86 WaitForInit()87 static bool WaitForInit() 88 { 89 return WaitForEvent( 90 DebugEvent::VM_INITIALIZATION, []() REQUIRES(event_mutex_) { 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(event_mutex_); 98 last_event_ = event; 99 last_event_thread_ = thread; 100 last_event_location_ = location; 101 if (event == DebugEvent::VM_INITIALIZATION) { 102 initialized_ = true; 103 } 104 event_cv_.Signal(); 105 } 106 Reset()107 static void Reset() 108 { 109 os::memory::LockHolder lock(event_mutex_); 110 initialized_ = false; 111 last_event_ = DebugEvent::VM_START; 112 } 113 GetTests()114 static TestMap &GetTests() 115 { 116 return test_map_; 117 } 118 IsTestFinished()119 static bool IsTestFinished() 120 { 121 os::memory::LockHolder lock(event_mutex_); 122 return last_event_ == 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 = extractor_factory_->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 std::vector<panda_file::LocalVariableInfo> GetVariables(const panda_file::File *pf, PtLocation location); 141 142 static int32_t GetValueRegister(Method *method, const char *varName, uint32_t offset = 0); 143 SuspendUntilContinue(DebugEvent reason,PtThread thread,PtLocation location)144 static bool SuspendUntilContinue(DebugEvent reason, PtThread thread, PtLocation location) 145 { 146 { 147 os::memory::LockHolder lock(suspend_mutex_); 148 suspended_ = true; 149 } 150 151 // Notify the debugger thread about the suspend event 152 Event(reason, thread, location); 153 154 // Wait for continue 155 { 156 os::memory::LockHolder lock(suspend_mutex_); 157 while (suspended_) { 158 suspend_cv_.Wait(&suspend_mutex_); 159 } 160 } 161 162 return true; 163 } 164 Continue()165 static bool Continue() 166 { 167 os::memory::LockHolder lock(suspend_mutex_); 168 suspended_ = false; 169 suspend_cv_.Signal(); 170 return true; 171 } 172 GetUserThreadList(DebugInterface * debugInterface,PandaVector<PtThread> * threadList)173 static bool GetUserThreadList(DebugInterface *debugInterface, PandaVector<PtThread> *threadList) 174 { 175 PandaVector<PtThread> threads; 176 debugInterface->GetThreadList(&threads); 177 178 for (auto &thread : threads) { 179 ManagedThread *managed_thread = thread.GetManagedThread(); 180 if (MTManagedThread::ThreadIsMTManagedThread(managed_thread)) { 181 auto mt_managed_thread = MTManagedThread::CastFromThread(managed_thread); 182 if (mt_managed_thread->IsDaemon()) { 183 continue; 184 } 185 } 186 threadList->push_back(thread); 187 } 188 return true; 189 } 190 191 private: 192 template <class Predicate, class OnSuccessAction> WaitForEvent(DebugEvent event,Predicate predicate,OnSuccessAction action)193 static bool WaitForEvent(DebugEvent event, Predicate predicate, OnSuccessAction action) 194 { 195 os::memory::LockHolder holder(event_mutex_); 196 while (!predicate()) { 197 if (last_event_ == DebugEvent::VM_DEATH) { 198 return false; 199 } 200 constexpr uint64_t TIMEOUT_MSEC = 100000U; 201 bool timeExceeded = event_cv_.TimedWait(&event_mutex_, TIMEOUT_MSEC); 202 if (timeExceeded) { 203 LOG(FATAL, DEBUGGER) << "Time limit exceeded while waiting " << event; 204 return false; 205 } 206 } 207 action(); 208 return true; 209 } 210 211 static TestMap test_map_; 212 static os::memory::Mutex event_mutex_; 213 static os::memory::ConditionVariable event_cv_ GUARDED_BY(event_mutex_); 214 static DebugEvent last_event_ GUARDED_BY(event_mutex_); 215 static PtThread last_event_thread_ GUARDED_BY(event_mutex_); 216 static PtLocation last_event_location_ GUARDED_BY(event_mutex_); 217 static os::memory::Mutex suspend_mutex_; 218 static os::memory::ConditionVariable suspend_cv_ GUARDED_BY(suspend_mutex_); 219 static bool suspended_ GUARDED_BY(suspend_mutex_); 220 static bool initialized_ GUARDED_BY(event_mutex_); 221 static TestExtractorFactory *extractor_factory_; 222 }; 223 224 // Some toolchains have << overloading for std::nullptr_t 225 // TODO(asoldatov): Find a better workaround, distro-specifc define seems too intrusive. 226 #if (!defined PANDA_TARGET_MOBILE) && (!defined PANDA_TARGET_LINUX_UBUNTU_20_04) 227 std::ostream &operator<<(std::ostream &out, std::nullptr_t); 228 #endif 229 230 #define _ASSERT_FAIL(val1, val2, strval1, strval2, msg) \ 231 std::cerr << "Assertion failed at " << __FILE__ << ':' << __LINE__ << std::endl; \ 232 std::cerr << "Expected that " strval1 " is " << msg << " " strval2 << std::endl; \ 233 std::cerr << "\t" strval1 ": " << (val1) << std::endl; \ 234 std::cerr << "\t" strval2 ": " << (val2) << std::endl; \ 235 std::abort(); 236 237 #define ASSERT_TRUE(cond) \ 238 do { \ 239 auto res = (cond); \ 240 if (!res) { \ 241 _ASSERT_FAIL(res, true, #cond, "true", "equal to") \ 242 } \ 243 } while (0) 244 245 #define ASSERT_FALSE(cond) \ 246 do { \ 247 auto res = (cond); \ 248 if (res) { \ 249 _ASSERT_FAIL(res, false, #cond, "false", "equal to") \ 250 } \ 251 } while (0) 252 253 #define ASSERT_EQ(lhs, rhs) \ 254 do { \ 255 auto res1 = (lhs); \ 256 decltype(res1) res2 = (rhs); \ 257 if (res1 != res2) { \ 258 _ASSERT_FAIL(res1, res2, #lhs, #rhs, "equal to") \ 259 } \ 260 } while (0) 261 262 #define ASSERT_NE(lhs, rhs) \ 263 do { \ 264 auto res1 = (lhs); \ 265 decltype(res1) res2 = (rhs); \ 266 if (res1 == res2) { \ 267 _ASSERT_FAIL(res1, res2, #lhs, #rhs, "not equal to") \ 268 } \ 269 } while (0) 270 271 #define ASSERT_STREQ(lhs, rhs) \ 272 do { \ 273 auto res1 = (lhs); \ 274 decltype(res1) res2 = (rhs); \ 275 if (::strcmp(res1, res2) != 0) { \ 276 _ASSERT_FAIL(res1, res2, #lhs, #rhs, "equal to") \ 277 } \ 278 } while (0) 279 280 #define ASSERT_SUCCESS(api_call) \ 281 do { \ 282 auto error = api_call; \ 283 if (error) { \ 284 _ASSERT_FAIL(error.value().GetMessage(), "Success", "API call result", "Expected", "") \ 285 } \ 286 } while (0) 287 288 #define ASSERT_EXITED() \ 289 do { \ 290 bool res = TestUtil::WaitForExit(); \ 291 if (!res) { \ 292 _ASSERT_FAIL(TestUtil::IsTestFinished(), true, "TestUtil::IsTestFinished()", "true", "") \ 293 } \ 294 } while (0) 295 296 #define ASSERT_LOCATION_EQ(lhs, rhs) \ 297 do { \ 298 ASSERT_STREQ(lhs.GetPandaFile(), rhs.GetPandaFile()); \ 299 ASSERT_EQ(lhs.GetMethodId().GetOffset(), rhs.GetMethodId().GetOffset()); \ 300 ASSERT_EQ(lhs.GetBytecodeOffset(), rhs.GetBytecodeOffset()); \ 301 } while (0); 302 303 #define ASSERT_THREAD_VALID(thread) \ 304 do { \ 305 ASSERT_NE(thread.GetId(), PtThread::NONE.GetId()); \ 306 } while (0); 307 308 #define ASSERT_BREAKPOINT_SUCCESS(location) \ 309 do { \ 310 PtThread suspended = TestUtil::WaitForBreakpoint(location); \ 311 ASSERT_THREAD_VALID(suspended); \ 312 } while (0); 313 314 } // namespace panda::tooling::test 315 316 #endif // PANDA_RUNTIME_DEBUG_TEST_TEST_UTIL_H 317