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