1/*
2 * Copyright (c) 2022-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#include "dfx_unwind_async_thread.h"
16
17#include "crash_exception.h"
18#include "dfx_config.h"
19#include "dfx_log.h"
20#include "dfx_memory.h"
21#include "dfx_maps.h"
22#include "dfx_regs.h"
23#include "dfx_ring_buffer_wrapper.h"
24#include "dfx_trace.h"
25#ifdef PARSE_LOCK_OWNER
26#include "lock_parser.h"
27#endif
28#include "process_dumper.h"
29#include "printer.h"
30#include "unique_stack_table.h"
31
32namespace OHOS {
33namespace HiviewDFX {
34bool DfxUnwindAsyncThread::UnwindStack(pid_t vmPid)
35{
36    if (unwinder_ == nullptr || thread_ == nullptr) {
37        DFXLOGE("%{public}s::thread or unwinder is not initialized.", __func__);
38        return false;
39    }
40    // 1: get crash stack
41    // unwinding with context passed by dump request, only for crash thread or target thread.
42    auto regs = thread_->GetThreadRegs();
43    unwinder_->SetRegs(regs);
44    pid_t tid = thread_->threadInfo_.nsTid;
45    DFXLOGI("%{public}s, unwind tid(%{public}d) start.", __func__, tid);
46    auto tmpPid = vmPid != 0 ? vmPid : tid;
47    DFX_TRACE_START("KeyThreadUnwindRemote:%d", tid);
48    bool ret = unwinder_->UnwindRemote(tmpPid,
49                                       regs != nullptr,
50                                       DfxConfig::GetConfig().maxFrameNums);
51    DFX_TRACE_FINISH();
52    if (ProcessDumper::GetInstance().IsCrash()) {
53        ReportUnwinderException(unwinder_->GetLastErrorCode());
54        if (!ret) {
55            UnwindThreadFallback();
56        }
57        DFX_TRACE_START("KeyThreadGetFrames:%d", tid);
58        thread_->SetFrames(unwinder_->GetFrames());
59        DFX_TRACE_FINISH();
60        UnwindThreadByParseStackIfNeed();
61        // 2: get submitterStack
62        std::vector<DfxFrame> submmiterFrames;
63        GetSubmitterStack(submmiterFrames);
64        // 3: merge two stack
65        MergeStack(submmiterFrames);
66    } else {
67#ifdef PARSE_LOCK_OWNER
68        DFX_TRACE_START("KeyThreadGetFrames:%d", tid);
69        thread_->SetFrames(unwinder_->GetFrames());
70        DFX_TRACE_FINISH();
71        LockParser::ParseLockInfo(unwinder_, tmpPid, thread_->threadInfo_.nsTid);
72#else
73        thread_->Detach();
74        DFX_TRACE_START("KeyThreadGetFrames:%d", tid);
75        thread_->SetFrames(unwinder_->GetFrames());
76        DFX_TRACE_FINISH();
77#endif
78    }
79
80    DFXLOGI("%{public}s, unwind tid(%{public}d) finish ret(%{public}d).", __func__, tid, ret);
81    return ret;
82}
83
84void DfxUnwindAsyncThread::GetSubmitterStack(std::vector<DfxFrame> &submitterFrames)
85{
86    if (stackId_ == 0) {
87        return;
88    }
89    const std::shared_ptr<DfxMaps>& maps = unwinder_->GetMaps();
90    if (maps == nullptr) {
91        return;
92    }
93    std::vector<std::shared_ptr<DfxMap>> mapVec;
94    if (!maps->FindMapsByName("[anon:async_stack_table]", mapVec)) {
95        DFXLOGE("%{public}s::Can not find map of async stack table", __func__);
96        return;
97    }
98    auto map = mapVec.front();
99    size_t size = map->end - map->begin;
100    auto tableData = std::make_shared<std::vector<uint8_t>>(size);
101    size_t byte = DfxMemory::ReadProcMemByPid(thread_->threadInfo_.nsTid, map->begin, tableData->data(), size);
102    if (byte != size) {
103        DFXLOGE("Failed to read unique_table from target");
104        return;
105    }
106    auto table = std::make_shared<UniqueStackTable>(tableData->data(), size, false);
107    std::vector<uintptr_t> pcs;
108    StackId id;
109    id.value = stackId_;
110    if (table->GetPcsByStackId(id, pcs)) {
111        unwinder_->GetFramesByPcs(submitterFrames, pcs);
112    } else {
113        DFXLOGW("%{public}s::Failed to get pcs", __func__);
114    }
115}
116
117void DfxUnwindAsyncThread::MergeStack(std::vector<DfxFrame> &submitterFrames)
118{
119    auto frames = thread_->GetFrames();
120    frames.insert(frames.end(), submitterFrames.begin(), submitterFrames.end());
121    thread_->SetFrames(frames);
122}
123
124void DfxUnwindAsyncThread::UnwindThreadFallback()
125{
126#ifndef __x86_64__
127    if (unwinder_->GetFrames().size() > 0) {
128        return;
129    }
130    // As we failed to init libunwind, just print pc and lr for first two frames
131    std::shared_ptr<DfxRegs> regs = thread_->GetThreadRegs();
132    if (regs == nullptr) {
133        DfxRingBufferWrapper::GetInstance().AppendMsg("RegisterInfo is not existed for crash process");
134        return;
135    }
136
137    std::shared_ptr<DfxMaps> maps = unwinder_->GetMaps();
138    if (maps == nullptr) {
139        DfxRingBufferWrapper::GetInstance().AppendMsg("MapsInfo is not existed for crash process");
140        return;
141    }
142    std::shared_ptr<Unwinder> unwinder = unwinder_;
143
144    auto createFrame = [maps, unwinder] (size_t index, uintptr_t pc, uintptr_t sp = 0) {
145        std::shared_ptr<DfxMap> map;
146        DfxFrame frame;
147        frame.pc = pc;
148        frame.sp = sp;
149        frame.index = index;
150        if (maps->FindMapByAddr(pc, map)) {
151            frame.relPc = map->GetRelPc(pc);
152            frame.mapName = map->name;
153        } else {
154            frame.relPc = pc;
155            frame.mapName = (index == 0 ? "Not mapped pc" : "Not mapped lr");
156        }
157        unwinder->AddFrame(frame);
158    };
159
160    createFrame(0, regs->GetPc(), regs->GetSp());
161    createFrame(1, *(regs->GetReg(REG_LR)));
162#endif
163}
164
165void DfxUnwindAsyncThread::UnwindThreadByParseStackIfNeed()
166{
167    if (thread_ == nullptr || unwinder_ == nullptr) {
168        return;
169    }
170    auto frames = thread_->GetFrames();
171    constexpr int minFramesNum = 3;
172    size_t initSize = frames.size();
173    if (initSize < minFramesNum || frames[minFramesNum - 1].mapName.find("Not mapped") != std::string::npos ||
174        frames[minFramesNum - 1].mapName.find("[Unknown]") != std::string::npos) {
175        bool needParseStack = true;
176        thread_->InitFaultStack(needParseStack);
177        auto faultStack = thread_->GetFaultStack();
178        if (faultStack == nullptr || !faultStack->ParseUnwindStack(unwinder_->GetMaps(), frames)) {
179            DFXLOGE("%{public}s : Failed to parse unwind stack.", __func__);
180            return;
181        }
182        thread_->SetFrames(frames);
183        tip = StringPrintf(
184            " Failed to unwind stack, try to get unreliable call stack from #%02zu by reparsing thread stack.",
185            initSize);
186    }
187}
188}
189}