1/* 2 * Copyright (c) 2023-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 <cstdio> 19#include <malloc.h> 20#include <map> 21#include <securec.h> 22#include <thread> 23#include <unistd.h> 24 25#include "dfx_config.h" 26#include "dfx_frame_formatter.h" 27#include "dfx_ptrace.h" 28#include "dfx_regs_get.h" 29#include "dfx_test_util.h" 30#include "elapsed_time.h" 31#include "unwinder.h" 32 33#if defined(__x86_64__) 34#include <unwind.h> // GCC's internal unwinder, part of libgcc 35#endif 36 37using namespace testing; 38using namespace testing::ext; 39 40namespace OHOS { 41namespace HiviewDFX { 42#undef LOG_DOMAIN 43#undef LOG_TAG 44#define LOG_TAG "DfxUnwinderTest" 45#define LOG_DOMAIN 0xD002D11 46#define TIME_SLEEP 3 47 48class UnwinderTest : public testing::Test { 49public: 50 static void SetUpTestCase() {} 51 static void TearDownTestCase() {} 52 void SetUp() {} 53 void TearDown() {} 54 55 std::map<int, std::shared_ptr<Unwinder>> unwinders_; 56 const size_t skipFrameNum = 2; 57}; 58 59/** 60 * @tc.name: GetStackRangeTest001 61 * @tc.desc: test unwinder GetStackRange interface in pid == tid 62 * @tc.type: FUNC 63 */ 64HWTEST_F(UnwinderTest, GetStackRangeTest001, TestSize.Level2) 65{ 66 GTEST_LOG_(INFO) << "GetStackRangeTest001: start."; 67 auto unwinder = std::make_shared<Unwinder>(); 68 uintptr_t stackBottom = 1; 69 uintptr_t stackTop = static_cast<uintptr_t>(-1); 70 GTEST_LOG_(INFO) << "when pid == tid and maps_ != null, GetStackRange(stackBottom, stackTop) is true"; 71 ASSERT_TRUE(unwinder->GetStackRange(stackBottom, stackTop)); 72 // When the param is less than -1, maps_ = null when method Unwinder is constructed 73 auto unwinderNegative = std::make_shared<Unwinder>(-2); 74 GTEST_LOG_(INFO) << "when pid == tid and maps_ == null, GetStackRange(stackBottom, stackTop) is false"; 75 ASSERT_TRUE(unwinderNegative->GetStackRange(stackBottom, stackTop)); 76 GTEST_LOG_(INFO) << "GetStackRangeTest001: end."; 77} 78 79/** 80 * @tc.name: GetStackRangeTest002 81 * @tc.desc: test unwinder GetStackRange interface in pid != tid 82 * @tc.type: FUNC 83 */ 84HWTEST_F(UnwinderTest, GetStackRangeTest002, TestSize.Level2) 85{ 86 GTEST_LOG_(INFO) << "GetStackRangeTest002: start."; 87 auto unwinder = std::make_shared<Unwinder>(); 88 uintptr_t stackBottom = 1; 89 uintptr_t stackTop = static_cast<uintptr_t>(-1); 90 bool result = false; 91 GTEST_LOG_(INFO) << "Run the function with thread will get pid != tid, " 92 "GetStackRange(stackBottom, stackTop) is true"; 93 std::thread th([unwinder, &stackBottom, &stackTop, &result] { 94 result = unwinder->GetStackRange(stackBottom, stackTop); 95 }); 96 if (th.joinable()) { 97 th.join(); 98 } 99 ASSERT_TRUE(result); 100 GTEST_LOG_(INFO) << "GetStackRangeTest002: end."; 101} 102 103/** 104 * @tc.name: UnwinderLocalTest001 105 * @tc.desc: test unwinder local unwind 106 * @tc.type: FUNC 107 */ 108HWTEST_F(UnwinderTest, UnwinderLocalTest001, TestSize.Level2) 109{ 110 GTEST_LOG_(INFO) << "UnwinderLocalTest001: start."; 111 auto unwinder = std::make_shared<Unwinder>(); 112 ElapsedTime counter; 113 MAYBE_UNUSED bool unwRet = unwinder->UnwindLocal(); 114 unwinder->EnableMethodIdLocal(true); 115 time_t elapsed1 = counter.Elapsed(); 116 EXPECT_EQ(true, unwRet) << "UnwinderLocalTest001: Unwind:" << unwRet; 117 auto frames = unwinder->GetFrames(); 118 ASSERT_GT(frames.size(), 1); 119 time_t elapsed2 = counter.Elapsed(); 120 GTEST_LOG_(INFO) << "Elapsed-: " << elapsed1 << "\tElapsed+: " << elapsed2; 121 GTEST_LOG_(INFO) << "UnwinderLocalTest001: frames:\n" << Unwinder::GetFramesStr(frames); 122 unwRet = unwinder->UnwindLocal(false, false, DEFAULT_MAX_FRAME_NUM, skipFrameNum); 123 EXPECT_EQ(true, unwRet) << "UnwinderLocalTest001: Unwind:" << unwRet; 124 auto frames2 = unwinder->GetFrames(); 125 ASSERT_GT(frames.size(), frames2.size()); 126 GTEST_LOG_(INFO) << "UnwinderLocalTest001: frames2:\n" << Unwinder::GetFramesStr(frames2); 127 GTEST_LOG_(INFO) << "UnwinderLocalTest001: end."; 128} 129 130/** 131 * @tc.name: UnwinderLocalTest002 132 * @tc.desc: test unwinder local unwind n counts 133 * @tc.type: FUNC 134 */ 135HWTEST_F(UnwinderTest, UnwinderLocalTest002, TestSize.Level2) 136{ 137 GTEST_LOG_(INFO) << "UnwinderLocalTest002: start."; 138 unwinders_.clear(); 139 std::shared_ptr<Unwinder> unwinder = nullptr; 140 pid_t pid = getpid(); 141 GTEST_LOG_(INFO) << "pid: " << pid; 142 for (int i = 0; i < 10; ++i) { 143 auto it = unwinders_.find(pid); 144 if (it != unwinders_.end()) { 145 unwinder = it->second; 146 } else { 147 unwinder = std::make_shared<Unwinder>(); 148 } 149 ElapsedTime counter; 150 MAYBE_UNUSED bool unwRet = unwinder->UnwindLocal(); 151 time_t elapsed1 = counter.Elapsed(); 152 EXPECT_EQ(true, unwRet) << "UnwinderLocalTest002: Unwind:" << unwRet; 153 auto frames = unwinder->GetFrames(); 154 ASSERT_GT(frames.size(), 1); 155 time_t elapsed2 = counter.Elapsed(); 156 GTEST_LOG_(INFO) << "Elapsed-: " << elapsed1 << "\tElapsed+: " << elapsed2; 157 GTEST_LOG_(INFO) << "UnwinderLocalTest002: frames:\n" << Unwinder::GetFramesStr(frames); 158 unwinders_[pid] = unwinder; 159 sleep(1); 160 }; 161 GTEST_LOG_(INFO) << "UnwinderLocalTest002: end."; 162} 163 164/** 165 * @tc.name: UnwinderLocalTest003 166 * @tc.desc: test unwinder UnwindLocal interface 167 * @tc.type: FUNC 168 */ 169HWTEST_F(UnwinderTest, UnwinderLocalTest003, TestSize.Level2) 170{ 171 GTEST_LOG_(INFO) << "UnwinderLocalTest003: start."; 172 // When the param is less than -1, maps_ = null when method Unwinder is constructed 173 auto unwinderNegative = std::make_shared<Unwinder>(-2); 174 GTEST_LOG_(INFO) << "when pid == tid and maps_ == null, " 175 "UnwindLocal(maxFrameNum, skipFrameNum) is false"; 176 ASSERT_FALSE(unwinderNegative->UnwindLocal()); 177 auto unwinder = std::make_shared<Unwinder>(); 178 GTEST_LOG_(INFO) << "when pid == tid and maps_ != null, " 179 "UnwindLocal(maxFrameNum, skipFrameNum) is true"; 180 ASSERT_TRUE(unwinder->UnwindLocal()); 181 GTEST_LOG_(INFO) << "UnwinderLocalTest003: end."; 182} 183 184/** 185 * @tc.name: UnwinderRemoteTest001 186 * @tc.desc: test unwinder remote unwind 187 * @tc.type: FUNC 188 */ 189HWTEST_F(UnwinderTest, UnwinderRemoteTest001, TestSize.Level2) 190{ 191 GTEST_LOG_(INFO) << "UnwinderRemoteTest001: start."; 192 pid_t child = fork(); 193 if (child == 0) { 194 sleep(TIME_SLEEP); 195 _exit(0); 196 } 197 198 GTEST_LOG_(INFO) << "pid: " << child << ", ppid:" << getpid(); 199 auto unwinder = std::make_shared<Unwinder>(child); 200 bool unwRet = DfxPtrace::Attach(child); 201 EXPECT_EQ(true, unwRet) << "UnwinderRemoteTest001: Attach:" << unwRet; 202 ElapsedTime counter; 203 unwRet = unwinder->UnwindRemote(child); 204 time_t elapsed1 = counter.Elapsed(); 205 EXPECT_EQ(true, unwRet) << "UnwinderRemoteTest001: unwRet:" << unwRet; 206 auto frames = unwinder->GetFrames(); 207 ASSERT_GT(frames.size(), 1); 208 time_t elapsed2 = counter.Elapsed(); 209 GTEST_LOG_(INFO) << "Elapsed-: " << elapsed1 << "\tElapsed+: " << elapsed2; 210 GTEST_LOG_(INFO) << "UnwinderRemoteTest001: frames:\n" << Unwinder::GetFramesStr(frames); 211 unwRet = unwinder->UnwindRemote(child, false, DEFAULT_MAX_FRAME_NUM, skipFrameNum); 212 EXPECT_EQ(true, unwRet) << "UnwinderRemoteTest001: unwRet:" << unwRet; 213 auto frames2 = unwinder->GetFrames(); 214 ASSERT_GT(frames.size(), frames2.size()); 215 GTEST_LOG_(INFO) << "UnwinderRemoteTest001: frames2:\n" << Unwinder::GetFramesStr(frames2); 216 DfxPtrace::Detach(child); 217 GTEST_LOG_(INFO) << "UnwinderRemoteTest001: end."; 218} 219 220/** 221 * @tc.name: UnwinderRemoteTest002 222 * @tc.desc: test unwinder remote unwind n counts 223 * @tc.type: FUNC 224 */ 225HWTEST_F(UnwinderTest, UnwinderRemoteTest002, TestSize.Level2) 226{ 227 GTEST_LOG_(INFO) << "UnwinderRemoteTest002: start."; 228 pid_t child = fork(); 229 if (child == 0) { 230 sleep(TIME_SLEEP); 231 _exit(0); 232 } 233 234 GTEST_LOG_(INFO) << "pid: " << child << ", ppid:" << getpid(); 235 unwinders_.clear(); 236 std::shared_ptr<Unwinder> unwinder = nullptr; 237 bool unwRet = DfxPtrace::Attach(child); 238 EXPECT_EQ(true, unwRet) << "UnwinderRemoteTest002: Attach:" << unwRet; 239 for (int i = 0; i < 10; ++i) { 240 auto it = unwinders_.find(child); 241 if (it != unwinders_.end()) { 242 unwinder = it->second; 243 } else { 244 unwinder = std::make_shared<Unwinder>(child); 245 } 246 ElapsedTime counter; 247 unwRet = unwinder->UnwindRemote(child); 248 time_t elapsed1 = counter.Elapsed(); 249 EXPECT_EQ(true, unwRet) << "UnwinderRemoteTest002: Unwind:" << unwRet; 250 auto frames = unwinder->GetFrames(); 251 ASSERT_GT(frames.size(), 1); 252 time_t elapsed2 = counter.Elapsed(); 253 GTEST_LOG_(INFO) << "Elapsed-: " << elapsed1 << "\tElapsed+: " << elapsed2; 254 GTEST_LOG_(INFO) << "UnwinderRemoteTest002: frames:\n" << Unwinder::GetFramesStr(frames); 255 unwinders_[child] = unwinder; 256 sleep(1); 257 } 258 DfxPtrace::Detach(child); 259 GTEST_LOG_(INFO) << "UnwinderRemoteTest002: end."; 260} 261 262/** 263 * @tc.name: UnwinderRemoteTest003 264 * @tc.desc: test unwinder UnwindRemote interface 265 * @tc.type: FUNC 266 */ 267HWTEST_F(UnwinderTest, UnwinderRemoteTest003, TestSize.Level2) 268{ 269 GTEST_LOG_(INFO) << "UnwinderRemoteTest003: start."; 270 // When the param is less than -1, pid_ < 0 when method Unwinder is constructed 271 auto unwinderNegative = std::make_shared<Unwinder>(-2); 272 size_t maxFrameNum = 64; 273 size_t skipFrameNum = 0; 274 GTEST_LOG_(INFO) << "when pid <= 0, UnwindRemote(maxFrameNum, skipFrameNum) is false"; 275 ASSERT_FALSE(unwinderNegative->UnwindRemote(-2, maxFrameNum, skipFrameNum)); 276 GTEST_LOG_(INFO) << "UnwinderRemoteTest003: end."; 277} 278 279/** 280 * @tc.name: UnwindTest001 281 * @tc.desc: test unwinder unwind interface in remote case 282 * @tc.type: FUNC 283 */ 284HWTEST_F(UnwinderTest, UnwindTest001, TestSize.Level2) 285{ 286 GTEST_LOG_(INFO) << "UnwindTest001: start."; 287 pid_t child = fork(); 288 if (child == 0) { 289 sleep(TIME_SLEEP); 290 _exit(0); 291 } 292 293 GTEST_LOG_(INFO) << "pid: " << child << ", ppid:" << getpid(); 294 auto unwinder = std::make_shared<Unwinder>(child); 295 bool unwRet = DfxPtrace::Attach(child); 296 EXPECT_EQ(true, unwRet) << "UnwindTest001: Attach:" << unwRet; 297 auto regs = DfxRegs::CreateRemoteRegs(child); 298 unwinder->SetRegs(regs); 299 auto maps = DfxMaps::Create(child); 300 UnwindContext context; 301 context.pid = child; 302 context.regs = regs; 303 context.maps = maps; 304 ElapsedTime counter; 305 unwRet = unwinder->Unwind(&context); 306 time_t elapsed1 = counter.Elapsed(); 307 EXPECT_EQ(true, unwRet) << "UnwindTest001: Unwind:" << unwRet; 308 auto frames = unwinder->GetFrames(); 309 ASSERT_GT(frames.size(), 1); 310 time_t elapsed2 = counter.Elapsed(); 311 GTEST_LOG_(INFO) << "Elapsed-: " << elapsed1 << "\tElapsed+: " << elapsed2; 312 GTEST_LOG_(INFO) << "UnwindTest001: frames:\n" << Unwinder::GetFramesStr(frames); 313 DfxPtrace::Detach(child); 314 GTEST_LOG_(INFO) << "UnwindTest001: end."; 315} 316 317/** 318 * @tc.name: UnwindTest002 319 * @tc.desc: test unwinder unwind interface in local case 320 * @tc.type: FUNC 321 */ 322HWTEST_F(UnwinderTest, UnwindTest002, TestSize.Level2) 323{ 324 GTEST_LOG_(INFO) << "UnwindTest002: start."; 325 auto unwinder = std::make_shared<Unwinder>(); 326 uintptr_t stackBottom = 1, stackTop = static_cast<uintptr_t>(-1); 327 ASSERT_TRUE(unwinder->GetStackRange(stackBottom, stackTop)); 328 GTEST_LOG_(INFO) << "UnwindTest002: GetStackRange."; 329 UnwindContext context; 330 context.stackCheck = false; 331 context.stackBottom = stackBottom; 332 context.stackTop = stackTop; 333 334 auto regs = DfxRegs::Create(); 335 auto regsData = regs->RawData(); 336 GetLocalRegs(regsData); 337 unwinder->SetRegs(regs); 338 auto maps = DfxMaps::Create(getpid()); 339 context.pid = UNWIND_TYPE_LOCAL; 340 context.regs = regs; 341 context.maps = maps; 342 bool unwRet = unwinder->Unwind(&context); 343 EXPECT_EQ(true, unwRet) << "UnwindTest002: unwRet:" << unwRet; 344 auto frames = unwinder->GetFrames(); 345 ASSERT_GT(frames.size(), 1); 346 GTEST_LOG_(INFO) << "UnwindTest002:frames:\n" << Unwinder::GetFramesStr(frames); 347 GTEST_LOG_(INFO) << "UnwindTest002: end."; 348} 349 350/** 351 * @tc.name: UnwindTest003 352 * @tc.desc: test GetLastErrorCode GetLastErrorAddr functions 353 * in local case 354 * @tc.type: FUNC 355 */ 356HWTEST_F(UnwinderTest, UnwindTest003, TestSize.Level2) 357{ 358 GTEST_LOG_(INFO) << "UnwindTest003: start."; 359 360 auto unwinder = std::make_shared<Unwinder>(); 361 unwinder->IgnoreMixstack(true); 362 MAYBE_UNUSED bool unwRet = unwinder->UnwindLocal(); 363 EXPECT_EQ(true, unwRet) << "UnwindTest003: Unwind ret:" << unwRet; 364 unwinder->EnableFillFrames(false); 365 const auto& frames = unwinder->GetFrames(); 366 ASSERT_GT(frames.size(), 1) << "frames.size() error"; 367 368 uint16_t errorCode = unwinder->GetLastErrorCode(); 369 uint64_t errorAddr = unwinder->GetLastErrorAddr(); 370 GTEST_LOG_(INFO) << "errorCode:" << errorCode; 371 GTEST_LOG_(INFO) << "errorAddr:" << errorAddr; 372 GTEST_LOG_(INFO) << "UnwindTest003: end."; 373} 374 375/** 376 * @tc.name: UnwindTest004 377 * @tc.desc: test unwinder local unwind for 378 * GetFramesStr(const std::vector<std::shared_ptr<DfxFrame>>& frames) 379 * @tc.type: FUNC 380 */ 381HWTEST_F(UnwinderTest, UnwindTest004, TestSize.Level2) 382{ 383 GTEST_LOG_(INFO) << "UnwindTest004: start."; 384 385 auto unwinder = std::make_shared<Unwinder>(); 386 ElapsedTime counter; 387 MAYBE_UNUSED bool unwRet = unwinder->UnwindLocal(); 388 ASSERT_EQ(true, unwRet) << "UnwindTest004: Unwind:" << unwRet; 389 auto frames = unwinder->GetFrames(); 390 ASSERT_GT(frames.size(), 1); 391 392 auto framesVec = DfxFrameFormatter::ConvertFrames(frames); 393 std::string framesStr = DfxFrameFormatter::GetFramesStr(framesVec); 394 GTEST_LOG_(INFO) << "UnwindTest004: frames:\n" << framesStr; 395 396 string log[] = {"pc", "test_unwind", "#00", "#01", "#02"}; 397 int len = sizeof(log) / sizeof(log[0]); 398 int count = GetKeywordsNum(framesStr, log, len); 399 ASSERT_EQ(count, len) << "UnwindTest004 Failed"; 400 GTEST_LOG_(INFO) << "UnwindTest004: end."; 401} 402 403/** 404 * @tc.name: StepTest001 405 * @tc.desc: test unwinder Step interface in remote case 406 * @tc.type: FUNC 407 */ 408HWTEST_F(UnwinderTest, StepTest001, TestSize.Level2) 409{ 410 GTEST_LOG_(INFO) << "StepTest001: start."; 411 pid_t child = fork(); 412 if (child == 0) { 413 sleep(TIME_SLEEP); 414 _exit(0); 415 } 416 417 GTEST_LOG_(INFO) << "pid: " << child << ", ppid:" << getpid(); 418 auto unwinder = std::make_shared<Unwinder>(child); 419 bool unwRet = DfxPtrace::Attach(child); 420 EXPECT_EQ(true, unwRet) << "StepTest001: Attach:" << unwRet; 421 auto regs = DfxRegs::CreateRemoteRegs(child); 422 auto maps = DfxMaps::Create(child); 423 unwinder->SetRegs(regs); 424 UnwindContext context; 425 context.pid = child; 426 context.regs = regs; 427 context.maps = maps; 428 429 uintptr_t pc, sp; 430 pc = regs->GetPc(); 431 sp = regs->GetSp(); 432 std::shared_ptr<DfxMap> map = nullptr; 433 ASSERT_TRUE(maps->FindMapByAddr(pc, map)); 434 context.map = map; 435 unwRet = unwinder->Step(pc, sp, &context); 436 ASSERT_TRUE(unwRet) << "StepTest001: Unwind:" << unwRet; 437 DfxPtrace::Detach(child); 438 GTEST_LOG_(INFO) << "StepTest001: end."; 439} 440 441/** 442 * @tc.name: StepTest002 443 * @tc.desc: test unwinder Step interface in local case 444 * @tc.type: FUNC 445 */ 446HWTEST_F(UnwinderTest, StepTest002, TestSize.Level2) 447{ 448 GTEST_LOG_(INFO) << "StepTest002: start."; 449 auto unwinder = std::make_shared<Unwinder>(); 450 uintptr_t stackBottom = 1, stackTop = static_cast<uintptr_t>(-1); 451 ASSERT_TRUE(unwinder->GetStackRange(stackBottom, stackTop)); 452 GTEST_LOG_(INFO) << "StepTest002: GetStackRange."; 453 auto maps = DfxMaps::Create(getpid()); 454 UnwindContext context; 455 context.pid = UNWIND_TYPE_LOCAL; 456 context.stackCheck = false; 457 context.stackBottom = stackBottom; 458 context.stackTop = stackTop; 459 460 auto regs = DfxRegs::Create(); 461 auto regsData = regs->RawData(); 462 GetLocalRegs(regsData); 463 unwinder->SetRegs(regs); 464 context.regs = regs; 465 context.maps = maps; 466 467 uintptr_t pc, sp; 468 pc = regs->GetPc(); 469 sp = regs->GetSp(); 470 bool unwRet = unwinder->Step(pc, sp, &context); 471 ASSERT_TRUE(unwRet) << "StepTest002: unwRet:" << unwRet; 472 GTEST_LOG_(INFO) << "StepTest002: end."; 473} 474 475#if defined(__aarch64__) 476/** 477 * @tc.name: StepTest003 478 * @tc.desc: test unwinder UnwindByFp interface in remote case 479 * @tc.type: FUNC 480 */ 481HWTEST_F(UnwinderTest, StepTest003, TestSize.Level2) 482{ 483 GTEST_LOG_(INFO) << "StepTest003: start."; 484 pid_t child = fork(); 485 if (child == 0) { 486 sleep(TIME_SLEEP); 487 _exit(0); 488 } 489 490 GTEST_LOG_(INFO) << "pid: " << child << ", ppid:" << getpid(); 491 auto unwinder = std::make_shared<Unwinder>(child); 492 bool unwRet = DfxPtrace::Attach(child); 493 EXPECT_EQ(true, unwRet) << "StepTest003: Attach:" << unwRet; 494 auto regs = DfxRegs::CreateRemoteRegs(child); 495 unwinder->SetRegs(regs); 496 UnwindContext context; 497 context.pid = child; 498 ElapsedTime counter; 499 unwRet = unwinder->UnwindByFp(&context); 500 ASSERT_TRUE(unwRet) << "StepTest003: unwind:" << unwRet; 501 DfxPtrace::Detach(child); 502 time_t elapsed = counter.Elapsed(); 503 GTEST_LOG_(INFO) << "StepTest003: Elapsed: " << elapsed; 504 auto pcs = unwinder->GetPcs(); 505 std::vector<DfxFrame> frames; 506 unwinder->GetFramesByPcs(frames, pcs); 507 ASSERT_GT(frames.size(), 1); 508 GTEST_LOG_(INFO) << "StepTest003: frames:\n" << Unwinder::GetFramesStr(frames); 509 GTEST_LOG_(INFO) << "StepTest003: end."; 510} 511 512/** 513 * @tc.name: StepTest004 514 * @tc.desc: test unwinder FpStep interface in local case 515 * @tc.type: FUNC 516 */ 517HWTEST_F(UnwinderTest, StepTest004, TestSize.Level2) 518{ 519 GTEST_LOG_(INFO) << "StepTest004: start."; 520 auto unwinder = std::make_shared<Unwinder>(); 521 ElapsedTime counter; 522 uintptr_t stackBottom = 1, stackTop = static_cast<uintptr_t>(-1); 523 ASSERT_TRUE(unwinder->GetStackRange(stackBottom, stackTop)); 524 GTEST_LOG_(INFO) << "StepTest004: GetStackRange."; 525 526 auto regs = DfxRegs::Create(); 527 auto regsData = regs->RawData(); 528 GetLocalRegs(regsData); 529 UnwindContext context; 530 context.pid = UNWIND_TYPE_LOCAL; 531 context.stackCheck = false; 532 context.stackBottom = stackBottom; 533 context.stackTop = stackTop; 534 unwinder->SetRegs(regs); 535 536 bool unwRet = unwinder->UnwindByFp(&context); 537 ASSERT_TRUE(unwRet) << "StepTest004: unwRet:" << unwRet; 538 auto unwSize = unwinder->GetPcs().size(); 539 ASSERT_GT(unwSize, 1) << "pcs.size() error"; 540 541 uintptr_t miniRegs[FP_MINI_REGS_SIZE] = {0}; 542 GetFramePointerMiniRegs(miniRegs, sizeof(miniRegs) / sizeof(miniRegs[0])); 543 regs = DfxRegs::CreateFromRegs(UnwindMode::FRAMEPOINTER_UNWIND, miniRegs, sizeof(miniRegs) / sizeof(miniRegs[0])); 544 unwinder->SetRegs(regs); 545 size_t idx = 0; 546 uintptr_t pc, fp; 547 while (true) { 548 pc = regs->GetPc(); 549 fp = regs->GetFp(); 550 idx++; 551 if (!unwinder->FpStep(fp, pc, &context) || (pc == 0)) { 552 break; 553 } 554 }; 555 ASSERT_EQ(idx, unwSize) << "StepTest004: idx:" << idx; 556 time_t elapsed = counter.Elapsed(); 557 GTEST_LOG_(INFO) << "StepTest004: Elapsed: " << elapsed; 558 GTEST_LOG_(INFO) << "StepTest004: end."; 559} 560#endif 561 562#if defined(__arm__) || defined(__aarch64__) 563/** 564 * @tc.name: StepTest005 565 * @tc.desc: test unwinder Step interface in lr callback with apply failed case 566 * @tc.type: FUNC 567 */ 568HWTEST_F(UnwinderTest, StepTest005, TestSize.Level2) 569{ 570 GTEST_LOG_(INFO) << "StepTest005: start."; 571 auto unwinder = std::make_shared<Unwinder>(); 572 uintptr_t stackBottom = 1, stackTop = static_cast<uintptr_t>(-1); 573 ASSERT_TRUE(unwinder->GetStackRange(stackBottom, stackTop)); 574 GTEST_LOG_(INFO) << "StepTest005: GetStackRange."; 575 576 UnwindContext context; 577 context.pid = UNWIND_TYPE_LOCAL; 578 context.stackCheck = false; 579 context.stackBottom = stackBottom; 580 context.stackTop = stackTop; 581 582 auto regs = DfxRegs::Create(); 583 auto regsData = regs->RawData(); 584 GetLocalRegs(regsData); 585 unwinder->SetRegs(regs); 586 context.regs = regs; 587 context.maps = unwinder->GetMaps(); 588 589 uintptr_t lr = *(regs->GetReg(REG_LR)); 590 uintptr_t pc = regs->GetPc(); 591 uintptr_t failSp = stackTop + 1; // arm cfa get from sp 592 regs->SetSp(failSp); 593 uintptr_t failFp = stackTop + 1; // arm64 cfa get from fp 594 regs->SetFp(failFp); 595 bool unwRet = unwinder->Step(pc, failSp, &context); 596 ASSERT_TRUE(unwRet) << "StepTest005: unwRet:" << unwRet; 597 ASSERT_EQ(lr, pc) << "StepTest005: lr callback"; 598 GTEST_LOG_(INFO) << "StepTest005: end."; 599} 600 601/** 602 * @tc.name: StepTest006 603 * @tc.desc: test unwinder Step interface in lr callback with step failed case 604 * @tc.type: FUNC 605 */ 606HWTEST_F(UnwinderTest, StepTest006, TestSize.Level2) 607{ 608 GTEST_LOG_(INFO) << "StepTest006: start."; 609 auto unwinder = std::make_shared<Unwinder>(); 610 uintptr_t stackBottom = 1, stackTop = static_cast<uintptr_t>(-1); 611 ASSERT_TRUE(unwinder->GetStackRange(stackBottom, stackTop)); 612 GTEST_LOG_(INFO) << "StepTest006: GetStackRange."; 613 614 UnwindContext context; 615 context.pid = UNWIND_TYPE_LOCAL; 616 context.stackCheck = true; 617 context.stackBottom = stackBottom; 618 context.stackTop = stackTop; 619 620 auto regs = DfxRegs::Create(); 621 auto regsData = regs->RawData(); 622 GetLocalRegs(regsData); 623 unwinder->SetRegs(regs); 624 context.regs = regs; 625 context.maps = unwinder->GetMaps(); 626 627 uintptr_t lr = *(regs->GetReg(REG_LR)); 628 uintptr_t sp = regs->GetSp(); 629 uintptr_t failPc = stackTop + 1; 630 regs->SetPc(failPc); 631 bool unwRet = unwinder->Step(failPc, sp, &context); 632 ASSERT_TRUE(unwRet) << "StepTest006: unwRet:" << unwRet; 633 ASSERT_EQ(lr, failPc) << "StepTest006: lr callback"; 634 635 GTEST_LOG_(INFO) << "StepTest006: end."; 636} 637#endif 638 639/** 640 * @tc.name: DfxConfigTest001 641 * @tc.desc: test DfxConfig class functions 642 * @tc.type: FUNC 643 */ 644HWTEST_F(UnwinderTest, DfxConfigTest001, TestSize.Level2) 645{ 646 GTEST_LOG_(INFO) << "DfxConfigTest001: start."; 647 ASSERT_EQ(DfxConfig::GetConfig().displayRegister, true); 648 ASSERT_EQ(DfxConfig::GetConfig().displayBacktrace, true); 649 ASSERT_EQ(DfxConfig::GetConfig().displayMaps, true); 650 ASSERT_EQ(DfxConfig::GetConfig().displayFaultStack, true); 651 ASSERT_EQ(DfxConfig::GetConfig().dumpOtherThreads, true); 652 ASSERT_EQ(DfxConfig::GetConfig().highAddressStep, 512); 653 ASSERT_EQ(DfxConfig::GetConfig().lowAddressStep, 16); 654 ASSERT_EQ(DfxConfig::GetConfig().maxFrameNums, 256); 655 GTEST_LOG_(INFO) << "DfxConfigTest001: end."; 656} 657 658/** 659 * @tc.name: FillFrameTest001 660 * @tc.desc: test unwinder FillFrame interface 661 * in local case 662 * @tc.type: FUNC 663 */ 664HWTEST_F(UnwinderTest, FillFrameTest001, TestSize.Level2) 665{ 666 GTEST_LOG_(INFO) << "FillFrameTest001: start."; 667 auto unwinder = std::make_shared<Unwinder>(); 668 DfxFrame frame; 669 unwinder->FillFrame(frame); 670 GTEST_LOG_(INFO) << " when DfxFrame::map is null, frame.buildId.size() is 0"; 671 ASSERT_EQ(frame.buildId.size(), 0); 672 string testMap = "f6d83000-f6d84000 r--p 00001000 b3:07 1892 /system/lib/init/libinit_context.z.so.noexit"; 673 auto map = DfxMap::Create(testMap, sizeof(testMap)); 674 frame.map = map; 675 unwinder->FillFrame(frame); 676 GTEST_LOG_(INFO) << " when DfxFrame::map is not null and file not exist, frame.buildId.size() is 0"; 677 ASSERT_EQ(frame.buildId.size(), 0); 678#ifdef __arm__ 679 testMap = "f6d83000-f6d84000 r--p 00001000 b3:07 1892 /system/lib/init/libinit_context.z.so"; 680#else 681 testMap = "7f0ab40000-7f0ab41000 r--p 00000000 b3:07 1882 /system/lib64/init/libinit_context.z.so"; 682#endif 683 map = DfxMap::Create(testMap, sizeof(testMap)); 684 frame.map = map; 685 unwinder->FillFrame(frame); 686 GTEST_LOG_(INFO) << " when DfxFrame::map is not null and file exist, frame.buildId.size() is bigger than 0"; 687 ASSERT_EQ(frame.buildId.size() == 0, false); 688 GTEST_LOG_(INFO) << "FillFrameTest001: end."; 689} 690 691/** 692 * @tc.name: FillJsFrameTest001 693 * @tc.desc: test unwinder FillJsFrame interface 694 * in local case 695 * @tc.type: FUNC 696 */ 697HWTEST_F(UnwinderTest, FillJsFrameTest001, TestSize.Level2) 698{ 699 GTEST_LOG_(INFO) << "FillJsFrameTest001: start."; 700 auto unwinder = std::make_shared<Unwinder>(); 701 DfxFrame frame; 702 unwinder->FillJsFrame(frame); 703 GTEST_LOG_(INFO) << " when DfxFrame::map is null, frame.map is nullptr"; 704 ASSERT_EQ(frame.map, nullptr); 705 string testMap = "f6d83000-f6d84000 r--p 00001000 b3:07 1892 /system/lib/init/libinit_context.z.so"; 706 auto map = DfxMap::Create(testMap, sizeof(testMap)); 707 frame.map = map; 708 unwinder->FillJsFrame(frame); 709 GTEST_LOG_(INFO) << " when DfxFrame::map is not null and file exist, frame.map.GetHap is not nullptr"; 710 ASSERT_NE(frame.map->GetHap(), nullptr); 711 GTEST_LOG_(INFO) << "FillJsFrameTest001: end."; 712} 713 714/** 715 * @tc.name: FillFramesTest001 716 * @tc.desc: test unwinder FillFrames interface 717 * in local case 718 * @tc.type: FUNC 719 */ 720HWTEST_F(UnwinderTest, FillFramesTest001, TestSize.Level2) 721{ 722 GTEST_LOG_(INFO) << "FillFramesTest001: start."; 723#ifdef __arm__ 724 const string testMap = "f6d83000-f6d84000 r--p 00001000 b3:07 1892 /system/lib/init/libinit_context.z.so"; 725#else 726 const string testMap = "7f0ab40000-7f0ab41000 r--p 00000000 b3:07 1882 /system/lib64/init/libinit_context.z.so"; 727#endif 728 auto unwinder = std::make_shared<Unwinder>(); 729 std::vector<DfxFrame> frames; 730 DfxFrame frame; 731 auto map = DfxMap::Create(testMap, sizeof(testMap)); 732 frame.map = map; 733 frames.push_back(frame); 734 ASSERT_EQ(frames[0].buildId.size(), 0); 735 unwinder->FillFrames(frames); 736 ASSERT_EQ(frames[0].buildId.size() == 0, false); 737 GTEST_LOG_(INFO) << "FillFramesTest001: end."; 738} 739 740#if defined(__arm__) || defined(__aarch64__) 741/** 742 * @tc.name: UnwindLocalWithContextTest001 743 * @tc.desc: test unwinder UnwindLocalWithContext interface 744 * in local case 745 * @tc.type: FUNC 746 */ 747HWTEST_F(UnwinderTest, UnwindLocalWithContextTest001, TestSize.Level2) 748{ 749 GTEST_LOG_(INFO) << "UnwindLocalWithContextTest001: start."; 750 auto regs = DfxRegs::Create(); 751 auto regsData = regs->RawData(); 752 GetLocalRegs(regsData); 753 ucontext_t context; 754 (void)memset_s(&context, sizeof(context), 0, sizeof(context)); 755#ifdef __arm__ 756 context.uc_mcontext.arm_r0 = *(regs->GetReg(REG_ARM_R0)); 757 context.uc_mcontext.arm_r1 = *(regs->GetReg(REG_ARM_R1)); 758 context.uc_mcontext.arm_r2 = *(regs->GetReg(REG_ARM_R2)); 759 context.uc_mcontext.arm_r3 = *(regs->GetReg(REG_ARM_R3)); 760 context.uc_mcontext.arm_r4 = *(regs->GetReg(REG_ARM_R4)); 761 context.uc_mcontext.arm_r5 = *(regs->GetReg(REG_ARM_R5)); 762 context.uc_mcontext.arm_r6 = *(regs->GetReg(REG_ARM_R6)); 763 context.uc_mcontext.arm_r7 = *(regs->GetReg(REG_ARM_R7)); 764 context.uc_mcontext.arm_r8 = *(regs->GetReg(REG_ARM_R8)); 765 context.uc_mcontext.arm_r9 = *(regs->GetReg(REG_ARM_R9)); 766 context.uc_mcontext.arm_r10 = *(regs->GetReg(REG_ARM_R10)); 767 context.uc_mcontext.arm_fp = *(regs->GetReg(REG_ARM_R11)); 768 context.uc_mcontext.arm_ip = *(regs->GetReg(REG_ARM_R12)); 769 context.uc_mcontext.arm_sp = *(regs->GetReg(REG_ARM_R13)); 770 context.uc_mcontext.arm_lr = *(regs->GetReg(REG_ARM_R14)); 771 context.uc_mcontext.arm_pc = *(regs->GetReg(REG_ARM_R15)); 772#else 773 for (int i = 0; i < REG_LAST; ++i) { 774 context.uc_mcontext.regs[i] = *(regs->GetReg(i)); 775 } 776#endif 777 auto unwinder = std::make_shared<Unwinder>(); 778 ASSERT_TRUE(unwinder->UnwindLocalWithContext(context)); 779 auto frames = unwinder->GetFrames(); 780 ASSERT_GT(frames.size(), 1); 781 GTEST_LOG_(INFO) << "UnwindLocalWithContextTest001: frames:\n" << Unwinder::GetFramesStr(frames); 782 GTEST_LOG_(INFO) << "UnwindLocalWithContextTest001: end."; 783} 784#endif 785 786static int32_t g_tid = 0; 787static std::mutex g_mutex; 788__attribute__((noinline)) void ThreadTest002() 789{ 790 printf("ThreadTest002\n"); 791 g_mutex.lock(); 792 g_mutex.unlock(); 793} 794 795__attribute__((noinline)) void ThreadTest001() 796{ 797 g_tid = gettid(); 798 printf("ThreadTest001: tid: %d\n", g_tid); 799 ThreadTest002(); 800} 801 802/** 803 * @tc.name: UnwindLocalWithTidTest001 804 * @tc.desc: test unwinder UnwindLocalWithTid interface 805 * in local case 806 * @tc.type: FUNC 807 */ 808HWTEST_F(UnwinderTest, UnwindLocalWithTidTest001, TestSize.Level2) 809{ 810 GTEST_LOG_(INFO) << "UnwindLocalWithTidTest001: start."; 811 auto unwinder = std::make_shared<Unwinder>(); 812 g_mutex.lock(); 813 std::thread unwThread(ThreadTest001); 814 sleep(1); 815 if (g_tid <= 0) { 816 FAIL() << "UnwindLocalWithTidTest001: Failed to create child thread.\n"; 817 } 818 ASSERT_TRUE(unwinder->UnwindLocalWithTid(g_tid)); 819#if defined(__aarch64__) 820 auto pcs = unwinder->GetPcs(); 821 std::vector<DfxFrame> frames; 822 unwinder->GetFramesByPcs(frames, pcs); 823#else 824 auto frames = unwinder->GetFrames(); 825#endif 826 ASSERT_GT(frames.size(), 1); 827 GTEST_LOG_(INFO) << "UnwindLocalWithTidTest001: frames:\n" << Unwinder::GetFramesStr(frames); 828 g_mutex.unlock(); 829 g_tid = 0; 830 if (unwThread.joinable()) { 831 unwThread.join(); 832 } 833 GTEST_LOG_(INFO) << "UnwindLocalWithTidTest001: end."; 834} 835 836#if defined(__x86_64__) 837static _Unwind_Reason_Code TraceFunc(_Unwind_Context *ctx, void *d) 838{ 839 int *depth = (int*)d; 840 printf("\t#%d: program counter at %p\n", *depth, reinterpret_cast<void *>(_Unwind_GetIP(ctx))); 841 (*depth)++; 842 return _URC_NO_REASON; 843} 844 845static void PrintUnwindBacktrace() 846{ 847 int depth = 0; 848 _Unwind_Backtrace(&TraceFunc, &depth); 849} 850 851/** 852 * @tc.name: UnwindLocalX86_64Test001 853 * @tc.desc: test unwinder UnwindLocal interface 854 * @tc.type: FUNC 855 */ 856HWTEST_F(UnwinderTest, UnwindLocalX86_64Test001, TestSize.Level2) 857{ 858 GTEST_LOG_(INFO) << "UnwindLocalX86_64Test001: start."; 859 auto unwinder = std::make_shared<Unwinder>(); 860 if (unwinder->UnwindLocal()) { 861 auto frames = unwinder->GetFrames(); 862 printf("Unwinder frame size: %zu\n", frames.size()); 863 auto framesStr = Unwinder::GetFramesStr(frames); 864 printf("Unwinder frames:\n%s\n", framesStr.c_str()); 865 ASSERT_GT(frames.size(), 0); 866 } 867 868 PrintUnwindBacktrace(); 869 GTEST_LOG_(INFO) << "UnwindLocalX86_64Test001: end."; 870} 871 872/** 873 * @tc.name: UnwindRemoteX86_64Test001 874 * @tc.desc: test unwinder UnwindRemote interface 875 * @tc.type: FUNC 876 */ 877HWTEST_F(UnwinderTest, UnwindRemoteX86_64Test001, TestSize.Level2) 878{ 879 GTEST_LOG_(INFO) << "UnwindLocalX86_64Test001: start."; 880 const pid_t initPid = 1; 881 auto unwinder = std::make_shared<Unwinder>(initPid); 882 DfxPtrace::Attach(initPid); 883 if (unwinder->UnwindRemote(initPid)) { 884 auto frames = unwinder->GetFrames(); 885 printf("Unwinder frame size: %zu\n", frames.size()); 886 auto framesStr = Unwinder::GetFramesStr(frames); 887 printf("Unwinder frames:\n%s\n", framesStr.c_str()); 888 ASSERT_GT(frames.size(), 0); 889 } 890 DfxPtrace::Detach(initPid); 891 892 GTEST_LOG_(INFO) << "UnwindRemoteX86_64Test001: end."; 893} 894#endif 895 896/** 897 * @tc.name: GetSymbolByPcTest001 898 * @tc.desc: test unwinder GetSymbolByPc interface 899 * in local case 900 * @tc.type: FUNC 901 */ 902HWTEST_F(UnwinderTest, GetSymbolByPcTest001, TestSize.Level2) 903{ 904 GTEST_LOG_(INFO) << "GetSymbolByPcTest001: start."; 905 auto unwinder = std::make_shared<Unwinder>(); 906 unwinder->UnwindLocal(); 907 auto frames = unwinder->GetFrames(); 908 uintptr_t pc0 = static_cast<uintptr_t>(frames[0].pc); 909 std::string funcName; 910 uint64_t funcOffset; 911 std::shared_ptr<DfxMaps> maps = std::make_shared<DfxMaps>(); 912 ASSERT_FALSE(unwinder->GetSymbolByPc(0x00000000, maps, funcName, funcOffset)); // Find map is null 913 ASSERT_FALSE(unwinder->GetSymbolByPc(pc0, maps, funcName, funcOffset)); // Get elf is null 914 GTEST_LOG_(INFO) << "GetSymbolByPcTest001: end."; 915} 916 917/** 918 * @tc.name: AccessMemTest001 919 * @tc.desc: test unwinder AccessMem interface 920 * @tc.type: FUNC 921 */ 922HWTEST_F(UnwinderTest, AccessMemTest001, TestSize.Level2) 923{ 924 GTEST_LOG_(INFO) << "AccessMemTest001: start."; 925 auto unwinder = std::make_shared<Unwinder>(); 926 auto acc = std::make_shared<DfxAccessorsLocal>(); 927 auto memory = std::make_shared<DfxMemory>(acc); 928 uintptr_t val; 929 EXPECT_FALSE(memory->ReadReg(0, &val)); 930 uintptr_t regs[] = {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa}; 931 UnwindContext ctx; 932 ctx.regs = DfxRegs::CreateFromRegs(UnwindMode::DWARF_UNWIND, regs, sizeof(regs) / sizeof(regs[0])); 933 memory->SetCtx(&ctx); 934 EXPECT_FALSE(memory->ReadReg(-1, &val)); 935 936 uint8_t values[] = {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8}; 937 uintptr_t addr = reinterpret_cast<uintptr_t>(&values[0]); 938 EXPECT_FALSE(unwinder->AccessMem(&memory, addr, nullptr)); 939 GTEST_LOG_(INFO) << "AccessMemTest001: end."; 940} 941 942/** 943 * @tc.name: UnwinderTest001 944 * @tc.desc: test Unwinder::xxxx interface 945 * @tc.type: FUNC 946 */ 947HWTEST_F(UnwinderTest, UnwinderTest001, TestSize.Level2) 948{ 949 GTEST_LOG_(INFO) << "UnwinderTest001: start."; 950 auto unwinder = std::make_shared<Unwinder>(getpid()); 951 unwinder->EnableUnwindCache(false); 952 unwinder->EnableFpCheckMapExec(false); 953 auto regs = unwinder->GetRegs(); 954 ASSERT_EQ(regs, nullptr); 955 DfxFrame frame; 956 unwinder->FillFrame(frame); 957 unwinder->AddFrame(frame); 958 unwinder->ArkWriteJitCodeToFile(1); 959 auto jitCache = unwinder->GetJitCache(); 960 ASSERT_EQ(jitCache.size(), 0); 961 GTEST_LOG_(INFO) << "UnwinderTest001: end."; 962} 963 964/** 965 * @tc.name: UnwinderTest002 966 * @tc.desc: test DfxFrameFormatter GetFrameStr 967 * @tc.type: FUNC 968 */ 969HWTEST_F(UnwinderTest, UnwinderTest002, TestSize.Level2) 970{ 971 GTEST_LOG_(INFO) << "UnwinderTest002: start."; 972 std::shared_ptr<DfxFrame> frame = nullptr; 973 std::string str = DfxFrameFormatter::GetFrameStr(frame); 974 ASSERT_EQ(str, ""); 975 std::vector<std::shared_ptr<DfxFrame>> frames; 976 str = DfxFrameFormatter::GetFramesStr(frames); 977 ASSERT_EQ(str, ""); 978 GTEST_LOG_(INFO) << "UnwinderTest002: end."; 979} 980} // namespace HiviewDFX 981} // namepsace OHOS 982 983