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
16#include "backtrace_local.h"
17
18#include <cstring>
19#include <dirent.h>
20#include <mutex>
21#include <unistd.h>
22#include <vector>
23
24#include "backtrace_local_thread.h"
25#include "elapsed_time.h"
26#include "dfx_frame_formatter.h"
27#include "dfx_kernel_stack.h"
28#include "dfx_log.h"
29#include "dfx_util.h"
30#include "directory_ex.h"
31#include "procinfo.h"
32#include "unwinder.h"
33
34namespace OHOS {
35namespace HiviewDFX {
36namespace {
37#undef LOG_DOMAIN
38#undef LOG_TAG
39#define LOG_TAG "DfxBacktrace"
40#define LOG_DOMAIN 0xD002D11
41
42std::string GetThreadHead(int32_t tid)
43{
44    std::string threadName;
45    if (tid == BACKTRACE_CURRENT_THREAD) {
46        tid = gettid();
47    }
48    ReadThreadName(tid, threadName);
49    std::string threadHead = "Tid:" + std::to_string(tid) + ", Name:" + threadName + "\n";
50    return threadHead;
51}
52}
53
54bool GetBacktraceFramesByTid(std::vector<DfxFrame>& frames, int32_t tid, size_t skipFrameNum, bool fast,
55                             size_t maxFrameNums)
56{
57    std::shared_ptr<Unwinder> unwinder = nullptr;
58#ifdef __aarch64__
59    if (fast || (tid != BACKTRACE_CURRENT_THREAD)) {
60        unwinder = std::make_shared<Unwinder>(false);
61    }
62#endif
63    if (unwinder == nullptr) {
64        unwinder = std::make_shared<Unwinder>();
65    }
66    BacktraceLocalThread thread(tid, unwinder);
67    bool ret = thread.Unwind(fast, maxFrameNums, skipFrameNum + 1);
68    frames = thread.GetFrames();
69    return ret;
70}
71
72bool GetBacktraceStringByTid(std::string& out, int32_t tid, size_t skipFrameNum, bool fast,
73                             size_t maxFrameNums, bool enableKernelStack)
74{
75    std::vector<DfxFrame> frames;
76    bool ret = GetBacktraceFramesByTid(frames, tid, skipFrameNum + 1, fast, maxFrameNums);
77    if (!ret && enableKernelStack) {
78        std::string msg = "";
79        DfxThreadStack threadStack;
80        if (DfxGetKernelStack(tid, msg) == 0 && FormatThreadKernelStack(msg, threadStack)) {
81            frames = threadStack.frames;
82            ret = true;
83            DFXLOGI("Failed to get tid(%{public}d) user stack, try kernel", tid);
84        }
85    }
86    if (ret) {
87        out.clear();
88        std::string threadHead = GetThreadHead(tid);
89        out = threadHead + Unwinder::GetFramesStr(frames);
90    }
91    return ret;
92}
93
94bool PrintBacktrace(int32_t fd, bool fast, size_t maxFrameNums)
95{
96    DFXLOGI("Receive PrintBacktrace request.");
97    std::vector<DfxFrame> frames;
98    bool ret = GetBacktraceFramesByTid(frames,
99        BACKTRACE_CURRENT_THREAD, 1, fast, maxFrameNums); // 1: skip current frame
100    if (!ret) {
101        return false;
102    }
103
104    for (auto const& frame : frames) {
105        auto line = DfxFrameFormatter::GetFrameStr(frame);
106        if (fd >= 0) {
107            dprintf(fd, "    %s", line.c_str());
108        }
109        DFXLOGI(" %{public}s", line.c_str());
110    }
111    return ret;
112}
113
114bool GetBacktrace(std::string& out, bool fast, size_t maxFrameNums)
115{
116    ElapsedTime et;
117    bool ret = GetBacktraceStringByTid(out, BACKTRACE_CURRENT_THREAD, 1,
118                                       fast, maxFrameNums, false); // 1: skip current frame
119    DFXLOGI("GetBacktrace elapsed time: %{public}" PRId64 " ms", et.Elapsed<std::chrono::milliseconds>());
120    return ret;
121}
122
123bool GetBacktrace(std::string& out, size_t skipFrameNum, bool fast, size_t maxFrameNums)
124{
125    ElapsedTime et;
126    bool ret = GetBacktraceStringByTid(out, BACKTRACE_CURRENT_THREAD, skipFrameNum + 1, fast, maxFrameNums, false);
127    DFXLOGI("GetBacktrace with skip, elapsed time: %{public}" PRId64 " ms", et.Elapsed<std::chrono::milliseconds>());
128    return ret;
129}
130
131bool PrintTrace(int32_t fd, size_t maxFrameNums)
132{
133    return PrintBacktrace(fd, false, maxFrameNums);
134}
135
136const char* GetTrace(size_t skipFrameNum, size_t maxFrameNums)
137{
138    static std::string trace;
139    trace.clear();
140    if (!GetBacktrace(trace, skipFrameNum, false, maxFrameNums)) {
141        DFXLOGE("Failed to get trace string");
142    }
143    return trace.c_str();
144}
145
146std::string GetProcessStacktrace(size_t maxFrameNums, bool enableKernelStack)
147{
148    auto unwinder = std::make_shared<Unwinder>();
149    std::string ss = "\n" + GetStacktraceHeader();
150    std::function<bool(int)> func = [&](int tid) {
151        if (tid <= 0 || tid == gettid()) {
152            return false;
153        }
154        BacktraceLocalThread thread(tid, unwinder);
155        if (thread.Unwind(false, maxFrameNums, 0)) {
156            ss += thread.GetFormattedStr(true) + "\n";
157        } else if (enableKernelStack) {
158            std::string msg = "";
159            DfxThreadStack threadStack;
160            if (DfxGetKernelStack(tid, msg) == 0 && FormatThreadKernelStack(msg, threadStack)) {
161                thread.SetFrames(threadStack.frames);
162                ss += thread.GetFormattedStr(true) + "\n";
163                DFXLOGI("Failed to get tid(%{public}d) user stack, try kernel", tid);
164            }
165        }
166        return true;
167    };
168
169    std::vector<int> tids;
170    GetTidsByPidWithFunc(getpid(), tids, func);
171
172    return ss;
173}
174} // namespace HiviewDFX
175} // namespace OHOS
176