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