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";
__anon0ec0e5150102null93 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 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 */
135 HWTEST_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 */
169 HWTEST_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 */
189 HWTEST_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 */
225 HWTEST_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 */
267 HWTEST_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 */
284 HWTEST_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 */
322 HWTEST_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 */
356 HWTEST_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 */
381 HWTEST_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 */
408 HWTEST_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 */
446 HWTEST_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 */
481 HWTEST_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 */
517 HWTEST_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 */
568 HWTEST_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 */
606 HWTEST_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 */
644 HWTEST_F(UnwinderTest, DfxConfigTest001, TestSize.Level2)
645 {
646 GTEST_LOG_(INFO) << "DfxConfigTest001: start.";
647 ASSERT_EQ(DfxConfig::GetConfig().logPersist, false);
648 ASSERT_EQ(DfxConfig::GetConfig().displayRegister, true);
649 ASSERT_EQ(DfxConfig::GetConfig().displayBacktrace, true);
650 ASSERT_EQ(DfxConfig::GetConfig().displayMaps, true);
651 ASSERT_EQ(DfxConfig::GetConfig().displayFaultStack, true);
652 ASSERT_EQ(DfxConfig::GetConfig().dumpOtherThreads, true);
653 ASSERT_EQ(DfxConfig::GetConfig().highAddressStep, 512);
654 ASSERT_EQ(DfxConfig::GetConfig().lowAddressStep, 16);
655 ASSERT_EQ(DfxConfig::GetConfig().maxFrameNums, 256);
656 GTEST_LOG_(INFO) << "DfxConfigTest001: end.";
657 }
658
659 /**
660 * @tc.name: FillFrameTest001
661 * @tc.desc: test unwinder FillFrame interface
662 * in local case
663 * @tc.type: FUNC
664 */
665 HWTEST_F(UnwinderTest, FillFrameTest001, TestSize.Level2)
666 {
667 GTEST_LOG_(INFO) << "FillFrameTest001: start.";
668 auto unwinder = std::make_shared<Unwinder>();
669 DfxFrame frame;
670 unwinder->FillFrame(frame);
671 GTEST_LOG_(INFO) << " when DfxFrame::map is null, frame.buildId.size() is 0";
672 ASSERT_EQ(frame.buildId.size(), 0);
673 string testMap = "f6d83000-f6d84000 r--p 00001000 b3:07 1892 /system/lib/init/libinit_context.z.so.noexit";
674 auto map = DfxMap::Create(testMap, sizeof(testMap));
675 frame.map = map;
676 unwinder->FillFrame(frame);
677 GTEST_LOG_(INFO) << " when DfxFrame::map is not null and file not exist, frame.buildId.size() is 0";
678 ASSERT_EQ(frame.buildId.size(), 0);
679 #ifdef __arm__
680 testMap = "f6d83000-f6d84000 r--p 00001000 b3:07 1892 /system/lib/init/libinit_context.z.so";
681 #else
682 testMap = "7f0ab40000-7f0ab41000 r--p 00000000 b3:07 1882 /system/lib64/init/libinit_context.z.so";
683 #endif
684 map = DfxMap::Create(testMap, sizeof(testMap));
685 frame.map = map;
686 unwinder->FillFrame(frame);
687 GTEST_LOG_(INFO) << " when DfxFrame::map is not null and file exist, frame.buildId.size() is bigger than 0";
688 ASSERT_EQ(frame.buildId.size() == 0, false);
689 GTEST_LOG_(INFO) << "FillFrameTest001: end.";
690 }
691
692 /**
693 * @tc.name: FillJsFrameTest001
694 * @tc.desc: test unwinder FillJsFrame interface
695 * in local case
696 * @tc.type: FUNC
697 */
698 HWTEST_F(UnwinderTest, FillJsFrameTest001, TestSize.Level2)
699 {
700 GTEST_LOG_(INFO) << "FillJsFrameTest001: start.";
701 auto unwinder = std::make_shared<Unwinder>();
702 DfxFrame frame;
703 unwinder->FillJsFrame(frame);
704 GTEST_LOG_(INFO) << " when DfxFrame::map is null, frame.map is nullptr";
705 ASSERT_EQ(frame.map, nullptr);
706 string testMap = "f6d83000-f6d84000 r--p 00001000 b3:07 1892 /system/lib/init/libinit_context.z.so";
707 auto map = DfxMap::Create(testMap, sizeof(testMap));
708 frame.map = map;
709 unwinder->FillJsFrame(frame);
710 GTEST_LOG_(INFO) << " when DfxFrame::map is not null and file exist, frame.map.GetHap is not nullptr";
711 ASSERT_NE(frame.map->GetHap(), nullptr);
712 GTEST_LOG_(INFO) << "FillJsFrameTest001: end.";
713 }
714
715 /**
716 * @tc.name: FillFramesTest001
717 * @tc.desc: test unwinder FillFrames interface
718 * in local case
719 * @tc.type: FUNC
720 */
721 HWTEST_F(UnwinderTest, FillFramesTest001, TestSize.Level2)
722 {
723 GTEST_LOG_(INFO) << "FillFramesTest001: start.";
724 #ifdef __arm__
725 const string testMap = "f6d83000-f6d84000 r--p 00001000 b3:07 1892 /system/lib/init/libinit_context.z.so";
726 #else
727 const string testMap = "7f0ab40000-7f0ab41000 r--p 00000000 b3:07 1882 /system/lib64/init/libinit_context.z.so";
728 #endif
729 auto unwinder = std::make_shared<Unwinder>();
730 std::vector<DfxFrame> frames;
731 DfxFrame frame;
732 auto map = DfxMap::Create(testMap, sizeof(testMap));
733 frame.map = map;
734 frames.push_back(frame);
735 ASSERT_EQ(frames[0].buildId.size(), 0);
736 unwinder->FillFrames(frames);
737 ASSERT_EQ(frames[0].buildId.size() == 0, false);
738 GTEST_LOG_(INFO) << "FillFramesTest001: end.";
739 }
740
741 #if defined(__arm__) || defined(__aarch64__)
742 /**
743 * @tc.name: UnwindLocalWithContextTest001
744 * @tc.desc: test unwinder UnwindLocalWithContext interface
745 * in local case
746 * @tc.type: FUNC
747 */
748 HWTEST_F(UnwinderTest, UnwindLocalWithContextTest001, TestSize.Level2)
749 {
750 GTEST_LOG_(INFO) << "UnwindLocalWithContextTest001: start.";
751 auto regs = DfxRegs::Create();
752 auto regsData = regs->RawData();
753 GetLocalRegs(regsData);
754 ucontext_t context;
755 (void)memset_s(&context, sizeof(context), 0, sizeof(context));
756 #ifdef __arm__
757 context.uc_mcontext.arm_r0 = *(regs->GetReg(REG_ARM_R0));
758 context.uc_mcontext.arm_r1 = *(regs->GetReg(REG_ARM_R1));
759 context.uc_mcontext.arm_r2 = *(regs->GetReg(REG_ARM_R2));
760 context.uc_mcontext.arm_r3 = *(regs->GetReg(REG_ARM_R3));
761 context.uc_mcontext.arm_r4 = *(regs->GetReg(REG_ARM_R4));
762 context.uc_mcontext.arm_r5 = *(regs->GetReg(REG_ARM_R5));
763 context.uc_mcontext.arm_r6 = *(regs->GetReg(REG_ARM_R6));
764 context.uc_mcontext.arm_r7 = *(regs->GetReg(REG_ARM_R7));
765 context.uc_mcontext.arm_r8 = *(regs->GetReg(REG_ARM_R8));
766 context.uc_mcontext.arm_r9 = *(regs->GetReg(REG_ARM_R9));
767 context.uc_mcontext.arm_r10 = *(regs->GetReg(REG_ARM_R10));
768 context.uc_mcontext.arm_fp = *(regs->GetReg(REG_ARM_R11));
769 context.uc_mcontext.arm_ip = *(regs->GetReg(REG_ARM_R12));
770 context.uc_mcontext.arm_sp = *(regs->GetReg(REG_ARM_R13));
771 context.uc_mcontext.arm_lr = *(regs->GetReg(REG_ARM_R14));
772 context.uc_mcontext.arm_pc = *(regs->GetReg(REG_ARM_R15));
773 #else
774 for (int i = 0; i < REG_LAST; ++i) {
775 context.uc_mcontext.regs[i] = *(regs->GetReg(i));
776 }
777 #endif
778 auto unwinder = std::make_shared<Unwinder>();
779 ASSERT_TRUE(unwinder->UnwindLocalWithContext(context));
780 auto frames = unwinder->GetFrames();
781 ASSERT_GT(frames.size(), 1);
782 GTEST_LOG_(INFO) << "UnwindLocalWithContextTest001: frames:\n" << Unwinder::GetFramesStr(frames);
783 GTEST_LOG_(INFO) << "UnwindLocalWithContextTest001: end.";
784 }
785 #endif
786
787 static int32_t g_tid = 0;
788 static std::mutex g_mutex;
ThreadTest002()789 __attribute__((noinline)) void ThreadTest002()
790 {
791 printf("ThreadTest002\n");
792 g_mutex.lock();
793 g_mutex.unlock();
794 }
795
ThreadTest001()796 __attribute__((noinline)) void ThreadTest001()
797 {
798 g_tid = gettid();
799 printf("ThreadTest001: tid: %d\n", g_tid);
800 ThreadTest002();
801 }
802
803 /**
804 * @tc.name: UnwindLocalWithTidTest001
805 * @tc.desc: test unwinder UnwindLocalWithTid interface
806 * in local case
807 * @tc.type: FUNC
808 */
809 HWTEST_F(UnwinderTest, UnwindLocalWithTidTest001, TestSize.Level2)
810 {
811 GTEST_LOG_(INFO) << "UnwindLocalWithTidTest001: start.";
812 auto unwinder = std::make_shared<Unwinder>();
813 g_mutex.lock();
814 std::thread unwThread(ThreadTest001);
815 sleep(1);
816 if (g_tid <= 0) {
817 FAIL() << "UnwindLocalWithTidTest001: Failed to create child thread.\n";
818 }
819 ASSERT_TRUE(unwinder->UnwindLocalWithTid(g_tid));
820 #if defined(__aarch64__)
821 auto pcs = unwinder->GetPcs();
822 std::vector<DfxFrame> frames;
823 unwinder->GetFramesByPcs(frames, pcs);
824 #else
825 auto frames = unwinder->GetFrames();
826 #endif
827 ASSERT_GT(frames.size(), 1);
828 GTEST_LOG_(INFO) << "UnwindLocalWithTidTest001: frames:\n" << Unwinder::GetFramesStr(frames);
829 g_mutex.unlock();
830 g_tid = 0;
831 if (unwThread.joinable()) {
832 unwThread.join();
833 }
834 GTEST_LOG_(INFO) << "UnwindLocalWithTidTest001: end.";
835 }
836
837 #if defined(__x86_64__)
TraceFunc(_Unwind_Context * ctx,void * d)838 static _Unwind_Reason_Code TraceFunc(_Unwind_Context *ctx, void *d)
839 {
840 int *depth = (int*)d;
841 printf("\t#%d: program counter at %p\n", *depth, reinterpret_cast<void *>(_Unwind_GetIP(ctx)));
842 (*depth)++;
843 return _URC_NO_REASON;
844 }
845
PrintUnwindBacktrace()846 static void PrintUnwindBacktrace()
847 {
848 int depth = 0;
849 _Unwind_Backtrace(&TraceFunc, &depth);
850 }
851
852 /**
853 * @tc.name: UnwindLocalX86_64Test001
854 * @tc.desc: test unwinder UnwindLocal interface
855 * @tc.type: FUNC
856 */
857 HWTEST_F(UnwinderTest, UnwindLocalX86_64Test001, TestSize.Level2)
858 {
859 GTEST_LOG_(INFO) << "UnwindLocalX86_64Test001: start.";
860 auto unwinder = std::make_shared<Unwinder>();
861 if (unwinder->UnwindLocal()) {
862 auto frames = unwinder->GetFrames();
863 printf("Unwinder frame size: %zu\n", frames.size());
864 auto framesStr = Unwinder::GetFramesStr(frames);
865 printf("Unwinder frames:\n%s\n", framesStr.c_str());
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 */
877 HWTEST_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 }
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 } // namespace HiviewDFX
980 } // namepsace OHOS
981
982