• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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