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