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