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
WriteKernelStackToFd(int originFd,const std::string & msg,int pid)81 int WriteKernelStackToFd(int originFd, const std::string& msg, int pid)
82 {
83 std::string logPath = "/data/log/eventlog/";
84 std::vector<std::string> files;
85 FileUtil::GetDirFiles(logPath, files, false);
86 std::string filterName = "-KernelStack-" + std::to_string(originFd);
87 std::string targetPath = "";
88 for (auto& fileName : files) {
89 if (fileName.find(filterName) != std::string::npos) {
90 targetPath = fileName;
91 break;
92 }
93 }
94 int fd = -1;
95 std::string realPath = "";
96 if (FileUtil::PathToRealPath(targetPath, realPath)) {
97 fd = open(realPath.c_str(), O_WRONLY | O_APPEND);
98 } else {
99 std::string procName = CommonUtils::GetProcFullNameByPid(pid);
100 if (procName.empty()) {
101 return -1;
102 }
103 StringUtil::FormatProcessName(procName);
104 auto logTime = TimeUtil::GetMilliseconds() / TimeUtil::SEC_TO_MILLISEC;
105 std::string formatTime = TimeUtil::TimestampFormatToDate(logTime, "%Y%m%d%H%M%S");
106 std::string logName = procName + "-" + std::to_string(pid) +
107 "-" + formatTime + filterName + ".log";
108 realPath = logPath + logName;
109 fd = open(realPath.c_str(), O_CREAT | O_WRONLY | O_TRUNC, DEFAULT_LOG_FILE_MODE);
110 }
111 if (fd >= 0) {
112 FileUtil::SaveStringToFd(fd, msg);
113 close(fd);
114 return 0;
115 }
116 return -1;
117 }
118
DumpStacktrace(int fd,int pid)119 int DumpStacktrace(int fd, int pid)
120 {
121 if (fd < 0) {
122 return -1;
123 }
124 std::string msg = "";
125 if (!GetDump(pid, msg)) {
126 DfxDumpCatcher dumplog;
127 std::string ret;
128 auto dumpResult = dumplog.DumpCatchProcess(pid, ret);
129 if (dumpResult == DUMP_STACK_FAILED) {
130 msg = "Failed to dump stacktrace for " + std::to_string(pid) + "\n" + ret;
131 } else if (dumpResult == DUMP_KERNEL_STACK_SUCCESS) {
132 if (!DfxJsonFormatter::FormatKernelStack(ret, msg, false)) {
133 msg = "Failed to format kernel stack for " + std::to_string(pid) + "\n";
134 }
135 WriteKernelStackToFd(fd, ret, pid);
136 } else {
137 msg = ret;
138 }
139 FinshDump(pid, "\n-repeat-\n" + msg);
140 }
141
142 if (msg == "") {
143 msg = "dumpCatch return empty stack!!!!";
144 }
145 FileUtil::SaveStringToFd(fd, msg);
146 return 0;
147 }
148
GetFfrtDumpType(int pid)149 FFRT_TYPE GetFfrtDumpType(int pid)
150 {
151 std::list<SystemProcessInfo> systemProcessInfos;
152 sptr<ISystemAbilityManager> sam = SystemAbilityManagerClient::GetInstance().GetSystemAbilityManager();
153 sam->GetRunningSystemProcess(systemProcessInfos);
154 if (std::any_of(systemProcessInfos.begin(), systemProcessInfos.end(),
155 [pid](auto& systemProcessInfo) {
156 return pid == systemProcessInfo.pid;
157 })) {
158 return SYS;
159 }
160 return APP;
161 }
162
ReadShellToFile(int fd,const std::string & serviceName,const std::string & cmd,int & count)163 void ReadShellToFile(int fd, const std::string& serviceName, const std::string& cmd, int& count)
164 {
165 int childPid = fork();
166 if (childPid < 0) {
167 return;
168 }
169 if (childPid == 0) {
170 if (fd < 0 || dup2(fd, STDOUT_FILENO) == -1 || dup2(fd, STDIN_FILENO) == -1 || dup2(fd, STDERR_FILENO) == -1) {
171 _exit(-1);
172 }
173 execl("/system/bin/hidumper", "hidumper", "-s", serviceName.c_str(), "-a", cmd.c_str(), nullptr);
174 } else {
175 int ret = waitpid(childPid, nullptr, WNOHANG);
176 while (count > 0 && (ret == 0)) {
177 usleep(WAIT_CHILD_PROCESS_INTERVAL);
178 count--;
179 ret = waitpid(childPid, nullptr, WNOHANG);
180 }
181
182 if (ret == childPid || ret < 0) {
183 return;
184 }
185
186 kill(childPid, SIGKILL);
187 int retryCount = MAX_RETRY_COUNT;
188 while (retryCount > 0 && waitpid(childPid, nullptr, WNOHANG) == 0) {
189 usleep(WAIT_CHILD_PROCESS_INTERVAL);
190 retryCount--;
191 }
192 }
193 }
194 }
195 } // namespace HiviewDFX
196 } // namespace OHOS
197