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