• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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