1 /*
2  * Copyright (c) Huawei Technologies Co., Ltd. 2021. All rights reserved.
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 #include <cinttypes>
16 #include <gtest/gtest.h>
17 #include <dlfcn.h>
18 
19 #include "process_data_plugin.h"
20 #include "plugin_module_api.h"
21 
22 using namespace testing::ext;
23 
24 namespace {
25 const std::string DEFAULT_TEST_PATH = "/data/local/tmp/resources";
26 const std::string SO_PATH = "libprocessplugin.z.so";
27 constexpr uint32_t BUF_SIZE = 4 * 1024 * 1024;
28 const int PERCENT = 100;
29 constexpr int PROCESS_NUM = 2;
30 constexpr int THREAD_NUM = 3;
31 std::string g_path = "";
32 std::string g_testPath = "";
33 std::shared_ptr<ProcessDataPlugin> processPlugin = nullptr;
34 
35 struct PssData {
36     int32_t pssInfo;
37 };
38 
39 struct DiskioData {
40     int64_t rchar;
41     int64_t wchar;
42     int64_t syscr;
43     int64_t syscw;
44     int64_t rbytes;
45     int64_t wbytes;
46     int64_t cancelledWbytes;
47 };
48 
49 struct CpuData {
50     double cpuUsage;
51     int32_t threadSum;
52     int64_t cpuTimeMs;
53 };
54 
55 struct ProcessStatus {
56     int32_t pid;
57     std::string name;
58     int32_t ppid;
59     int32_t uid;
60 };
61 
62 struct ProcessCpuData {
63     int64_t utime;
64     int64_t stime;
65     int64_t cutime;
66     int64_t cstime;
67 };
68 
69 struct BootData {
70     int64_t user;
71     int64_t nice;
72     int64_t system;
73     int64_t idle;
74     int64_t iowait;
75     int64_t irq;
76     int64_t softirq;
77     int64_t steal;
78 };
79 
80 PssData g_pssData[] = { {1499}, {230} };
81 DiskioData g_diskioData[] = { {479, 577, 1973, 8092, 2574, 50, 91}, {7201784, 3168, 54150, 35, 22499328, 0, 0} };
82 ProcessStatus g_processStatus[] = { {11, "test", 2, 0}, {1872, "ibus-x11", 0, 1} };
83 ProcessCpuData g_processCpuData[] = { {60, 10, 20, 30}, {70, 10, 50, 30} };
84 BootData g_bootData = {24875428, 3952448, 11859815, 1193297105, 8980661, 0, 2607250, 0};
85 
86 class ProcessDataPluginTest : public ::testing::Test {
87 public:
SetUpTestCase()88     static void SetUpTestCase() {}
89 
TearDownTestCase()90     static void TearDownTestCase()
91     {
92         if (access(g_testPath.c_str(), F_OK) == 0) {
93             std::string str = "rm -rf " + g_testPath;
94             system(str.c_str());
95         }
96     }
97 };
98 
Getexepath()99 string Getexepath()
100 {
101     char buf[PATH_MAX] = "";
102     std::string path = "/proc/self/exe";
103     size_t rslt = readlink(path.c_str(), buf, sizeof(buf));
104     if (rslt < 0 || (rslt >= sizeof(buf))) {
105         return "";
106     }
107     buf[rslt] = '\0';
108     for (int i = rslt; i >= 0; i--) {
109         if (buf[i] == '/') {
110             buf[i + 1] = '\0';
111             break;
112         }
113     }
114     return buf;
115 }
116 
GetFullPath(std::string path)117 std::string GetFullPath(std::string path)
118 {
119     if (path.size() > 0 && path[0] != '/') {
120         return Getexepath() + path;
121     }
122     return path;
123 }
124 
PluginCpuinfoStub(ProcessData& processData, ProcessConfig& protoConfig, bool unusualBuff)125 bool PluginCpuinfoStub(ProcessData& processData, ProcessConfig& protoConfig, bool unusualBuff)
126 {
127     CHECK_NOTNULL(processPlugin, false, "PluginCpuinfoStub fail");
128     // serialize
129     std::vector<uint8_t> configData(protoConfig.ByteSizeLong());
130     int ret = protoConfig.SerializeToArray(configData.data(), configData.size());
131 
132     // start
133     ret = processPlugin->Start(configData.data(), configData.size());
134     if (ret < 0) {
135         return false;
136     }
137 
138     // report
139     std::vector<uint8_t> bufferData(BUF_SIZE);
140     if (unusualBuff) { // buffer异常,调整缓冲区长度为1,测试异常情况
141         bufferData.resize(1, 0);
142     }
143 
144     ret = processPlugin->Report(bufferData.data(), bufferData.size());
145     if (ret > 0) {
146         processData.ParseFromArray(bufferData.data(), ret);
147         return true;
148     }
149 
150     return false;
151 }
152 
GetCpuData(std::vector<CpuData>& cpuDataVec, int64_t Hz)153 void GetCpuData(std::vector<CpuData>& cpuDataVec, int64_t Hz)
154 {
155     int64_t bootTime = (g_bootData.user + g_bootData.nice + g_bootData.system + g_bootData.idle + g_bootData.iowait
156                         + g_bootData.irq + g_bootData.softirq + g_bootData.steal) * Hz;
157     for (int i = 0; i < PROCESS_NUM; i++) {
158         int cpuTimeMs = (g_processCpuData[i].utime + g_processCpuData[i].stime + g_processCpuData[i].cutime
159                             + g_processCpuData[i].cstime) * Hz;
160         double cpuUsage = static_cast<double>(cpuTimeMs) / bootTime * PERCENT;
161         cpuDataVec.push_back({cpuUsage, 1, cpuTimeMs});
162     }
163     cpuDataVec[1].threadSum = THREAD_NUM;
164 }
165 
166 /**
167  * @tc.name: process plugin
168  * @tc.desc: Test whether the path exists.
169  * @tc.type: FUNC
170  */
HWTEST_F(ProcessDataPluginTest, TestPath, TestSize.Level1)171 HWTEST_F(ProcessDataPluginTest, TestPath, TestSize.Level1)
172 {
173     g_path = GetFullPath(DEFAULT_TEST_PATH);
174     g_testPath = g_path;
175     EXPECT_NE("", g_path);
176 }
177 
178 /**
179  * @tc.name: process plugin
180  * @tc.desc: process plugin test for report process tree.
181  * @tc.type: FUNC
182  */
HWTEST_F(ProcessDataPluginTest, TestPluginReportProcessTree, TestSize.Level1)183 HWTEST_F(ProcessDataPluginTest, TestPluginReportProcessTree, TestSize.Level1)
184 {
185     g_path = g_testPath + "/proc/";
186     processPlugin = std::make_shared<ProcessDataPlugin>();
187     processPlugin->SetPath(g_path);
188 
189     ProcessData processData;
190     ProcessConfig processConfig;
191     processConfig.set_report_process_tree(true);
192     EXPECT_TRUE(PluginCpuinfoStub(processData, processConfig, false));
193 
194     for (int i = 0; i < PROCESS_NUM && i < processData.processesinfo().size(); i++) {
195         ProcessInfo processesinfo = processData.processesinfo()[i];
196         EXPECT_EQ(processesinfo.pid(), g_processStatus[i].pid);
197         EXPECT_STREQ(processesinfo.name().c_str(), g_processStatus[i].name.c_str());
198         EXPECT_EQ(processesinfo.ppid(), g_processStatus[i].ppid);
199         EXPECT_EQ(processesinfo.uid(), g_processStatus[i].uid);
200     }
201 
202     EXPECT_EQ(processPlugin->Stop(), 0);
203 }
204 
205 /**
206  * @tc.name: process plugin
207  * @tc.desc: process plugin test for report cpu.
208  * @tc.type: FUNC
209  */
HWTEST_F(ProcessDataPluginTest, TestPluginReportCpu, TestSize.Level1)210 HWTEST_F(ProcessDataPluginTest, TestPluginReportCpu, TestSize.Level1)
211 {
212     g_path = g_testPath + "/proc/";
213     processPlugin = std::make_shared<ProcessDataPlugin>();
214     processPlugin->SetPath(g_path);
215 
216     ProcessData processData;
217     ProcessConfig processConfig;
218     processConfig.set_report_process_tree(true);
219     processConfig.set_report_cpu(true);
220     EXPECT_TRUE(PluginCpuinfoStub(processData, processConfig, false));
221 
222     std::vector<CpuData> cpuDataVec;
223     int64_t Hz = processPlugin->GetUserHz();
224     GetCpuData(cpuDataVec, Hz);
225     for (int i = 0; i < PROCESS_NUM && i < processData.processesinfo().size(); i++) {
226         CpuInfo cpuInfo = processData.processesinfo()[i].cpuinfo();
227         EXPECT_FLOAT_EQ(cpuInfo.cpu_usage(), cpuDataVec[i].cpuUsage);
228         EXPECT_EQ(cpuInfo.thread_sum(), cpuDataVec[i].threadSum);
229         EXPECT_EQ(cpuInfo.cpu_time_ms(), cpuDataVec[i].cpuTimeMs);
230     }
231 
232     EXPECT_EQ(processPlugin->Stop(), 0);
233 }
234 
235 /**
236  * @tc.name: process plugin
237  * @tc.desc: process plugin test for report diskio.
238  * @tc.type: FUNC
239  */
HWTEST_F(ProcessDataPluginTest, TestPluginReportDiskio, TestSize.Level1)240 HWTEST_F(ProcessDataPluginTest, TestPluginReportDiskio, TestSize.Level1)
241 {
242     g_path = g_testPath + "/proc/";
243     processPlugin = std::make_shared<ProcessDataPlugin>();
244     processPlugin->SetPath(g_path);
245 
246     ProcessData processData;
247     ProcessConfig processConfig;
248     processConfig.set_report_process_tree(true);
249     processConfig.set_report_diskio(true);
250     EXPECT_TRUE(PluginCpuinfoStub(processData, processConfig, false));
251 
252     for (int i = 0; i < PROCESS_NUM && i < processData.processesinfo().size(); i++) {
253         DiskioInfo diskinfo = processData.processesinfo()[i].diskinfo();
254         EXPECT_EQ(diskinfo.rchar(), g_diskioData[i].rchar);
255         EXPECT_EQ(diskinfo.wchar(), g_diskioData[i].wchar);
256         EXPECT_EQ(diskinfo.syscr(), g_diskioData[i].syscr);
257         EXPECT_EQ(diskinfo.syscw(), g_diskioData[i].syscw);
258         EXPECT_EQ(diskinfo.rbytes(), g_diskioData[i].rbytes);
259         EXPECT_EQ(diskinfo.wbytes(), g_diskioData[i].wbytes);
260         EXPECT_EQ(diskinfo.cancelled_wbytes(), g_diskioData[i].cancelledWbytes);
261     }
262 
263     EXPECT_EQ(processPlugin->Stop(), 0);
264 }
265 
266 /**
267  * @tc.name: process plugin
268  * @tc.desc: process plugin test for report pss.
269  * @tc.type: FUNC
270  */
HWTEST_F(ProcessDataPluginTest, TestPluginReportPss, TestSize.Level1)271 HWTEST_F(ProcessDataPluginTest, TestPluginReportPss, TestSize.Level1)
272 {
273     g_path = g_testPath + "/proc/";
274     processPlugin = std::make_shared<ProcessDataPlugin>();
275     processPlugin->SetPath(g_path);
276 
277     ProcessData processData;
278     ProcessConfig processConfig;
279     processConfig.set_report_process_tree(true);
280     processConfig.set_report_pss(true);
281     EXPECT_TRUE(PluginCpuinfoStub(processData, processConfig, false));
282 
283     for (int i = 0; i < PROCESS_NUM && i < processData.processesinfo().size(); i++) {
284         PssInfo pssinfo = processData.processesinfo()[i].pssinfo();
285         EXPECT_EQ(pssinfo.pss_info(), g_pssData[i].pssInfo);
286     }
287 
288     EXPECT_EQ(processPlugin->Stop(), 0);
289 }
290 
291 /**
292  * @tc.name: process plugin
293  * @tc.desc: process plugin test.
294  * @tc.type: FUNC
295  */
HWTEST_F(ProcessDataPluginTest, TestPluginReportAll, TestSize.Level1)296 HWTEST_F(ProcessDataPluginTest, TestPluginReportAll, TestSize.Level1)
297 {
298     g_path = g_testPath + "/proc/";
299     processPlugin = std::make_shared<ProcessDataPlugin>();
300     processPlugin->SetPath(g_path);
301 
302     ProcessData processData;
303     ProcessConfig processConfig;
304     processConfig.set_report_process_tree(true);
305     processConfig.set_report_cpu(true);
306     processConfig.set_report_pss(true);
307     processConfig.set_report_diskio(true);
308     EXPECT_TRUE(PluginCpuinfoStub(processData, processConfig, false));
309 
310     std::vector<CpuData> cpuDataVec;
311     int64_t Hz = processPlugin->GetUserHz();
312     GetCpuData(cpuDataVec, Hz);
313     for (int i = 0; i < PROCESS_NUM && i < processData.processesinfo().size(); i++) {
314         ProcessInfo processesinfo = processData.processesinfo()[i];
315         CpuInfo cpuInfo = processData.processesinfo()[i].cpuinfo();
316         DiskioInfo diskinfo = processData.processesinfo()[i].diskinfo();
317         PssInfo pssinfo = processData.processesinfo()[i].pssinfo();
318         EXPECT_EQ(processesinfo.pid(), g_processStatus[i].pid);
319         EXPECT_STREQ(processesinfo.name().c_str(), g_processStatus[i].name.c_str());
320         EXPECT_EQ(processesinfo.ppid(), g_processStatus[i].ppid);
321         EXPECT_EQ(processesinfo.uid(), g_processStatus[i].uid);
322         EXPECT_FLOAT_EQ(cpuInfo.cpu_usage(), cpuDataVec[i].cpuUsage);
323         EXPECT_EQ(cpuInfo.thread_sum(), cpuDataVec[i].threadSum);
324         EXPECT_EQ(cpuInfo.cpu_time_ms(), cpuDataVec[i].cpuTimeMs);
325         EXPECT_EQ(diskinfo.rchar(), g_diskioData[i].rchar);
326         EXPECT_EQ(diskinfo.wchar(), g_diskioData[i].wchar);
327         EXPECT_EQ(diskinfo.syscr(), g_diskioData[i].syscr);
328         EXPECT_EQ(diskinfo.syscw(), g_diskioData[i].syscw);
329         EXPECT_EQ(diskinfo.rbytes(), g_diskioData[i].rbytes);
330         EXPECT_EQ(diskinfo.wbytes(), g_diskioData[i].wbytes);
331         EXPECT_EQ(diskinfo.cancelled_wbytes(), g_diskioData[i].cancelledWbytes);
332         EXPECT_EQ(pssinfo.pss_info(), g_pssData[i].pssInfo);
333     }
334 
335     EXPECT_EQ(processPlugin->Stop(), 0);
336 }
337 
338 /**
339  * @tc.name: process plugin
340  * @tc.desc: process plugin test for unusual path.
341  * @tc.type: FUNC
342  */
HWTEST_F(ProcessDataPluginTest, TestPluginUnusualPath, TestSize.Level1)343 HWTEST_F(ProcessDataPluginTest, TestPluginUnusualPath, TestSize.Level1)
344 {
345     processPlugin = std::make_shared<ProcessDataPlugin>();
346     processPlugin->SetPath("123");
347 
348     ProcessData processData;
349     ProcessConfig processConfig;
350     processConfig.set_report_process_tree(true);
351     EXPECT_FALSE(PluginCpuinfoStub(processData, processConfig, false));
352 }
353 
354 
355 /**
356  * @tc.name: process plugin
357  * @tc.desc: process plugin test for buffer exception.
358  * @tc.type: FUNC
359  */
HWTEST_F(ProcessDataPluginTest, TestPluginBufferException, TestSize.Level1)360 HWTEST_F(ProcessDataPluginTest, TestPluginBufferException, TestSize.Level1)
361 {
362     g_path = g_testPath + "/proc/";
363     processPlugin = std::make_shared<ProcessDataPlugin>();
364     processPlugin->SetPath(g_path);
365 
366     // 缓冲区异常
367     ProcessData processData;
368     ProcessConfig processConfig;
369     processConfig.set_report_process_tree(true);
370     EXPECT_FALSE(PluginCpuinfoStub(processData, processConfig, true));
371 }
372 
373 /**
374  * @tc.name: process plugin
375  * @tc.desc: process plugin registration test.
376  * @tc.type: FUNC
377  */
HWTEST_F(ProcessDataPluginTest, TestPluginRegister, TestSize.Level1)378 HWTEST_F(ProcessDataPluginTest, TestPluginRegister, TestSize.Level1)
379 {
380     void* handle = dlopen(SO_PATH.c_str(), RTLD_LAZY);
381     ASSERT_NE(handle, nullptr);
382     PluginModuleStruct* processPlugin = (PluginModuleStruct*)dlsym(handle, "g_pluginModule");
383     ASSERT_NE(processPlugin, nullptr);
384     EXPECT_STREQ(processPlugin->name, "process-plugin");
385     EXPECT_EQ(processPlugin->resultBufferSizeHint, BUF_SIZE);
386 
387     // Serialize config
388     ProcessConfig processConfig;
389     processConfig.set_report_process_tree(true);
390     int configLength = processConfig.ByteSizeLong();
391     ASSERT_GT(configLength, 0);
392     std::vector<uint8_t> configBuffer(configLength);
393     EXPECT_TRUE(processConfig.SerializeToArray(configBuffer.data(), configLength));
394 
395     // run plugin
396     std::vector<uint8_t> dataBuffer(processPlugin->resultBufferSizeHint);
397     EXPECT_EQ(processPlugin->callbacks->onPluginSessionStart(configBuffer.data(), configLength), RET_SUCC);
398     int len = processPlugin->callbacks->onPluginReportResult(dataBuffer.data(), processPlugin->resultBufferSizeHint);
399     ASSERT_GT(len, 0);
400     EXPECT_EQ(processPlugin->callbacks->onPluginSessionStop(), RET_SUCC);
401 
402     // 反序列化失败导致的start失败
403     EXPECT_EQ(processPlugin->callbacks->onPluginSessionStart(configBuffer.data(), configLength+1), RET_FAIL);
404 }
405 
406 /**
407  * @tc.name: process plugin
408  * @tc.desc: process plugin anomaly branch test.
409  * @tc.type: FUNC
410  */
HWTEST_F(ProcessDataPluginTest, TestPluginReportAnomaly, TestSize.Level1)411 HWTEST_F(ProcessDataPluginTest, TestPluginReportAnomaly, TestSize.Level1)
412 {
413     g_path = g_testPath + "/other/";
414     processPlugin = std::make_shared<ProcessDataPlugin>();
415     processPlugin->SetPath(g_path);
416 
417     ProcessData processData;
418     ProcessConfig processConfig;
419     processConfig.set_report_process_tree(true);
420     EXPECT_TRUE(PluginCpuinfoStub(processData, processConfig, false));
421 
422     EXPECT_EQ(processPlugin->Stop(), 0);
423 }
424 } // namespace
425