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