1 /*
2  * Copyright (c) 2024 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 "test_util.h"
17 
18 #include <iostream>
19 #include <fstream>
20 #include <regex>
21 #include <sys/stat.h>
22 #include <sys/prctl.h>
23 #include <sys/wait.h>
24 #include <unistd.h>
25 #include <csignal>
26 
27 #include "ffrt.h"
28 #include "fault_detector_util.h"
29 #include "hiview_logger.h"
30 
31 namespace OHOS {
32 namespace HiviewDFX {
33 DEFINE_LOG_TAG("TestUtil");
34 using namespace std;
35 
36 constexpr int OVERTIME_TO_RESTART_PROCESS = 10; // 10s
37 constexpr int BUFFER_LENGTH = 128;
38 constexpr int MS_PER_SECOND = 1000;
39 
40 pid_t TestUtil::leakProcessPid_ = -1;
41 
FileExists(const string & path)42 bool TestUtil::FileExists(const string &path)
43 {
44     struct stat buffer;
45     return (stat(path.c_str(), &buffer) == 0);
46 }
47 
GetSmapsPath(const string & processName)48 string TestUtil::GetSmapsPath(const string &processName)
49 {
50     HIVIEW_LOGI("GetSmapsPath start");
51     string pid = GetPidByProcess(processName);
52     if (pid.empty()) {
53         HIVIEW_LOGW("GetSmapsPath failed because pid is empty");
54         return "";
55     }
56 
57     string path = MEMORY_LEAK_PATH + "/memleak-native-" + processName + "-" + pid + "-smaps.txt";
58     return path;
59 }
60 
GetSampleFile(const string & processName)61 string TestUtil::GetSampleFile(const string &processName)
62 {
63     HIVIEW_LOGI("GetSampleFile start");
64     string pid = GetPidByProcess(processName);
65     if (pid.empty()) {
66         HIVIEW_LOGW("GetSampleFile failed because pid is empty");
67         return "";
68     }
69 
70     string path = MEMORY_LEAK_PATH + "/memleak-native-" + processName + "-" + pid + "-sample.txt";
71     HIVIEW_LOGI("GetSampleFile success, path:%{public}s", path.c_str());
72     return path;
73 }
74 
RestartProcess(const string & name)75 bool TestUtil::RestartProcess(const string &name)
76 {
77     string pidBefore = GetPidByProcess(name);
78     if (pidBefore.empty()) {
79         return false;
80     }
81     KillProcess(name);
82     size_t waitTime = 0;
83     string pidAfter;
84 
85     while (waitTime++ <= OVERTIME_TO_RESTART_PROCESS) {
86         pidAfter = GetPidByProcess(name);
87         if (!pidAfter.empty()) {
88             break;
89         }
90         ffrt::this_task::sleep_for(chrono::milliseconds(MS_PER_SECOND));
91     }
92     return pidBefore != pidAfter;
93 }
94 
KillProcess(const string & name)95 void TestUtil::KillProcess(const string &name)
96 {
97     ExecCmd(CmdType::KILL_ALL, name);
98 }
99 
GetPidByProcess(const string & name)100 string TestUtil::GetPidByProcess(const string &name)
101 {
102     string pid = ExecCmd(CmdType::PID_OF, name);
103     while (!pid.empty() && pid.back() == '\n') {
104         pid.pop_back();
105     }
106     HIVIEW_LOGI("pid: %{public}s", pid.c_str());
107     return pid;
108 }
109 
ClearDir(string name)110 void TestUtil::ClearDir(string name)
111 {
112     HIVIEW_LOGI("ClearDir:%{public}s", name.c_str());
113     if (name.empty()) {
114         return;
115     }
116     while (!name.empty() && name.back() == '/') {
117         name.pop_back();
118     }
119     ExecCmd(CmdType::CLEAR_DIR, name);
120 }
121 
IsValidTarget(const string & target)122 bool TestUtil::IsValidTarget(const string &target)
123 {
124     return regex_match(target, regex("^[a-zA-Z0-9_\\/]+$"));
125 }
126 
BuildCommand(CmdType type,const string & target)127 string TestUtil::BuildCommand(CmdType type, const string &target)
128 {
129     if (!IsValidTarget(target)) {
130         HIVIEW_LOGW("BuildCommand failed, by target invalid");
131         return "";
132     }
133 
134     string cmd;
135     switch (type) {
136         case CmdType::KILL_ALL:
137             cmd = "killall " + target;
138             break;
139         case CmdType::CLEAR_DIR:
140             cmd = "rm " + target + "/*";
141             break;
142         case CmdType::PID_OF:
143             cmd = "pidof " + target;
144             break;
145         default:
146             return "";
147     }
148     HIVIEW_LOGI("BuildCommand success, cmd:%{public}s", cmd.c_str());
149     return cmd;
150 }
151 
ExecCmd(CmdType type,const string & target)152 string TestUtil::ExecCmd(CmdType type, const string &target)
153 {
154     string cmd = BuildCommand(type, target);
155     if (cmd.empty()) {
156         HIVIEW_LOGW("ExecCmd failed, by cmd empty");
157         return "";
158     }
159 
160     unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(), "r"), pclose);
161     if (!pipe) {
162         HIVIEW_LOGW("ExecCmd failed, by pipe popen failed");
163         return "";
164     }
165 
166     array<char, BUFFER_LENGTH> buffer;
167     string result;
168     while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
169         result += buffer.data();
170     }
171     HIVIEW_LOGW("ExecCmd success, result:%{public}s", result.c_str());
172     return result;
173 }
174 
IsLeakingProcessAlive()175 bool TestUtil::IsLeakingProcessAlive()
176 {
177     return leakProcessPid_ != -1;
178 }
179 
IsSelfOverLimit(int leakTarget)180 bool TestUtil::IsSelfOverLimit(int leakTarget)
181 {
182     uint64_t rss;
183     if (!GetSelfStatm(rss)) {
184         HIVIEW_LOGW("GetSelfStatm failed");
185         return false;
186     }
187 
188     if (rss > leakTarget) {
189         HIVIEW_LOGI("has over limit");
190         return true;
191     }
192     HIVIEW_LOGI("has not over limit");
193     return false;
194 }
195 
GetStatm(uint64_t & rss)196 bool TestUtil::GetStatm(uint64_t &rss)
197 {
198     if (!IsLeakingProcessAlive()) {
199         HIVIEW_LOGW("GetStatm failed, leak process died");
200         return false;
201     }
202     ifstream statmStream("/proc/" + to_string(leakProcessPid_) + "/statm");
203     if (!statmStream) {
204         HIVIEW_LOGW("GetStatm failed, open statm failed");
205         return false;
206     }
207     string statmLine;
208     getline(statmStream, statmLine);
209     statmStream.close();
210     list<string> numStrArr = GetDightStrArr(statmLine);
211     auto it = numStrArr.begin();
212     unsigned long long multiples = 4;
213     it++;
214     rss = multiples * stoull(*it);
215     return true;
216 }
217 
GetSelfStatm(uint64_t & rss)218 bool TestUtil::GetSelfStatm(uint64_t &rss)
219 {
220     ifstream statmStream("/proc/self/statm");
221     if (!statmStream) {
222         HIVIEW_LOGW("GetStatm failed, open statm failed");
223         return false;
224     }
225     string statmLine;
226     getline(statmStream, statmLine);
227     statmStream.close();
228     list<string> numStrArr = GetDightStrArr(statmLine);
229     auto it = numStrArr.begin();
230     unsigned long long multiples = 4;
231     it++;
232     rss = multiples * stoull(*it);
233     return true;
234 }
235 
GetDightStrArr(const string & target)236 list<string> TestUtil::GetDightStrArr(const string &target)
237 {
238     list<string> ret;
239     string temp = "";
240     for (size_t i = 0, len = target.size(); i < len; i++) {
241         if (target[i] >= '0' && target[i] <= '9') {
242             temp += target[i];
243             continue;
244         }
245         if (temp.size() != 0) {
246             ret.push_back(temp);
247             temp = "";
248         }
249     }
250     if (temp.size() != 0) {
251         ret.push_back(temp);
252     }
253     ret.push_back("0");
254     return ret;
255 }
256 
CopyFile(const string & srcFile,const string & dstFile)257 void TestUtil::CopyFile(const string &srcFile, const string &dstFile)
258 {
259     ifstream src(srcFile, std::ios::binary);
260     ofstream dst(dstFile, std::ios::binary);
261     dst << src.rdbuf();
262 }
263 
WriteFile(const string & file,const string & line)264 void TestUtil::WriteFile(const string &file, const string &line)
265 {
266     std::ofstream fs(file, std::ios::out);
267     if (fs.is_open()) {
268         fs << line << "\n";
269         fs.close();
270     }
271 }
272 
DeleteFile(const string & file)273 void TestUtil::DeleteFile(const string &file)
274 {
275     if (std::remove(file.c_str()) != 0) {
276         HIVIEW_LOGW("delete file failed");
277         return;
278     }
279     HIVIEW_LOGI("delete file successfully");
280 }
281 
282 } // namespace HiviewDFX
283 } // namespace OHOS
284 
285