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}