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