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