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
31using namespace OHOS::HiviewDFX;
32using namespace testing::ext;
33using namespace std;
34
35namespace OHOS {
36namespace HiviewDFX {
37class LockParserUnittest : public testing::Test {
38public:
39    static void SetUpTestCase(void);
40    static void TearDownTestCase(void);
41    void SetUp();
42    void TearDown();
43};
44} // namespace HiviewDFX
45} // namespace OHOS
46
47void LockParserUnittest::SetUpTestCase(void)
48{
49    UnwinderConfig::SetEnableMiniDebugInfo(true);
50    UnwinderConfig::SetEnableLoadSymbolLazily(true);
51}
52
53void LockParserUnittest::TearDownTestCase(void)
54{
55}
56
57void LockParserUnittest::SetUp(void)
58{
59}
60
61void LockParserUnittest::TearDown(void)
62{
63}
64
65namespace {
66constexpr const int LOCK_TYPE_IDX = 0;
67constexpr const int LOCK_OWNER_IDX = 1;
68constexpr const int LOCK_OWNER_MASK = 0x3fffffff;
69void 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
79void 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
88void 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 */
104HWTEST_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;
114    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 */
158HWTEST_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;
168    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 */
212HWTEST_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;
220    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 */
251HWTEST_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;
259    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 */
290HWTEST_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;
298    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}