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 }