• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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;
__anon75981a250202null114     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         pthread_mutex_unlock(&mutex);
136         ASSERT_TRUE(false);
137     }
138 
139     if (memcmp(buffer.data(), &mutex, sizeof(pthread_mutex_t)) != 0) {
140         pthread_mutex_unlock(&mutex);
141         ASSERT_TRUE(false);
142     }
143 
144     int lockOwner = mutexInt[LOCK_OWNER_IDX] & LOCK_OWNER_MASK;
145     ASSERT_EQ(gettid(), lockOwner);
146     printf("CurrentTid:%d Lock owner:%d\n", gettid(), lockOwner);
147     ASSERT_EQ(PTHREAD_MUTEX_ERRORCHECK, mutexInt[LOCK_TYPE_IDX]);
148     pthread_mutex_unlock(&mutex);
149     if (t1.joinable()) {
150         t1.join();
151     }
152     GTEST_LOG_(INFO) << "LockParserUnittest001: end.";
153 }
154 
155 /**
156  * @tc.name: LockParserUnittest002
157  * @tc.desc: unwinder parse normal lock owner
158  * @tc.type: FUNC
159  */
160 HWTEST_F(LockParserUnittest, LockParserUnittest002, TestSize.Level2)
161 {
162     GTEST_LOG_(INFO) << "LockParserUnittest002: start.";
163     pthread_mutex_t mutex;
164     InitMutexByType(PTHREAD_MUTEX_NORMAL, mutex);
165 
166     auto mutexInt = reinterpret_cast<int*>(&mutex);
167     LockMutex(mutex);
168 
169     int tid = 0;
__anon75981a250302null170     std::thread t1([&tid, &mutex] {
171         tid = gettid();
172         printf("BlockTid:%d\n", tid);
173         pthread_mutex_lock(&mutex);
174     });
175 
176     WaitThreadBlock(tid);
177     printf("CurrentTid:%d BlockTid:%d\n", gettid(), tid);
178     auto unwinder = std::make_shared<Unwinder>(true);
179     ASSERT_EQ(unwinder->UnwindLocalWithTid(tid), true);
180 
181     std::vector<uintptr_t> pcs = unwinder->GetPcs();
182     ASSERT_FALSE(pcs.empty());
183 
184     std::vector<DfxFrame> frames;
185     (void)unwinder->GetFramesByPcs(frames, pcs);
186     ASSERT_FALSE(frames.empty());
187     unwinder->SetFrames(frames);
188 
189     std::vector<char> buffer(sizeof(pthread_mutex_t), 0);
190     if (!unwinder->GetLockInfo(tid, buffer.data(), sizeof(pthread_mutex_t))) {
191         pthread_mutex_unlock(&mutex);
192         ASSERT_TRUE(false);
193     }
194 
195     if (memcmp(buffer.data(), &mutex, sizeof(pthread_mutex_t)) != 0) {
196         pthread_mutex_unlock(&mutex);
197         ASSERT_TRUE(false);
198     }
199 
200     int lockOwner = mutexInt[LOCK_OWNER_IDX] & LOCK_OWNER_MASK;
201     ASSERT_EQ(EBUSY, lockOwner);
202     printf("EBUSY:%d Lock owner:%d\n", EBUSY, lockOwner);
203     ASSERT_EQ(PTHREAD_MUTEX_NORMAL, mutexInt[LOCK_TYPE_IDX]);
204     pthread_mutex_unlock(&mutex);
205     if (t1.joinable()) {
206         t1.join();
207     }
208     GTEST_LOG_(INFO) << "LockParserUnittest002: end.";
209 }
210 
211 /**
212  * @tc.name: LockParserUnittest003
213  * @tc.desc: test lock parser parse normal lock
214  * @tc.type: FUNC
215  */
216 HWTEST_F(LockParserUnittest, LockParserUnittest003, TestSize.Level2)
217 {
218     GTEST_LOG_(INFO) << "LockParserUnittest003: start.";
219     pthread_mutex_t mutex;
220     InitMutexByType(PTHREAD_MUTEX_NORMAL, mutex);
221     LockMutex(mutex);
222 
223     int tid = 0;
__anon75981a250402null224     std::thread t1([&tid, &mutex] {
225         tid = gettid();
226         printf("BlockTid:%d\n", tid);
227         pthread_mutex_lock(&mutex);
228     });
229 
230     WaitThreadBlock(tid);
231     printf("CurrentTid:%d BlockTid:%d\n", gettid(), tid);
232     auto unwinder = std::make_shared<Unwinder>(true);
233     ASSERT_EQ(unwinder->UnwindLocalWithTid(tid), true);
234 
235     std::vector<uintptr_t> pcs = unwinder->GetPcs();
236     std::vector<DfxFrame> frames;
237     (void)unwinder->GetFramesByPcs(frames, pcs);
238     unwinder->SetFrames(frames);
239 
240     bool ret = LockParser::ParseLockInfo(unwinder, getpid(), tid);
241     ASSERT_EQ(ret, true);
242 
243     pthread_mutex_unlock(&mutex);
244     if (t1.joinable()) {
245         t1.join();
246     }
247     GTEST_LOG_(INFO) << "LockParserUnittest003: end.";
248 }
249 
250 /**
251  * @tc.name: LockParserUnittest004
252  * @tc.desc: test lock parser parse errorcheck lock
253  * @tc.type: FUNC
254  */
255 HWTEST_F(LockParserUnittest, LockParserUnittest004, TestSize.Level2)
256 {
257     GTEST_LOG_(INFO) << "LockParserUnittest004: start.";
258     pthread_mutex_t mutex;
259     InitMutexByType(PTHREAD_MUTEX_ERRORCHECK, mutex);
260     LockMutex(mutex);
261 
262     int tid = 0;
__anon75981a250502null263     std::thread t1([&tid, &mutex] {
264         tid = gettid();
265         printf("BlockTid:%d\n", tid);
266         pthread_mutex_lock(&mutex);
267     });
268 
269     WaitThreadBlock(tid);
270     printf("CurrentTid:%d BlockTid:%d\n", gettid(), tid);
271     auto unwinder = std::make_shared<Unwinder>(true);
272     ASSERT_EQ(unwinder->UnwindLocalWithTid(tid), true);
273 
274     std::vector<uintptr_t> pcs = unwinder->GetPcs();
275     std::vector<DfxFrame> frames;
276     (void)unwinder->GetFramesByPcs(frames, pcs);
277     unwinder->SetFrames(frames);
278 
279     bool ret = LockParser::ParseLockInfo(unwinder, getpid(), tid);
280     ASSERT_EQ(ret, true);
281 
282     pthread_mutex_unlock(&mutex);
283     if (t1.joinable()) {
284         t1.join();
285     }
286     GTEST_LOG_(INFO) << "LockParserUnittest004: end.";
287 }
288 
289 /**
290  * @tc.name: LockParserUnittest005
291  * @tc.desc: test lock parser parse PTHREAD_MUTEX_RECURSIVE lock
292  * @tc.type: FUNC
293  */
294 HWTEST_F(LockParserUnittest, LockParserUnittest005, TestSize.Level2)
295 {
296     GTEST_LOG_(INFO) << "LockParserUnittest005: start.";
297     pthread_mutex_t mutex;
298     InitMutexByType(PTHREAD_MUTEX_RECURSIVE, mutex);
299     LockMutex(mutex);
300 
301     int tid = 0;
__anon75981a250602null302     std::thread t1([&tid, &mutex] {
303         tid = gettid();
304         printf("BlockTid:%d\n", tid);
305         pthread_mutex_lock(&mutex);
306     });
307 
308     WaitThreadBlock(tid);
309     printf("CurrentTid:%d BlockTid:%d\n", gettid(), tid);
310     auto unwinder = std::make_shared<Unwinder>(true);
311     ASSERT_EQ(unwinder->UnwindLocalWithTid(tid), true);
312 
313     std::vector<uintptr_t> pcs = unwinder->GetPcs();
314     ASSERT_FALSE(pcs.empty());
315 
316     std::vector<DfxFrame> frames;
317     (void)unwinder->GetFramesByPcs(frames, pcs);
318     ASSERT_FALSE(frames.empty());
319     unwinder->SetFrames(frames);
320 
321     std::vector<char> buffer(sizeof(pthread_mutex_t), 0);
322     if (!unwinder->GetLockInfo(tid, buffer.data(), sizeof(pthread_mutex_t))) {
323         pthread_mutex_unlock(&mutex);
324         ASSERT_TRUE(false);
325     }
326 
327     if (memcmp(buffer.data(), &mutex, sizeof(pthread_mutex_t)) != 0) {
328         pthread_mutex_unlock(&mutex);
329         ASSERT_TRUE(false);
330     }
331 
332     auto mutexInt = reinterpret_cast<int*>(&mutex);
333     int lockOwner = mutexInt[LOCK_OWNER_IDX] & LOCK_OWNER_MASK;
334     ASSERT_EQ(gettid(), lockOwner);
335     printf("CurrentTid:%d Lock owner:%d\n", gettid(), lockOwner);
336     ASSERT_EQ(PTHREAD_MUTEX_RECURSIVE, mutexInt[LOCK_TYPE_IDX]);
337     ASSERT_EQ(LockParser::ParseLockInfo(unwinder, getpid(), tid), true);
338 
339     pthread_mutex_unlock(&mutex);
340     if (t1.joinable()) {
341         t1.join();
342     }
343     GTEST_LOG_(INFO) << "LockParserUnittest005: end.";
344 }
345 }