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