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 
34 namespace OHOS {
35 namespace HiviewDFX {
36 namespace {
37 #undef LOG_DOMAIN
38 #undef LOG_TAG
39 #define LOG_TAG "DfxBacktrace"
40 #define LOG_DOMAIN 0xD002D11
41 
GetThreadHead(int32_t tid)42 std::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 
GetBacktraceFramesByTid(std::vector<DfxFrame>& frames, int32_t tid, size_t skipFrameNum, bool fast, size_t maxFrameNums)54 bool 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 
GetBacktraceStringByTid(std::string& out, int32_t tid, size_t skipFrameNum, bool fast, size_t maxFrameNums, bool enableKernelStack)72 bool 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 
PrintBacktrace(int32_t fd, bool fast, size_t maxFrameNums)94 bool 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 
GetBacktrace(std::string& out, bool fast, size_t maxFrameNums)114 bool 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 
GetBacktrace(std::string& out, size_t skipFrameNum, bool fast, size_t maxFrameNums)123 bool 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 
PrintTrace(int32_t fd, size_t maxFrameNums)131 bool PrintTrace(int32_t fd, size_t maxFrameNums)
132 {
133     return PrintBacktrace(fd, false, maxFrameNums);
134 }
135 
GetTrace(size_t skipFrameNum, size_t maxFrameNums)136 const 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 
GetProcessStacktrace(size_t maxFrameNums, bool enableKernelStack)146 std::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