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