• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 }