1 /*
2 * Copyright (c) 2022 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 #include "xcollie_utils.h"
17 #include <ctime>
18 #include <cinttypes>
19 #include <algorithm>
20 #include <cstdlib>
21 #include <csignal>
22 #include <sstream>
23 #include <iostream>
24 #include <unistd.h>
25 #include <fcntl.h>
26 #include <sys/prctl.h>
27 #include <sys/stat.h>
28 #include <set>
29 #include "directory_ex.h"
30 #include "file_ex.h"
31 #include "storage_acl.h"
32 #include "parameter.h"
33 #include "parameters.h"
34 #include <dlfcn.h>
35
36 namespace OHOS {
37 namespace HiviewDFX {
GetCurrentTickMillseconds()38 uint64_t GetCurrentTickMillseconds()
39 {
40 struct timespec t;
41 t.tv_sec = 0;
42 t.tv_nsec = 0;
43 clock_gettime(CLOCK_MONOTONIC, &t);
44 return static_cast<int64_t>((t.tv_sec) * SEC_TO_MANOSEC + t.tv_nsec) / SEC_TO_MICROSEC;
45 }
46
IsFileNameFormat(char c)47 bool IsFileNameFormat(char c)
48 {
49 if (c >= '0' && c <= '9') {
50 return false;
51 }
52
53 if (c >= 'a' && c <= 'z') {
54 return false;
55 }
56
57 if (c >= 'A' && c <= 'Z') {
58 return false;
59 }
60
61 if (c == '.' || c == '-' || c == '_') {
62 return false;
63 }
64
65 return true;
66 }
67
GetSelfProcName()68 std::string GetSelfProcName()
69 {
70 constexpr uint16_t READ_SIZE = 128;
71 std::ifstream fin;
72 fin.open("/proc/self/comm", std::ifstream::in);
73 if (!fin.is_open()) {
74 XCOLLIE_LOGE("fin.is_open() false");
75 return "";
76 }
77 char readStr[READ_SIZE] = {'\0'};
78 fin.getline(readStr, READ_SIZE - 1);
79 fin.close();
80
81 std::string ret = std::string(readStr);
82 ret.erase(std::remove_if(ret.begin(), ret.end(), IsFileNameFormat), ret.end());
83 return ret;
84 }
85
GetFirstLine(const std::string & path)86 std::string GetFirstLine(const std::string& path)
87 {
88 char checkPath[PATH_MAX] = {0};
89 if (realpath(path.c_str(), checkPath) == nullptr) {
90 XCOLLIE_LOGE("canonicalize failed. path is %{public}s", path.c_str());
91 return "";
92 }
93
94 std::ifstream inFile(checkPath);
95 if (!inFile) {
96 return "";
97 }
98 std::string firstLine;
99 getline(inFile, firstLine);
100 inFile.close();
101 return firstLine;
102 }
103
GetProcessNameFromProcCmdline(int32_t pid)104 std::string GetProcessNameFromProcCmdline(int32_t pid)
105 {
106 std::string procCmdlinePath = "/proc/" + std::to_string(pid) + "/cmdline";
107 std::string procCmdlineContent = GetFirstLine(procCmdlinePath);
108 if (procCmdlineContent.empty()) {
109 return "";
110 }
111
112 size_t procNameStartPos = 0;
113 size_t procNameEndPos = procCmdlineContent.size();
114 for (size_t i = 0; i < procCmdlineContent.size(); i++) {
115 if (procCmdlineContent[i] == '/') {
116 procNameStartPos = i + 1;
117 } else if (procCmdlineContent[i] == '\0') {
118 procNameEndPos = i;
119 break;
120 }
121 }
122 size_t endPos = procNameEndPos - procNameStartPos;
123 return procCmdlineContent.substr(procNameStartPos, endPos);
124 }
125
GetLimitedSizeName(std::string name)126 std::string GetLimitedSizeName(std::string name)
127 {
128 if (name.size() > MAX_NAME_SIZE) {
129 return name.substr(0, MAX_NAME_SIZE);
130 }
131 return name;
132 }
133
IsProcessDebug(int32_t pid)134 bool IsProcessDebug(int32_t pid)
135 {
136 const int buffSize = 128;
137 char param[buffSize] = {0};
138 std::string filter = "hiviewdfx.freeze.filter." + GetProcessNameFromProcCmdline(pid);
139 GetParameter(filter.c_str(), "", param, buffSize - 1);
140 int32_t debugPid = atoi(param);
141 std::string procCmdlineContent = GetProcessNameFromProcCmdline(pid);
142 if (debugPid == pid) {
143 XCOLLIE_LOGI("appfreeze filtration %{public}s_%{public}d don't exit.",
144 procCmdlineContent.c_str(), debugPid);
145 return true;
146 }
147
148 char paramBundle[buffSize] = {0};
149 GetParameter("hiviewdfx.appfreeze.filter_bundle_name", "", paramBundle, buffSize - 1);
150 std::string debugBundle(paramBundle);
151 if (procCmdlineContent.compare(debugBundle) == 0) {
152 XCOLLIE_LOGI("appfreeze filtration %{public}s_%{public}s don't exit.",
153 debugBundle.c_str(), procCmdlineContent.c_str());
154 return true;
155 }
156 return false;
157 }
158
DelayBeforeExit(unsigned int leftTime)159 void DelayBeforeExit(unsigned int leftTime)
160 {
161 while (leftTime > 0) {
162 leftTime = sleep(leftTime);
163 }
164 }
165
TrimStr(const std::string & str,const char cTrim)166 std::string TrimStr(const std::string& str, const char cTrim)
167 {
168 std::string strTmp = str;
169 strTmp.erase(0, strTmp.find_first_not_of(cTrim));
170 strTmp.erase(strTmp.find_last_not_of(cTrim) + sizeof(char));
171 return strTmp;
172 }
173
SplitStr(const std::string & str,const std::string & sep,std::vector<std::string> & strs,bool canEmpty,bool needTrim)174 void SplitStr(const std::string& str, const std::string& sep,
175 std::vector<std::string>& strs, bool canEmpty, bool needTrim)
176 {
177 strs.clear();
178 std::string strTmp = needTrim ? TrimStr(str) : str;
179 std::string strPart;
180 while (true) {
181 std::string::size_type pos = strTmp.find(sep);
182 if (pos == std::string::npos || sep.empty()) {
183 strPart = needTrim ? TrimStr(strTmp) : strTmp;
184 if (!strPart.empty() || canEmpty) {
185 strs.push_back(strPart);
186 }
187 break;
188 } else {
189 strPart = needTrim ? TrimStr(strTmp.substr(0, pos)) : strTmp.substr(0, pos);
190 if (!strPart.empty() || canEmpty) {
191 strs.push_back(strPart);
192 }
193 strTmp = strTmp.substr(sep.size() + pos, strTmp.size() - sep.size() - pos);
194 }
195 }
196 }
197
ParsePeerBinderPid(std::ifstream & fin,int32_t pid)198 int ParsePeerBinderPid(std::ifstream& fin, int32_t pid)
199 {
200 const int decimal = 10;
201 std::string line;
202 bool isBinderMatchup = false;
203 while (getline(fin, line)) {
204 if (isBinderMatchup) {
205 break;
206 }
207
208 if (line.find("async") != std::string::npos) {
209 continue;
210 }
211
212 std::istringstream lineStream(line);
213 std::vector<std::string> strList;
214 std::string tmpstr;
215 while (lineStream >> tmpstr) {
216 strList.push_back(tmpstr);
217 }
218
219 auto splitPhase = [](const std::string& str, uint16_t index) -> std::string {
220 std::vector<std::string> strings;
221 SplitStr(str, ":", strings);
222 if (index < strings.size()) {
223 return strings[index];
224 }
225 return "";
226 };
227
228 if (strList.size() >= 7) { // 7: valid array size
229 // 2: peer id,
230 std::string server = splitPhase(strList[2], 0);
231 // 0: local id,
232 std::string client = splitPhase(strList[0], 0);
233 // 5: wait time, s
234 std::string wait = splitPhase(strList[5], 1);
235 if (server == "" || client == "" || wait == "") {
236 continue;
237 }
238 int serverNum = std::strtol(server.c_str(), nullptr, decimal);
239 int clientNum = std::strtol(client.c_str(), nullptr, decimal);
240 int waitNum = std::strtol(wait.c_str(), nullptr, decimal);
241 XCOLLIE_LOGI("server:%{public}d, client:%{public}d, wait:%{public}d",
242 serverNum, clientNum, waitNum);
243 if (clientNum != pid || waitNum < MIN_WAIT_NUM) {
244 continue;
245 }
246 return serverNum;
247 }
248 if (line.find("context") != line.npos) {
249 isBinderMatchup = true;
250 }
251 }
252 return -1;
253 }
254
KillProcessByPid(int32_t pid)255 bool KillProcessByPid(int32_t pid)
256 {
257 std::ifstream fin;
258 std::string path = LOGGER_BINDER_PROC_PATH;
259 char resolvePath[PATH_MAX] = {0};
260 if (realpath(path.c_str(), resolvePath) == nullptr) {
261 XCOLLIE_LOGI("GetBinderPeerPids realpath error");
262 return false;
263 }
264 fin.open(resolvePath);
265 if (!fin.is_open()) {
266 XCOLLIE_LOGI("open file failed, %{public}s.", resolvePath);
267 return false;
268 }
269
270 int peerBinderPid = ParsePeerBinderPid(fin, pid);
271 fin.close();
272 if (peerBinderPid <= INIT_PID || peerBinderPid == pid) {
273 XCOLLIE_LOGI("No PeerBinder process freeze occurs in the current process. "
274 "peerBinderPid=%{public}d, pid=%{public}d", peerBinderPid, pid);
275 return false;
276 }
277
278 XCOLLIE_LOGI("try to Kill PeerBinder process, name=%{public}s, pid=%{public}d",
279 GetProcessNameFromProcCmdline(peerBinderPid).c_str(), peerBinderPid);
280 int32_t ret = kill(peerBinderPid, SIGKILL);
281 if (ret == -1) {
282 XCOLLIE_LOGI("Kill PeerBinder process failed");
283 }
284 return (ret >= 0);
285 }
286
IsBetaVersion()287 bool IsBetaVersion()
288 {
289 auto versionType = OHOS::system::GetParameter(KEY_HIVIEW_USER_TYPE, "unknown");
290 return (versionType.find("beta") != std::string::npos);
291 }
292
CreateWatchdogDir()293 bool CreateWatchdogDir()
294 {
295 constexpr mode_t defaultLogDirMode = 0770;
296 if (!OHOS::FileExists(WATCHDOG_DIR)) {
297 OHOS::ForceCreateDirectory(WATCHDOG_DIR);
298 OHOS::ChangeModeDirectory(WATCHDOG_DIR, defaultLogDirMode);
299 }
300 if (OHOS::StorageDaemon::AclSetAccess(WATCHDOG_DIR, "g:1201:rwx") != 0) {
301 XCOLLIE_LOGI("Failed to AclSetAccess");
302 return false;
303 }
304 return true;
305 }
306
WriteStackToFd(int32_t pid,std::string & path,std::string & stack,const std::string & eventName)307 bool WriteStackToFd(int32_t pid, std::string& path, std::string& stack, const std::string& eventName)
308 {
309 if (!CreateWatchdogDir()) {
310 return false;
311 }
312 std::string time = GetFormatDate();
313 std::string realPath;
314 if (!OHOS::PathToRealPath(WATCHDOG_DIR, realPath)) {
315 XCOLLIE_LOGE("Path to realPath failed. errno: %{public}d", errno);
316 return false;
317 }
318 path = realPath + "/" + eventName + "_" + time.c_str() + "_" +
319 std::to_string(pid).c_str() + ".txt";
320 uint64_t stackSize = stack.size();
321 uint64_t fileSize = OHOS::GetFolderSize(realPath) + stackSize;
322 if (fileSize > MAX_FILE_SIZE) {
323 XCOLLIE_LOGI("CurrentDir already over limit. Will not write to stack file."
324 "MainThread fileSize: %{public}" PRIu64 " MAX_FILE_SIZE: %{public}" PRIu64 ".",
325 fileSize, MAX_FILE_SIZE);
326 return true;
327 }
328 constexpr mode_t defaultLogFileMode = 0644;
329 auto fd = open(path.c_str(), O_CREAT | O_WRONLY | O_TRUNC, defaultLogFileMode);
330 if (fd < 0) {
331 XCOLLIE_LOGI("Failed to create path");
332 return false;
333 } else {
334 XCOLLIE_LOGI("path=%{public}s", path.c_str());
335 }
336 OHOS::SaveStringToFd(fd, stack);
337 close(fd);
338
339 return true;
340 }
341
GetFormatDate()342 std::string GetFormatDate()
343 {
344 time_t t = time(nullptr);
345 char tmp[TIME_INDEX_MAX] = {0};
346 strftime(tmp, sizeof(tmp), "%Y%m%d%H%M%S", localtime(&t));
347 std::string date(tmp);
348 return date;
349 }
350
GetTimeStamp()351 int64_t GetTimeStamp()
352 {
353 std::chrono::nanoseconds ms = std::chrono::duration_cast< std::chrono::nanoseconds >(
354 std::chrono::system_clock::now().time_since_epoch());
355 return ms.count();
356 }
357
IsEnableVersion(const std::string & key,const std::string & type)358 bool IsEnableVersion(const std::string& key, const std::string& type)
359 {
360 auto enableType = system::GetParameter(key, "");
361 return (enableType.find(type) != std::string::npos);
362 }
363
FunctionOpen(void * funcHandler,const char * funcName)364 void* FunctionOpen(void* funcHandler, const char* funcName)
365 {
366 if (funcHandler == nullptr) {
367 XCOLLIE_LOGE("funcHandler is nullptr.\n");
368 return nullptr;
369 }
370 dlerror();
371 char* err = nullptr;
372 void* func = dlsym(funcHandler, funcName);
373 if ((err = dlerror()) != nullptr) {
374 XCOLLIE_LOGE("dlopen %{public}s failed. %{public}s\n", funcName, err);
375 dlclose(funcHandler);
376 return nullptr;
377 }
378 return func;
379 }
380 } // end of HiviewDFX
381 } // end of OHOS