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 }