• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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