• 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 #include "log_catcher_utils.h"
16 
17 #include <cstdio>
18 #include <fcntl.h>
19 #include <map>
20 #include <memory>
21 #include <sstream>
22 #include <sys/wait.h>
23 
24 #include "common_utils.h"
25 #include "dfx_dump_catcher.h"
26 #include "dfx_json_formatter.h"
27 #include "file_util.h"
28 #include "hiview_logger.h"
29 #include "iservice_registry.h"
30 #include "string_util.h"
31 #include "time_util.h"
32 
33 namespace OHOS {
34 namespace HiviewDFX {
35 namespace LogCatcherUtils {
36 static std::map<int, std::shared_ptr<std::pair<bool, std::string>>> dumpMap;
37 static std::mutex dumpMutex;
38 static std::condition_variable getSync;
39 static constexpr int DUMP_KERNEL_STACK_SUCCESS = 1;
40 static constexpr int DUMP_STACK_FAILED = -1;
41 static constexpr int MAX_RETRY_COUNT = 20;
42 static constexpr int WAIT_CHILD_PROCESS_INTERVAL = 5 * 1000;
43 static constexpr mode_t DEFAULT_LOG_FILE_MODE = 0644;
44 
GetDump(int pid,std::string & msg)45 bool GetDump(int pid, std::string& msg)
46 {
47     std::unique_lock lock(dumpMutex);
48     auto it = dumpMap.find(pid);
49     if (it == dumpMap.end()) {
50         dumpMap[pid] = std::make_shared<std::pair<bool, std::string>>(
51             std::pair<bool, std::string>(false, ""));
52         return false;
53     }
54     std::shared_ptr<std::pair<bool, std::string>> tmp = it->second;
55     if (!tmp->first) {
56         getSync.wait_for(lock, std::chrono::seconds(10), // 10: dump stack timeout
57                          [pid]() -> bool {
58                                 return (dumpMap.find(pid) == dumpMap.end());
59                             });
60         if (!tmp->first) {
61             return false;
62         }
63     }
64     msg = tmp->second;
65     return true;
66 }
67 
FinshDump(int pid,const std::string & msg)68 void FinshDump(int pid, const std::string& msg)
69 {
70     std::lock_guard lock(dumpMutex);
71     auto it = dumpMap.find(pid);
72     if (it == dumpMap.end()) {
73         return;
74     }
75     std::shared_ptr<std::pair<bool, std::string>> tmp = it->second;
76     tmp->first = true;
77     tmp->second = msg;
78     dumpMap.erase(pid);
79     getSync.notify_all();
80 }
81 
WriteKernelStackToFd(int originFd,const std::string & msg,int pid)82 int WriteKernelStackToFd(int originFd, const std::string& msg, int pid)
83 {
84     std::string logPath = "/data/log/eventlog/";
85     std::vector<std::string> files;
86     FileUtil::GetDirFiles(logPath, files, false);
87     std::string filterName = "-KernelStack-" + std::to_string(originFd) + ".log";
88     std::string targetPath = "";
89     for (auto& fileName : files) {
90         if (fileName.find(filterName) != std::string::npos) {
91             targetPath = fileName;
92             break;
93         }
94     }
95     FILE* fp = nullptr;
96     std::string realPath = "";
97     if (FileUtil::PathToRealPath(targetPath, realPath)) {
98         fp = fopen(realPath.c_str(), "a");
99     } else {
100         std::string procName = CommonUtils::GetProcFullNameByPid(pid);
101         if (procName.empty()) {
102             return -1;
103         }
104         StringUtil::FormatProcessName(procName);
105         auto logTime = TimeUtil::GetMilliseconds() / TimeUtil::SEC_TO_MILLISEC;
106         std::string formatTime = TimeUtil::TimestampFormatToDate(logTime, "%Y%m%d%H%M%S");
107         std::string logName = procName + "-" + formatTime + filterName;
108         realPath = logPath + logName;
109         fp = fopen(realPath.c_str(), "w+");
110         chmod(realPath.c_str(), DEFAULT_LOG_FILE_MODE);
111     }
112     if (fp != nullptr) {
113         FileUtil::SaveStringToFile(realPath, msg);
114         (void)fclose(fp);
115         fp = nullptr;
116         return 0;
117     }
118     return -1;
119 }
120 
DumpStacktrace(int fd,int pid,std::string & terminalBinderStack,int terminalBinderPid,int terminalBinderTid)121 int DumpStacktrace(int fd, int pid, std::string& terminalBinderStack, int terminalBinderPid, int terminalBinderTid)
122 {
123     if (fd < 0) {
124         return -1;
125     }
126     std::string msg = "";
127     if (!GetDump(pid, msg)) {
128         DfxDumpCatcher dumplog;
129         std::string ret;
130         auto dumpResult = dumplog.DumpCatchProcess(pid, ret);
131         if (dumpResult == DUMP_STACK_FAILED) {
132             msg = "Failed to dump stacktrace for " + std::to_string(pid) + "\n" + ret;
133         } else if (dumpResult == DUMP_KERNEL_STACK_SUCCESS) {
134             if (!DfxJsonFormatter::FormatKernelStack(ret, msg, false)) {
135                 msg = "Failed to format kernel stack for " + std::to_string(pid) + "\n";
136             }
137             WriteKernelStackToFd(fd, ret, pid);
138         } else {
139             msg = ret;
140         }
141         FinshDump(pid, "\n-repeat-\n" + msg);
142     }
143 
144     if (msg == "") {
145         msg = "dumpCatch return empty stack!!!!";
146     }
147     if (terminalBinderPid > 0 && pid == terminalBinderPid) {
148         terminalBinderTid  = (terminalBinderTid > 0) ? terminalBinderTid : terminalBinderPid;
149         GetThreadStack(msg, terminalBinderStack, terminalBinderTid);
150     }
151 
152     FileUtil::SaveStringToFd(fd, msg);
153     return 0;
154 }
155 
GetThreadStack(const std::string & processStack,std::string & stack,int tid)156 void GetThreadStack(const std::string& processStack, std::string& stack, int tid)
157 {
158     if (tid <= 0) {
159         return;
160     }
161 
162     std::istringstream issStack(processStack);
163     std::string regTidString = "^Tid:" + std::to_string(tid) + ", Name:(.{0,32})$";
164     std::regex regTid(regTidString);
165     std::regex regStack(R"(^#\d{2,3} (pc|at) .{0,1024}$)");
166     std::string line;
167     while (std::getline(issStack, line)) {
168         if (!issStack.good()) {
169             break;
170         }
171 
172         if (!std::regex_match(line, regTid)) {
173             continue;
174         }
175 
176         while (std::getline(issStack, line) && std::regex_match(line, regStack)) {
177             stack.append(line + "\n");
178             if (!issStack.good()) {
179                 break;
180             }
181         };
182         break;
183     }
184 }
185 
DumpStackFfrt(int fd,const std::string & pid)186 int DumpStackFfrt(int fd, const std::string& pid)
187 {
188     std::list<SystemProcessInfo> systemProcessInfos;
189     sptr<ISystemAbilityManager> sam = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
190     sam->GetRunningSystemProcess(systemProcessInfos);
191     std::string serviceName = std::any_of(systemProcessInfos.begin(), systemProcessInfos.end(),
192         [pid](auto& systemProcessInfo) { return pid == std::to_string(systemProcessInfo.pid); }) ?
193         "SystemAbilityManager" : "ApplicationManagerService";
194     int count = WAIT_CHILD_PROCESS_COUNT;
195 
196     ReadShellToFile(fd, serviceName, "--ffrt " + pid, count);
197     return 0;
198 }
199 
ReadShellToFile(int fd,const std::string & serviceName,const std::string & cmd,int & count)200 void ReadShellToFile(int fd, const std::string& serviceName, const std::string& cmd, int& count)
201 {
202     int childPid = fork();
203     if (childPid < 0) {
204         return;
205     }
206     if (childPid == 0) {
207         if (fd < 0 || dup2(fd, STDOUT_FILENO) == -1 || dup2(fd, STDIN_FILENO) == -1 || dup2(fd, STDERR_FILENO) == -1) {
208             _exit(-1);
209         }
210         int ret = execl("/system/bin/hidumper", "hidumper", "-s", serviceName.c_str(), "-a", cmd.c_str(), nullptr);
211         if (ret < 0) {
212             _exit(-1);
213         }
214     } else {
215         int ret = waitpid(childPid, nullptr, WNOHANG);
216         while (count > 0 && (ret == 0)) {
217             usleep(WAIT_CHILD_PROCESS_INTERVAL);
218             count--;
219             ret = waitpid(childPid, nullptr, WNOHANG);
220         }
221 
222         if (ret == childPid || ret < 0) {
223             return;
224         }
225 
226         kill(childPid, SIGKILL);
227         int retryCount = MAX_RETRY_COUNT;
228         while (retryCount > 0 && waitpid(childPid, nullptr, WNOHANG) == 0) {
229             usleep(WAIT_CHILD_PROCESS_INTERVAL);
230             retryCount--;
231         }
232     }
233 }
234 }
235 } // namespace HiviewDFX
236 } // namespace OHOS
237