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