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