1 /*
2  * Copyright (c) 2024 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 
16 #include <gtest/gtest.h>
17 
18 #include <cerrno>
19 #include <cinttypes>
20 #include <cstdio>
21 #include <cstring>
22 #include <thread>
23 #include <vector>
24 
25 #include <pthread.h>
26 
27 #include "lock_parser.h"
28 #include "unwinder.h"
29 #include "unwinder_config.h"
30 
31 using namespace OHOS::HiviewDFX;
32 using namespace testing::ext;
33 using namespace std;
34 
35 namespace OHOS {
36 namespace HiviewDFX {
37 class LockParserUnittest : public testing::Test {
38 public:
39     static void SetUpTestCase(void);
40     static void TearDownTestCase(void);
41     void SetUp();
42     void TearDown();
43 };
44 } // namespace HiviewDFX
45 } // namespace OHOS
46 
SetUpTestCase(void)47 void LockParserUnittest::SetUpTestCase(void)
48 {
49     UnwinderConfig::SetEnableMiniDebugInfo(true);
50     UnwinderConfig::SetEnableLoadSymbolLazily(true);
51 }
52 
TearDownTestCase(void)53 void LockParserUnittest::TearDownTestCase(void)
54 {
55 }
56 
SetUp(void)57 void LockParserUnittest::SetUp(void)
58 {
59 }
60 
TearDown(void)61 void LockParserUnittest::TearDown(void)
62 {
63 }
64 
65 namespace {
66 constexpr const int LOCK_TYPE_IDX = 0;
67 constexpr const int LOCK_OWNER_IDX = 1;
68 constexpr const int LOCK_OWNER_MASK = 0x3fffffff;
InitMutexByType(int32_t type,pthread_mutex_t & mutex)69 void InitMutexByType(int32_t type, pthread_mutex_t& mutex)
70 {
71     pthread_mutexattr_t mutexAttr;
72     pthread_mutexattr_init(&mutexAttr);
73     pthread_mutexattr_settype(&mutexAttr, type);
74 
75     pthread_mutex_init(&mutex, &mutexAttr);
76     pthread_mutexattr_destroy(&mutexAttr);
77 }
78 
LockMutex(pthread_mutex_t & mutex)79 void LockMutex(pthread_mutex_t& mutex)
80 {
81     auto mutexInt = reinterpret_cast<int*>(&mutex);
82     printf("mutex address:%llx\n", reinterpret_cast<long long>(&mutex));
83     printf("mutex owner before lock:%d\n", mutexInt[LOCK_OWNER_IDX]);
84     pthread_mutex_lock(&mutex);
85     printf("mutex owner after lock:%d\n", mutexInt[LOCK_OWNER_IDX]);
86 }
87 
WaitThreadBlock(int & tid)88 void WaitThreadBlock(int& tid)
89 {
90     while (true) {
91         if (tid > 0) {
92             printf("WaitThreadBlock:%d\n", tid);
93             break;
94         }
95         sleep(1);
96     }
97 }
98 
99 /**
100  * @tc.name: LockParserUnittest001
101  * @tc.desc: unwinder parse errorcheck lock owner
102  * @tc.type: FUNC
103  */
104 HWTEST_F(LockParserUnittest, LockParserUnittest001, TestSize.Level2)
105 {
106     GTEST_LOG_(INFO) << "LockParserUnittest001: start.";
107     pthread_mutex_t mutex;
108     InitMutexByType(PTHREAD_MUTEX_ERRORCHECK, mutex);
109 
110     auto mutexInt = reinterpret_cast<int*>(&mutex);
111     LockMutex(mutex);
112 
113     int tid = 0;
__anon7b59231d0202null114     std::thread t1([&tid, &mutex] {
115         tid = gettid();
116         printf("BlockTid:%d\n", tid);
117         pthread_mutex_lock(&mutex);
118     });
119 
120     WaitThreadBlock(tid);
121     printf("CurrentTid:%d BlockTid:%d\n", gettid(), tid);
122     auto unwinder = std::make_shared<Unwinder>(true);
123     ASSERT_EQ(unwinder->UnwindLocalWithTid(tid), true);
124 
125     std::vector<uintptr_t> pcs = unwinder->GetPcs();
126     ASSERT_FALSE(pcs.empty());
127 
128     std::vector<DfxFrame> frames;
129     (void)unwinder->GetFramesByPcs(frames, pcs);
130     ASSERT_FALSE(frames.empty());
131     unwinder->SetFrames(frames);
132 
133     std::vector<char> buffer(sizeof(pthread_mutex_t), 0);
134     if (!unwinder->GetLockInfo(tid, buffer.data(), sizeof(pthread_mutex_t))) {
135         ASSERT_TRUE(false);
136     }
137 
138     if (memcmp(buffer.data(), &mutex, sizeof(pthread_mutex_t)) != 0) {
139         ASSERT_TRUE(false);
140     }
141 
142     int lockOwner = mutexInt[LOCK_OWNER_IDX] & LOCK_OWNER_MASK;
143     ASSERT_EQ(gettid(), lockOwner);
144     printf("CurrentTid:%d Lock owner:%d\n", gettid(), lockOwner);
145     ASSERT_EQ(PTHREAD_MUTEX_ERRORCHECK, mutexInt[LOCK_TYPE_IDX]);
146     pthread_mutex_unlock(&mutex);
147     if (t1.joinable()) {
148         t1.join();
149     }
150     GTEST_LOG_(INFO) << "LockParserUnittest001: end.";
151 }
152 
153 /**
154  * @tc.name: LockParserUnittest002
155  * @tc.desc: unwinder parse normal lock owner
156  * @tc.type: FUNC
157  */
158 HWTEST_F(LockParserUnittest, LockParserUnittest002, TestSize.Level2)
159 {
160     GTEST_LOG_(INFO) << "LockParserUnittest002: start.";
161     pthread_mutex_t mutex;
162     InitMutexByType(PTHREAD_MUTEX_NORMAL, mutex);
163 
164     auto mutexInt = reinterpret_cast<int*>(&mutex);
165     LockMutex(mutex);
166 
167     int tid = 0;
__anon7b59231d0302null168     std::thread t1([&tid, &mutex] {
169         tid = gettid();
170         printf("BlockTid:%d\n", tid);
171         pthread_mutex_lock(&mutex);
172     });
173 
174     WaitThreadBlock(tid);
175     printf("CurrentTid:%d BlockTid:%d\n", gettid(), tid);
176     auto unwinder = std::make_shared<Unwinder>(true);
177     ASSERT_EQ(unwinder->UnwindLocalWithTid(tid), true);
178 
179     std::vector<uintptr_t> pcs = unwinder->GetPcs();
180     ASSERT_FALSE(pcs.empty());
181 
182     std::vector<DfxFrame> frames;
183     (void)unwinder->GetFramesByPcs(frames, pcs);
184     ASSERT_FALSE(frames.empty());
185     unwinder->SetFrames(frames);
186 
187     std::vector<char> buffer(sizeof(pthread_mutex_t), 0);
188     if (!unwinder->GetLockInfo(tid, buffer.data(), sizeof(pthread_mutex_t))) {
189         ASSERT_TRUE(false);
190     }
191 
192     if (memcmp(buffer.data(), &mutex, sizeof(pthread_mutex_t)) != 0) {
193         ASSERT_TRUE(false);
194     }
195 
196     int lockOwner = mutexInt[LOCK_OWNER_IDX] & LOCK_OWNER_MASK;
197     ASSERT_EQ(EBUSY, lockOwner);
198     printf("EBUSY:%d Lock owner:%d\n", EBUSY, lockOwner);
199     ASSERT_EQ(PTHREAD_MUTEX_NORMAL, mutexInt[LOCK_TYPE_IDX]);
200     pthread_mutex_unlock(&mutex);
201     if (t1.joinable()) {
202         t1.join();
203     }
204     GTEST_LOG_(INFO) << "LockParserUnittest002: end.";
205 }
206 
207 /**
208  * @tc.name: LockParserUnittest003
209  * @tc.desc: test lock parser parse normal lock
210  * @tc.type: FUNC
211  */
212 HWTEST_F(LockParserUnittest, LockParserUnittest003, TestSize.Level2)
213 {
214     GTEST_LOG_(INFO) << "LockParserUnittest003: start.";
215     pthread_mutex_t mutex;
216     InitMutexByType(PTHREAD_MUTEX_NORMAL, mutex);
217     LockMutex(mutex);
218 
219     int tid = 0;
__anon7b59231d0402null220     std::thread t1([&tid, &mutex] {
221         tid = gettid();
222         printf("BlockTid:%d\n", tid);
223         pthread_mutex_lock(&mutex);
224     });
225 
226     WaitThreadBlock(tid);
227     printf("CurrentTid:%d BlockTid:%d\n", gettid(), tid);
228     auto unwinder = std::make_shared<Unwinder>(true);
229     ASSERT_EQ(unwinder->UnwindLocalWithTid(tid), true);
230 
231     std::vector<uintptr_t> pcs = unwinder->GetPcs();
232     std::vector<DfxFrame> frames;
233     (void)unwinder->GetFramesByPcs(frames, pcs);
234     unwinder->SetFrames(frames);
235 
236     bool ret = LockParser::ParseLockInfo(unwinder, getpid(), tid);
237     ASSERT_EQ(ret, true);
238 
239     pthread_mutex_unlock(&mutex);
240     if (t1.joinable()) {
241         t1.join();
242     }
243     GTEST_LOG_(INFO) << "LockParserUnittest003: end.";
244 }
245 
246 /**
247  * @tc.name: LockParserUnittest004
248  * @tc.desc: test lock parser parse errorcheck lock
249  * @tc.type: FUNC
250  */
251 HWTEST_F(LockParserUnittest, LockParserUnittest004, TestSize.Level2)
252 {
253     GTEST_LOG_(INFO) << "LockParserUnittest004: start.";
254     pthread_mutex_t mutex;
255     InitMutexByType(PTHREAD_MUTEX_ERRORCHECK, mutex);
256     LockMutex(mutex);
257 
258     int tid = 0;
__anon7b59231d0502null259     std::thread t1([&tid, &mutex] {
260         tid = gettid();
261         printf("BlockTid:%d\n", tid);
262         pthread_mutex_lock(&mutex);
263     });
264 
265     WaitThreadBlock(tid);
266     printf("CurrentTid:%d BlockTid:%d\n", gettid(), tid);
267     auto unwinder = std::make_shared<Unwinder>(true);
268     ASSERT_EQ(unwinder->UnwindLocalWithTid(tid), true);
269 
270     std::vector<uintptr_t> pcs = unwinder->GetPcs();
271     std::vector<DfxFrame> frames;
272     (void)unwinder->GetFramesByPcs(frames, pcs);
273     unwinder->SetFrames(frames);
274 
275     bool ret = LockParser::ParseLockInfo(unwinder, getpid(), tid);
276     ASSERT_EQ(ret, true);
277 
278     pthread_mutex_unlock(&mutex);
279     if (t1.joinable()) {
280         t1.join();
281     }
282     GTEST_LOG_(INFO) << "LockParserUnittest004: end.";
283 }
284 
285 /**
286  * @tc.name: LockParserUnittest005
287  * @tc.desc: test lock parser parse PTHREAD_MUTEX_RECURSIVE lock
288  * @tc.type: FUNC
289  */
290 HWTEST_F(LockParserUnittest, LockParserUnittest005, TestSize.Level2)
291 {
292     GTEST_LOG_(INFO) << "LockParserUnittest005: start.";
293     pthread_mutex_t mutex;
294     InitMutexByType(PTHREAD_MUTEX_RECURSIVE, mutex);
295     LockMutex(mutex);
296 
297     int tid = 0;
__anon7b59231d0602null298     std::thread t1([&tid, &mutex] {
299         tid = gettid();
300         printf("BlockTid:%d\n", tid);
301         pthread_mutex_lock(&mutex);
302     });
303 
304     WaitThreadBlock(tid);
305     printf("CurrentTid:%d BlockTid:%d\n", gettid(), tid);
306     auto unwinder = std::make_shared<Unwinder>(true);
307     ASSERT_EQ(unwinder->UnwindLocalWithTid(tid), true);
308 
309     std::vector<uintptr_t> pcs = unwinder->GetPcs();
310     ASSERT_FALSE(pcs.empty());
311 
312     std::vector<DfxFrame> frames;
313     (void)unwinder->GetFramesByPcs(frames, pcs);
314     ASSERT_FALSE(frames.empty());
315     unwinder->SetFrames(frames);
316 
317     std::vector<char> buffer(sizeof(pthread_mutex_t), 0);
318     if (!unwinder->GetLockInfo(tid, buffer.data(), sizeof(pthread_mutex_t))) {
319         ASSERT_TRUE(false);
320     }
321 
322     if (memcmp(buffer.data(), &mutex, sizeof(pthread_mutex_t)) != 0) {
323         ASSERT_TRUE(false);
324     }
325 
326     auto mutexInt = reinterpret_cast<int*>(&mutex);
327     int lockOwner = mutexInt[LOCK_OWNER_IDX] & LOCK_OWNER_MASK;
328     ASSERT_EQ(gettid(), lockOwner);
329     printf("CurrentTid:%d Lock owner:%d\n", gettid(), lockOwner);
330     ASSERT_EQ(PTHREAD_MUTEX_RECURSIVE, mutexInt[LOCK_TYPE_IDX]);
331     ASSERT_EQ(LockParser::ParseLockInfo(unwinder, getpid(), tid), true);
332 
333     pthread_mutex_unlock(&mutex);
334     if (t1.joinable()) {
335         t1.join();
336     }
337     GTEST_LOG_(INFO) << "LockParserUnittest005: end.";
338 }
339 }