• 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 = 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