1 /*
2 * Copyright (c) 2021 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 #ifdef __clang__
17 #pragma clang diagnostic push
18 #pragma clang diagnostic ignored "-Wextern-c-compat"
19 #endif
20
21 #include "dfx_dump_catcher_local_dumper.h"
22
23 #include <cerrno>
24 #include <cinttypes>
25 #include <csignal>
26 #include <cstdlib>
27 #include <cstdio>
28 #include <cstring>
29 #include <ctime>
30 #include <fcntl.h>
31 #include <pthread.h>
32 #include <sched.h>
33 #include <unistd.h>
34
35 #include <sys/capability.h>
36 #include <sys/prctl.h>
37 #include <sys/syscall.h>
38 #include <sys/types.h>
39 #include <sys/uio.h>
40 #include <sys/wait.h>
41 #include <securec.h>
42
43 #include <libunwind.h>
44
45 #ifdef LOG_DOMAIN
46 #undef LOG_DOMAIN
47 #define LOG_DOMAIN 0x2D11
48 #endif
49
50 #ifdef LOG_TAG
51 #undef LOG_TAG
52 #define LOG_TAG "DfxDumpCatcherLocalDumper"
53 #endif
54
55 #ifndef NSIG
56 #define NSIG 64
57 #endif
58
59 #ifndef LOCAL_DUMPER_DEBUG
60 #define LOCAL_DUMPER_DEBUG
61 #endif
62 #undef LOCAL_DUMPER_DEBUG
63
64 namespace OHOS {
65 namespace HiviewDFX {
66 static constexpr int SYMBOL_BUF_SIZE = 1024;
67 static constexpr int SECONDS_TO_MILLSECONDS = 1000000;
68 static constexpr int NANOSECONDS_TO_MILLSECONDS = 1000;
69 static constexpr int NUMBER_SIXTYFOUR = 64;
70 static constexpr int INHERITABLE_OFFSET = 32;
71 constexpr int SIGLOCAL_DUMP = 36;
72 constexpr int MAX_FRAME_SIZE = 64;
73
74 static struct LocalDumperRequest g_localDumpRequest;
75 static pthread_mutex_t g_localDumperMutex = PTHREAD_MUTEX_INITIALIZER;
76 static struct sigaction g_localOldSigaction = {};
77
78 uint32_t DfxDumpCatcherLocalDumper::g_curIndex = 0;
79 bool DfxDumpCatcherLocalDumper::g_isLocalDumperInited = false;
80 std::condition_variable DfxDumpCatcherLocalDumper::g_localDumperCV;
81 std::shared_ptr<DfxElfMaps> DfxDumpCatcherLocalDumper::g_localDumperMaps = nullptr;
82 std::vector<DfxDumpCatcherFrame> DfxDumpCatcherLocalDumper::g_FrameV;
83 std::mutex DfxDumpCatcherLocalDumper::g_localDumperMutx;
84
InitLocalDumper()85 bool DfxDumpCatcherLocalDumper::InitLocalDumper()
86 {
87 DfxDumpCatcherLocalDumper::g_localDumperMaps = DfxElfMaps::Create(getpid());
88 DfxDumpCatcherLocalDumper::g_FrameV = std::vector<DfxDumpCatcherFrame>(MAX_FRAME_SIZE);
89 DfxDumpCatcherLocalDumper::DFX_InstallLocalDumper(SIGLOCAL_DUMP);
90 DfxDumpCatcherLocalDumper::g_isLocalDumperInited = true;
91 return true;
92 }
93
DestroyLocalDumper()94 void DfxDumpCatcherLocalDumper::DestroyLocalDumper()
95 {
96 DfxDumpCatcherLocalDumper::g_localDumperMaps = nullptr;
97 DfxDumpCatcherLocalDumper::g_FrameV.clear();
98 DfxDumpCatcherLocalDumper::DFX_UninstallLocalDumper(SIGLOCAL_DUMP);
99 DfxDumpCatcherLocalDumper::g_isLocalDumperInited = false;
100 }
101
SendLocalDumpRequest(int32_t tid)102 bool DfxDumpCatcherLocalDumper::SendLocalDumpRequest(int32_t tid)
103 {
104 return syscall(SYS_tkill, tid, SIGLOCAL_DUMP) == 0;
105 }
106
DfxDumpCatcherLocalDumper()107 DfxDumpCatcherLocalDumper::DfxDumpCatcherLocalDumper()
108 {
109 #ifdef LOCAL_DUMPER_DEBUG
110 HILOG_BASE_DEBUG(LOG_CORE, "%{public}s :: construct.", LOG_TAG);
111 #endif
112 }
113
~DfxDumpCatcherLocalDumper()114 DfxDumpCatcherLocalDumper::~DfxDumpCatcherLocalDumper()
115 {
116 #ifdef LOCAL_DUMPER_DEBUG
117 HILOG_BASE_DEBUG(LOG_CORE, "%{public}s :: destructor.", LOG_TAG);
118 #endif
119 }
120
CollectUnwindResult()121 std::string DfxDumpCatcherLocalDumper::CollectUnwindResult()
122 {
123 std::ostringstream result;
124 result << "Tid:" << g_localDumpRequest.tid << std::endl;
125 if (g_curIndex == 0) {
126 result << "Failed to get stacktrace." << std::endl;
127 }
128
129 for (uint32_t i = 0; i < g_curIndex; ++i) {
130 ResolveFrameInfo(g_FrameV[i]);
131 WriteFrameInfo(result, i, g_FrameV[i]);
132 }
133
134 result << std::endl;
135 return result.str();
136 }
137
CollectUnwindFrames(std::vector<std::shared_ptr<DfxDumpCatcherFrame>> & frames)138 void DfxDumpCatcherLocalDumper::CollectUnwindFrames(std::vector<std::shared_ptr<DfxDumpCatcherFrame>>& frames)
139 {
140 if (g_curIndex == 0) {
141 return;
142 }
143
144 for (uint32_t i = 0; i < g_curIndex; ++i) {
145 ResolveFrameInfo(g_FrameV[i]);
146 frames.push_back(std::make_shared<DfxDumpCatcherFrame>(g_FrameV[i]));
147 }
148 }
149
ResolveFrameInfo(DfxDumpCatcherFrame & frame)150 void DfxDumpCatcherLocalDumper::ResolveFrameInfo(DfxDumpCatcherFrame& frame)
151 {
152 if (g_localDumperMaps->FindMapByAddr(frame.GetFramePc(), frame.map_)) {
153 frame.SetFrameRelativePc(frame.GetRelativePc(g_localDumperMaps));
154 }
155 }
156
WriteFrameInfo(std::ostringstream & ss,size_t index,DfxDumpCatcherFrame & frame)157 void DfxDumpCatcherLocalDumper::WriteFrameInfo(std::ostringstream& ss, size_t index, DfxDumpCatcherFrame& frame)
158 {
159 char buf[SYMBOL_BUF_SIZE] = { 0 };
160 (void)sprintf_s(buf, sizeof(buf), "#%02zu pc %016" PRIx64 " ", index, frame.relativePc_);
161 if (strlen(buf) > 100) { // 100 : expected result length
162 ss << " Illegal frame" << std::endl;
163 return;
164 }
165
166 ss << std::string(buf, strlen(buf)) << " ";
167 if (frame.GetFrameMap() == nullptr) {
168 ss << "Unknown" << std::endl;
169 return;
170 }
171
172 ss << frame.GetFrameMap()->GetMapPath() << "(";
173 ss << std::string(frame.funcName_);
174 ss << "+" << frame.funcOffset_ << ")" << std::endl;
175 }
176
ExecLocalDump(int pid,int tid,size_t skipFramNum)177 bool DfxDumpCatcherLocalDumper::ExecLocalDump(int pid, int tid, size_t skipFramNum)
178 {
179 #ifdef LOCAL_DUMPER_DEBUG
180 HILOG_BASE_DEBUG(LOG_CORE, "%{public}s :: %{public}s : pid(%{public}d), tid(%{public}d), skpFram(%{public}d).", \
181 LOG_TAG, __func__, pid, tid, skipFramNum);
182 #endif
183
184 unw_context_t context;
185 unw_getcontext(&context);
186
187 unw_cursor_t cursor;
188 unw_init_local(&cursor, &context);
189
190 size_t index = 0;
191 DfxDumpCatcherLocalDumper::g_curIndex = 0;
192 while ((unw_step(&cursor) > 0) && (index < BACK_STACK_MAX_STEPS)) {
193 // skip 0 stack, as this is dump catcher. Caller don't need it.
194 if (index < skipFramNum) {
195 index++;
196 continue;
197 }
198
199 unw_word_t pc;
200 if (unw_get_reg(&cursor, UNW_REG_IP, (unw_word_t*)(&(pc)))) {
201 break;
202 }
203 g_FrameV[index - skipFramNum].SetFramePc((uint64_t)pc);
204
205 unw_word_t sp;
206 if (unw_get_reg(&cursor, UNW_REG_SP, (unw_word_t*)(&(sp)))) {
207 break;
208 }
209
210 g_FrameV[index - skipFramNum].SetFrameSp((uint64_t)sp);
211 (void)unw_get_proc_name(&cursor, g_FrameV[index - skipFramNum].funcName_,
212 SYMBOL_BUF_SIZE, (unw_word_t*)(&g_FrameV[index - skipFramNum].funcOffset_));
213 DfxDumpCatcherLocalDumper::g_curIndex = static_cast<uint32_t>(index - skipFramNum);
214 index++;
215 }
216
217 #ifdef LOCAL_DUMPER_DEBUG
218 HILOG_BASE_DEBUG(LOG_CORE, "%{public}s :: ExecLocalDump :: return true.", LOG_TAG);
219 #endif
220 return true;
221 }
222
DFX_LocalDumperUnwindLocal(int sig,siginfo_t * si,void * context)223 void DfxDumpCatcherLocalDumper::DFX_LocalDumperUnwindLocal(int sig, siginfo_t *si, void *context)
224 {
225 HILOG_BASE_DEBUG(LOG_CORE, "%{public}s :: DFX_LocalDumperUnwindLocal.", LOG_TAG);
226 DfxLogToSocket("DFX_LocalDumperUnwindLocal -S-");
227 #ifdef LOCAL_DUMPER_DEBUG
228 HILOG_BASE_DEBUG(LOG_CORE, "%{public}s :: sig(%{public}d), callerPid(%{public}d), callerTid(%{public}d).",
229 __func__, sig, si->si_pid, si->si_uid);
230 HILOG_BASE_DEBUG(LOG_CORE, "DFX_LocalDumperUnwindLocal :: sig(%{public}d), pid(%{public}d), tid(%{public}d).",
231 sig, g_localDumpRequest.pid, g_localDumpRequest.tid);
232 #endif
233 ExecLocalDump(g_localDumpRequest.pid, g_localDumpRequest.tid, DUMP_CATCHER_NUMBER_ONE);
234 g_localDumperCV.notify_one();
235 DfxLogToSocket("DFX_LocalDumperUnwindLocal -E-");
236 }
237
DFX_LocalDumper(int sig,siginfo_t * si,void * context)238 void DfxDumpCatcherLocalDumper::DFX_LocalDumper(int sig, siginfo_t *si, void *context)
239 {
240 pthread_mutex_lock(&g_localDumperMutex);
241 (void)memset_s(&g_localDumpRequest, sizeof(g_localDumpRequest), 0, sizeof(g_localDumpRequest));
242 g_localDumpRequest.type = sig;
243 g_localDumpRequest.tid = gettid();
244 g_localDumpRequest.pid = getpid();
245 g_localDumpRequest.timeStamp = (uint64_t)time(NULL);
246 DFX_LocalDumperUnwindLocal(sig, si, context);
247 pthread_mutex_unlock(&g_localDumperMutex);
248 }
249
DFX_InstallLocalDumper(int sig)250 void DfxDumpCatcherLocalDumper::DFX_InstallLocalDumper(int sig)
251 {
252 struct sigaction action;
253 memset_s(&action, sizeof(action), 0, sizeof(action));
254 memset_s(&g_localOldSigaction, sizeof(g_localOldSigaction), \
255 0, sizeof(g_localOldSigaction));
256 sigfillset(&action.sa_mask);
257 action.sa_sigaction = DfxDumpCatcherLocalDumper::DFX_LocalDumper;
258 action.sa_flags = SA_RESTART | SA_SIGINFO;
259
260 if (sigaction(sig, &action, &g_localOldSigaction) != EOK) {
261 DfxLogToSocket("DFX_InstallLocalDumper :: Failed to register signal.");
262 }
263 }
264
DFX_UninstallLocalDumper(int sig)265 void DfxDumpCatcherLocalDumper::DFX_UninstallLocalDumper(int sig)
266 {
267 if (g_localOldSigaction.sa_sigaction == nullptr) {
268 signal(sig, SIG_DFL);
269 return;
270 }
271
272 if (sigaction(sig, &g_localOldSigaction, NULL) != EOK) {
273 DfxLogToSocket("DFX_UninstallLocalDumper :: Failed to reset signal.");
274 signal(sig, SIG_DFL);
275 }
276 }
277 }
278 }
279