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  */
HWTEST_F(UnwinderTest, GetStackRangeTest001, TestSize.Level2)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  */
HWTEST_F(UnwinderTest, GetStackRangeTest002, TestSize.Level2)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";
93     std::thread th([unwinder, &stackBottom, &stackTop, &result] {
94         result = unwinder->GetStackRange(stackBottom, stackTop);
95     });
96     if (th.joinable()) {
97         th.join();
98     }
99     ASSERT_TRUE(result);
100     GTEST_LOG_(INFO) << "GetStackRangeTest002: end.";
101 }
102 
103 /**
104  * @tc.name: UnwinderLocalTest001
105  * @tc.desc: test unwinder local unwind
106  * @tc.type: FUNC
107  */
HWTEST_F(UnwinderTest, UnwinderLocalTest001, TestSize.Level2)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  */
HWTEST_F(UnwinderTest, UnwinderLocalTest002, TestSize.Level2)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  */
HWTEST_F(UnwinderTest, UnwinderLocalTest003, TestSize.Level2)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  */
HWTEST_F(UnwinderTest, UnwinderRemoteTest001, TestSize.Level2)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  */
HWTEST_F(UnwinderTest, UnwinderRemoteTest002, TestSize.Level2)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  */
HWTEST_F(UnwinderTest, UnwinderRemoteTest003, TestSize.Level2)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  */
HWTEST_F(UnwinderTest, UnwindTest001, TestSize.Level2)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  */
HWTEST_F(UnwinderTest, UnwindTest002, TestSize.Level2)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  */
HWTEST_F(UnwinderTest, UnwindTest003, TestSize.Level2)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  */
HWTEST_F(UnwinderTest, UnwindTest004, TestSize.Level2)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  */
HWTEST_F(UnwinderTest, StepTest001, TestSize.Level2)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  */
HWTEST_F(UnwinderTest, StepTest002, TestSize.Level2)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  */
HWTEST_F(UnwinderTest, StepTest003, TestSize.Level2)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  */
HWTEST_F(UnwinderTest, StepTest004, TestSize.Level2)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  */
HWTEST_F(UnwinderTest, StepTest005, TestSize.Level2)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  */
HWTEST_F(UnwinderTest, StepTest006, TestSize.Level2)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  */
HWTEST_F(UnwinderTest, DfxConfigTest001, TestSize.Level2)644 HWTEST_F(UnwinderTest, DfxConfigTest001, TestSize.Level2)
645 {
646     GTEST_LOG_(INFO) << "DfxConfigTest001: start.";
647     ASSERT_EQ(DfxConfig::GetConfig().displayRegister, true);
648     ASSERT_EQ(DfxConfig::GetConfig().displayBacktrace, true);
649     ASSERT_EQ(DfxConfig::GetConfig().displayMaps, true);
650     ASSERT_EQ(DfxConfig::GetConfig().displayFaultStack, true);
651     ASSERT_EQ(DfxConfig::GetConfig().dumpOtherThreads, true);
652     ASSERT_EQ(DfxConfig::GetConfig().highAddressStep, 512);
653     ASSERT_EQ(DfxConfig::GetConfig().lowAddressStep, 16);
654     ASSERT_EQ(DfxConfig::GetConfig().maxFrameNums, 256);
655     GTEST_LOG_(INFO) << "DfxConfigTest001: end.";
656 }
657 
658 /**
659  * @tc.name: FillFrameTest001
660  * @tc.desc: test unwinder FillFrame interface
661  *  in local case
662  * @tc.type: FUNC
663  */
HWTEST_F(UnwinderTest, FillFrameTest001, TestSize.Level2)664 HWTEST_F(UnwinderTest, FillFrameTest001, TestSize.Level2)
665 {
666     GTEST_LOG_(INFO) << "FillFrameTest001: start.";
667     auto unwinder = std::make_shared<Unwinder>();
668     DfxFrame frame;
669     unwinder->FillFrame(frame);
670     GTEST_LOG_(INFO) << " when DfxFrame::map is null, frame.buildId.size() is 0";
671     ASSERT_EQ(frame.buildId.size(), 0);
672     string testMap = "f6d83000-f6d84000 r--p 00001000 b3:07 1892 /system/lib/init/libinit_context.z.so.noexit";
673     auto map = DfxMap::Create(testMap, sizeof(testMap));
674     frame.map = map;
675     unwinder->FillFrame(frame);
676     GTEST_LOG_(INFO) << " when DfxFrame::map is not null and file not exist, frame.buildId.size() is 0";
677     ASSERT_EQ(frame.buildId.size(), 0);
678 #ifdef __arm__
679     testMap = "f6d83000-f6d84000 r--p 00001000 b3:07 1892 /system/lib/init/libinit_context.z.so";
680 #else
681     testMap = "7f0ab40000-7f0ab41000 r--p 00000000 b3:07 1882 /system/lib64/init/libinit_context.z.so";
682 #endif
683     map = DfxMap::Create(testMap, sizeof(testMap));
684     frame.map = map;
685     unwinder->FillFrame(frame);
686     GTEST_LOG_(INFO) << " when DfxFrame::map is not null and file exist, frame.buildId.size() is bigger than 0";
687     ASSERT_EQ(frame.buildId.size() == 0, false);
688     GTEST_LOG_(INFO) << "FillFrameTest001: end.";
689 }
690 
691 /**
692  * @tc.name: FillJsFrameTest001
693  * @tc.desc: test unwinder FillJsFrame interface
694  *  in local case
695  * @tc.type: FUNC
696  */
HWTEST_F(UnwinderTest, FillJsFrameTest001, TestSize.Level2)697 HWTEST_F(UnwinderTest, FillJsFrameTest001, TestSize.Level2)
698 {
699     GTEST_LOG_(INFO) << "FillJsFrameTest001: start.";
700     auto unwinder = std::make_shared<Unwinder>();
701     DfxFrame frame;
702     unwinder->FillJsFrame(frame);
703     GTEST_LOG_(INFO) << " when DfxFrame::map is null, frame.map is nullptr";
704     ASSERT_EQ(frame.map, nullptr);
705     string testMap = "f6d83000-f6d84000 r--p 00001000 b3:07 1892 /system/lib/init/libinit_context.z.so";
706     auto map = DfxMap::Create(testMap, sizeof(testMap));
707     frame.map = map;
708     unwinder->FillJsFrame(frame);
709     GTEST_LOG_(INFO) << " when DfxFrame::map is not null and file exist, frame.map.GetHap is not nullptr";
710     ASSERT_NE(frame.map->GetHap(), nullptr);
711     GTEST_LOG_(INFO) << "FillJsFrameTest001: end.";
712 }
713 
714 /**
715  * @tc.name: FillFramesTest001
716  * @tc.desc: test unwinder FillFrames interface
717  *  in local case
718  * @tc.type: FUNC
719  */
HWTEST_F(UnwinderTest, FillFramesTest001, TestSize.Level2)720 HWTEST_F(UnwinderTest, FillFramesTest001, TestSize.Level2)
721 {
722     GTEST_LOG_(INFO) << "FillFramesTest001: start.";
723 #ifdef __arm__
724     const string testMap = "f6d83000-f6d84000 r--p 00001000 b3:07 1892 /system/lib/init/libinit_context.z.so";
725 #else
726     const string testMap = "7f0ab40000-7f0ab41000 r--p 00000000 b3:07 1882 /system/lib64/init/libinit_context.z.so";
727 #endif
728     auto unwinder = std::make_shared<Unwinder>();
729     std::vector<DfxFrame> frames;
730     DfxFrame frame;
731     auto map = DfxMap::Create(testMap, sizeof(testMap));
732     frame.map = map;
733     frames.push_back(frame);
734     ASSERT_EQ(frames[0].buildId.size(), 0);
735     unwinder->FillFrames(frames);
736     ASSERT_EQ(frames[0].buildId.size() == 0, false);
737     GTEST_LOG_(INFO) << "FillFramesTest001: end.";
738 }
739 
740 #if defined(__arm__) || defined(__aarch64__)
741 /**
742  * @tc.name: UnwindLocalWithContextTest001
743  * @tc.desc: test unwinder UnwindLocalWithContext interface
744  *  in local case
745  * @tc.type: FUNC
746  */
HWTEST_F(UnwinderTest, UnwindLocalWithContextTest001, TestSize.Level2)747 HWTEST_F(UnwinderTest, UnwindLocalWithContextTest001, TestSize.Level2)
748 {
749     GTEST_LOG_(INFO) << "UnwindLocalWithContextTest001: start.";
750     auto regs = DfxRegs::Create();
751     auto regsData = regs->RawData();
752     GetLocalRegs(regsData);
753     ucontext_t context;
754     (void)memset_s(&context, sizeof(context), 0, sizeof(context));
755 #ifdef __arm__
756     context.uc_mcontext.arm_r0 = *(regs->GetReg(REG_ARM_R0));
757     context.uc_mcontext.arm_r1 = *(regs->GetReg(REG_ARM_R1));
758     context.uc_mcontext.arm_r2 = *(regs->GetReg(REG_ARM_R2));
759     context.uc_mcontext.arm_r3 = *(regs->GetReg(REG_ARM_R3));
760     context.uc_mcontext.arm_r4 = *(regs->GetReg(REG_ARM_R4));
761     context.uc_mcontext.arm_r5 = *(regs->GetReg(REG_ARM_R5));
762     context.uc_mcontext.arm_r6 = *(regs->GetReg(REG_ARM_R6));
763     context.uc_mcontext.arm_r7 = *(regs->GetReg(REG_ARM_R7));
764     context.uc_mcontext.arm_r8 = *(regs->GetReg(REG_ARM_R8));
765     context.uc_mcontext.arm_r9 = *(regs->GetReg(REG_ARM_R9));
766     context.uc_mcontext.arm_r10 = *(regs->GetReg(REG_ARM_R10));
767     context.uc_mcontext.arm_fp = *(regs->GetReg(REG_ARM_R11));
768     context.uc_mcontext.arm_ip = *(regs->GetReg(REG_ARM_R12));
769     context.uc_mcontext.arm_sp = *(regs->GetReg(REG_ARM_R13));
770     context.uc_mcontext.arm_lr = *(regs->GetReg(REG_ARM_R14));
771     context.uc_mcontext.arm_pc = *(regs->GetReg(REG_ARM_R15));
772 #else
773     for (int i = 0; i < REG_LAST; ++i) {
774         context.uc_mcontext.regs[i] = *(regs->GetReg(i));
775     }
776 #endif
777     auto unwinder = std::make_shared<Unwinder>();
778     ASSERT_TRUE(unwinder->UnwindLocalWithContext(context));
779     auto frames = unwinder->GetFrames();
780     ASSERT_GT(frames.size(), 1);
781     GTEST_LOG_(INFO) << "UnwindLocalWithContextTest001: frames:\n" << Unwinder::GetFramesStr(frames);
782     GTEST_LOG_(INFO) << "UnwindLocalWithContextTest001: end.";
783 }
784 #endif
785 
786 static int32_t g_tid = 0;
787 static std::mutex g_mutex;
ThreadTest002()788 __attribute__((noinline)) void ThreadTest002()
789 {
790     printf("ThreadTest002\n");
791     g_mutex.lock();
792     g_mutex.unlock();
793 }
794 
ThreadTest001()795 __attribute__((noinline)) void ThreadTest001()
796 {
797     g_tid = gettid();
798     printf("ThreadTest001: tid: %d\n", g_tid);
799     ThreadTest002();
800 }
801 
802 /**
803  * @tc.name: UnwindLocalWithTidTest001
804  * @tc.desc: test unwinder UnwindLocalWithTid interface
805  *  in local case
806  * @tc.type: FUNC
807  */
HWTEST_F(UnwinderTest, UnwindLocalWithTidTest001, TestSize.Level2)808 HWTEST_F(UnwinderTest, UnwindLocalWithTidTest001, TestSize.Level2)
809 {
810     GTEST_LOG_(INFO) << "UnwindLocalWithTidTest001: start.";
811     auto unwinder = std::make_shared<Unwinder>();
812     g_mutex.lock();
813     std::thread unwThread(ThreadTest001);
814     sleep(1);
815     if (g_tid <= 0) {
816         FAIL() << "UnwindLocalWithTidTest001: Failed to create child thread.\n";
817     }
818     ASSERT_TRUE(unwinder->UnwindLocalWithTid(g_tid));
819 #if defined(__aarch64__)
820     auto pcs = unwinder->GetPcs();
821     std::vector<DfxFrame> frames;
822     unwinder->GetFramesByPcs(frames, pcs);
823 #else
824     auto frames = unwinder->GetFrames();
825 #endif
826     ASSERT_GT(frames.size(), 1);
827     GTEST_LOG_(INFO) << "UnwindLocalWithTidTest001: frames:\n" << Unwinder::GetFramesStr(frames);
828     g_mutex.unlock();
829     g_tid = 0;
830     if (unwThread.joinable()) {
831         unwThread.join();
832     }
833     GTEST_LOG_(INFO) << "UnwindLocalWithTidTest001: end.";
834 }
835 
836 #if defined(__x86_64__)
TraceFunc(_Unwind_Context *ctx, void *d)837 static _Unwind_Reason_Code TraceFunc(_Unwind_Context *ctx, void *d)
838 {
839     int *depth = (int*)d;
840     printf("\t#%d: program counter at %p\n", *depth, reinterpret_cast<void *>(_Unwind_GetIP(ctx)));
841     (*depth)++;
842     return _URC_NO_REASON;
843 }
844 
PrintUnwindBacktrace()845 static void PrintUnwindBacktrace()
846 {
847     int depth = 0;
848     _Unwind_Backtrace(&TraceFunc, &depth);
849 }
850 
851 /**
852  * @tc.name: UnwindLocalX86_64Test001
853  * @tc.desc: test unwinder UnwindLocal interface
854  * @tc.type: FUNC
855  */
HWTEST_F(UnwinderTest, UnwindLocalX86_64Test001, TestSize.Level2)856 HWTEST_F(UnwinderTest, UnwindLocalX86_64Test001, TestSize.Level2)
857 {
858     GTEST_LOG_(INFO) << "UnwindLocalX86_64Test001: start.";
859     auto unwinder = std::make_shared<Unwinder>();
860     if (unwinder->UnwindLocal()) {
861         auto frames = unwinder->GetFrames();
862         printf("Unwinder frame size: %zu\n", frames.size());
863         auto framesStr = Unwinder::GetFramesStr(frames);
864         printf("Unwinder frames:\n%s\n", framesStr.c_str());
865         ASSERT_GT(frames.size(), 0);
866     }
867 
868     PrintUnwindBacktrace();
869     GTEST_LOG_(INFO) << "UnwindLocalX86_64Test001: end.";
870 }
871 
872 /**
873  * @tc.name: UnwindRemoteX86_64Test001
874  * @tc.desc: test unwinder UnwindRemote interface
875  * @tc.type: FUNC
876  */
HWTEST_F(UnwinderTest, UnwindRemoteX86_64Test001, TestSize.Level2)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         ASSERT_GT(frames.size(), 0);
889     }
890     DfxPtrace::Detach(initPid);
891 
892     GTEST_LOG_(INFO) << "UnwindRemoteX86_64Test001: end.";
893 }
894 #endif
895 
896 /**
897  * @tc.name: GetSymbolByPcTest001
898  * @tc.desc: test unwinder GetSymbolByPc interface
899  *  in local case
900  * @tc.type: FUNC
901  */
HWTEST_F(UnwinderTest, GetSymbolByPcTest001, TestSize.Level2)902 HWTEST_F(UnwinderTest, GetSymbolByPcTest001, TestSize.Level2)
903 {
904     GTEST_LOG_(INFO) << "GetSymbolByPcTest001: start.";
905     auto unwinder = std::make_shared<Unwinder>();
906     unwinder->UnwindLocal();
907     auto frames = unwinder->GetFrames();
908     uintptr_t pc0 = static_cast<uintptr_t>(frames[0].pc);
909     std::string funcName;
910     uint64_t funcOffset;
911     std::shared_ptr<DfxMaps> maps = std::make_shared<DfxMaps>();
912     ASSERT_FALSE(unwinder->GetSymbolByPc(0x00000000, maps, funcName, funcOffset)); // Find map is null
913     ASSERT_FALSE(unwinder->GetSymbolByPc(pc0, maps, funcName, funcOffset)); // Get elf is null
914     GTEST_LOG_(INFO) << "GetSymbolByPcTest001: end.";
915 }
916 
917 /**
918  * @tc.name: AccessMemTest001
919  * @tc.desc: test unwinder AccessMem interface
920  * @tc.type: FUNC
921  */
HWTEST_F(UnwinderTest, AccessMemTest001, TestSize.Level2)922 HWTEST_F(UnwinderTest, AccessMemTest001, TestSize.Level2)
923 {
924     GTEST_LOG_(INFO) << "AccessMemTest001: start.";
925     auto unwinder = std::make_shared<Unwinder>();
926     auto acc = std::make_shared<DfxAccessorsLocal>();
927     auto memory = std::make_shared<DfxMemory>(acc);
928     uintptr_t val;
929     EXPECT_FALSE(memory->ReadReg(0, &val));
930     uintptr_t regs[] = {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xa};
931     UnwindContext ctx;
932     ctx.regs = DfxRegs::CreateFromRegs(UnwindMode::DWARF_UNWIND, regs, sizeof(regs) / sizeof(regs[0]));
933     memory->SetCtx(&ctx);
934     EXPECT_FALSE(memory->ReadReg(-1, &val));
935 
936     uint8_t values[] = {0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8};
937     uintptr_t addr = reinterpret_cast<uintptr_t>(&values[0]);
938     EXPECT_FALSE(unwinder->AccessMem(&memory, addr, nullptr));
939     GTEST_LOG_(INFO) << "AccessMemTest001: end.";
940 }
941 
942 /**
943  * @tc.name: UnwinderTest001
944  * @tc.desc: test Unwinder::xxxx interface
945  * @tc.type: FUNC
946  */
HWTEST_F(UnwinderTest, UnwinderTest001, TestSize.Level2)947 HWTEST_F(UnwinderTest, UnwinderTest001, TestSize.Level2)
948 {
949     GTEST_LOG_(INFO) << "UnwinderTest001: start.";
950     auto unwinder = std::make_shared<Unwinder>(getpid());
951     unwinder->EnableUnwindCache(false);
952     unwinder->EnableFpCheckMapExec(false);
953     auto regs = unwinder->GetRegs();
954     ASSERT_EQ(regs, nullptr);
955     DfxFrame frame;
956     unwinder->FillFrame(frame);
957     unwinder->AddFrame(frame);
958     unwinder->ArkWriteJitCodeToFile(1);
959     auto jitCache = unwinder->GetJitCache();
960     ASSERT_EQ(jitCache.size(), 0);
961     GTEST_LOG_(INFO) << "UnwinderTest001: end.";
962 }
963 
964 /**
965  * @tc.name: UnwinderTest002
966  * @tc.desc: test DfxFrameFormatter GetFrameStr
967  * @tc.type: FUNC
968  */
HWTEST_F(UnwinderTest, UnwinderTest002, TestSize.Level2)969 HWTEST_F(UnwinderTest, UnwinderTest002, TestSize.Level2)
970 {
971     GTEST_LOG_(INFO) << "UnwinderTest002: start.";
972     std::shared_ptr<DfxFrame> frame = nullptr;
973     std::string str = DfxFrameFormatter::GetFrameStr(frame);
974     ASSERT_EQ(str, "");
975     std::vector<std::shared_ptr<DfxFrame>> frames;
976     str = DfxFrameFormatter::GetFramesStr(frames);
977     ASSERT_EQ(str, "");
978     GTEST_LOG_(INFO) << "UnwinderTest002: end.";
979 }
980 } // namespace HiviewDFX
981 } // namepsace OHOS
982 
983