1 /*
2 * Copyright (c) 2021 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 #include "process_data_plugin.h"
16
17 #include <sstream>
18 #include <fstream>
19 #include <iostream>
20
21 #include "buffer_splitter.h"
22 #include "securec.h"
23
24 namespace {
25 constexpr size_t READ_BUFFER_SIZE = 1024 * 16;
26 constexpr int DEC_BASE = 10;
27 constexpr int STAT_COUNT = 13;
28 constexpr int CPU_USER_HZ_L = 100;
29 constexpr int CPU_USER_HZ_H = 1000;
30 constexpr int CPU_HZ_H = 10;
31 const int PERCENT = 100;
32 } // namespace
33
ProcessDataPlugin()34 ProcessDataPlugin::ProcessDataPlugin()
35 : buffer_(new (std::nothrow) uint8_t[READ_BUFFER_SIZE]), err_(-1)
36 {
37 SetPath("/proc/");
38 }
39
~ProcessDataPlugin()40 ProcessDataPlugin::~ProcessDataPlugin()
41 {
42 HILOG_INFO(LOG_CORE, "%s:~ProcessDataPlugin!", __func__);
43
44 buffer_ = nullptr;
45
46 return;
47 }
48
Start(const uint8_t * configData,uint32_t configSize)49 int ProcessDataPlugin::Start(const uint8_t* configData, uint32_t configSize)
50 {
51 if (buffer_ == nullptr) {
52 HILOG_ERROR(LOG_CORE, "%s:buffer_ == null", __func__);
53 return RET_FAIL;
54 }
55
56 if (protoConfig_.ParseFromArray(configData, configSize) <= 0) {
57 HILOG_ERROR(LOG_CORE, "%s:parseFromArray failed!", __func__);
58 return RET_FAIL;
59 }
60
61 HILOG_INFO(LOG_CORE, "%s:start success!", __func__);
62 return RET_SUCC;
63 }
64
Report(uint8_t * data,uint32_t dataSize)65 int ProcessDataPlugin::Report(uint8_t* data, uint32_t dataSize)
66 {
67 ProcessData dataProto;
68 uint32_t length;
69
70 if (protoConfig_.report_process_tree()) {
71 WriteProcesseList(dataProto);
72 }
73
74 length = dataProto.ByteSizeLong();
75 if (length > dataSize) {
76 return -length;
77 }
78 if (dataProto.SerializeToArray(data, length) > 0) {
79 return length;
80 }
81 return 0;
82 }
83
Stop()84 int ProcessDataPlugin::Stop()
85 {
86 pids_.clear();
87 cpuTime_.clear();
88 bootTime_.clear();
89
90 HILOG_INFO(LOG_CORE, "%s:stop success!", __func__);
91 return 0;
92 }
93
OpenDestDir(const char * dirPath)94 DIR* ProcessDataPlugin::OpenDestDir(const char* dirPath)
95 {
96 DIR* destDir = nullptr;
97
98 destDir = opendir(dirPath);
99 if (destDir == nullptr) {
100 HILOG_ERROR(LOG_CORE, "%s:failed to opendir(%s), errno=%d", __func__, dirPath, errno);
101 }
102
103 return destDir;
104 }
105
GetValidPid(DIR * dirp)106 int32_t ProcessDataPlugin::GetValidPid(DIR* dirp)
107 {
108 if (!dirp) return 0;
109 while (struct dirent* dirEnt = readdir(dirp)) {
110 if (dirEnt->d_type != DT_DIR) {
111 continue;
112 }
113
114 int32_t pid = atoi(dirEnt->d_name);
115 if (pid) {
116 return pid;
117 }
118 }
119 return 0;
120 }
121
ReadProcPidFile(int32_t pid,const char * pFileName)122 int32_t ProcessDataPlugin::ReadProcPidFile(int32_t pid, const char* pFileName)
123 {
124 char fileName[PATH_MAX + 1] = {0};
125 char realPath[PATH_MAX + 1] = {0};
126 int fd = -1;
127 ssize_t bytesRead = 0;
128
129 if (snprintf_s(fileName, sizeof(fileName), sizeof(fileName) - 1, "%s%d/%s", path_.c_str(), pid, pFileName) < 0) {
130 HILOG_ERROR(LOG_CORE, "%s:snprintf_s error", __func__);
131 }
132 if (realpath(fileName, realPath) == nullptr) {
133 HILOG_ERROR(LOG_CORE, "%s:realpath failed, errno=%d", __func__, errno);
134 return RET_FAIL;
135 }
136 fd = open(realPath, O_RDONLY | O_CLOEXEC);
137 if (fd == -1) {
138 HILOG_INFO(LOG_CORE, "%s:failed to open(%s), errno=%d", __func__, fileName, errno);
139 err_ = errno;
140 return RET_FAIL;
141 }
142 if (buffer_.get() == nullptr) {
143 HILOG_INFO(LOG_CORE, "%s:empty address, buffer_ is NULL", __func__);
144 err_ = RET_NULL_ADDR;
145 close(fd);
146 return RET_FAIL;
147 }
148 bytesRead = read(fd, buffer_.get(), READ_BUFFER_SIZE - 1);
149 if (bytesRead < 0) {
150 close(fd);
151 HILOG_INFO(LOG_CORE, "%s:failed to read(%s), errno=%d", __func__, fileName, errno);
152 err_ = errno;
153 return RET_FAIL;
154 }
155 buffer_.get()[bytesRead] = '\0';
156 close(fd);
157
158 return bytesRead;
159 }
160
BufnCmp(const char * src,int srcLen,const char * key,int keyLen)161 bool ProcessDataPlugin::BufnCmp(const char* src, int srcLen, const char* key, int keyLen)
162 {
163 if (!src || !key || (srcLen < keyLen)) {
164 return false;
165 }
166 for (int i = 0; i < keyLen; i++) {
167 if (*src++ != *key++) {
168 return false;
169 }
170 }
171 return true;
172 }
173
addPidBySort(int32_t pid)174 bool ProcessDataPlugin::addPidBySort(int32_t pid)
175 {
176 auto pidsEnd = pids_.end();
177 auto it = std::lower_bound(pids_.begin(), pidsEnd, pid);
178 if (it != pidsEnd && *it == pid) {
179 return false;
180 }
181 it = pids_.insert(it, std::move(pid));
182 return true;
183 }
184
WriteProcess(ProcessInfo * processinfo,const char * pFile,uint32_t fileLen,int32_t pid)185 void ProcessDataPlugin::WriteProcess(ProcessInfo* processinfo, const char* pFile, uint32_t fileLen, int32_t pid)
186 {
187 BufferSplitter totalbuffer(const_cast<const char*>(pFile), fileLen + 1);
188
189 do {
190 totalbuffer.NextWord(':');
191 if (!totalbuffer.CurWord()) {
192 return;
193 }
194
195 if (BufnCmp(totalbuffer.CurWord(), totalbuffer.CurWordSize(), "Name", strlen("Name"))) {
196 totalbuffer.NextWord('\n');
197 if (!totalbuffer.CurWord()) {
198 return;
199 }
200 processinfo->set_name(totalbuffer.CurWord(), totalbuffer.CurWordSize());
201 } else if (BufnCmp(totalbuffer.CurWord(), totalbuffer.CurWordSize(), "Pid", strlen("Pid"))) {
202 totalbuffer.NextWord('\n');
203 if (!totalbuffer.CurWord()) {
204 return;
205 }
206 char* end = nullptr;
207 int32_t value = static_cast<int32_t>(strtoul(totalbuffer.CurWord(), &end, DEC_BASE));
208 if (value < 0) {
209 HILOG_ERROR(LOG_CORE, "%s:strtoull value failed", __func__);
210 }
211 processinfo->set_pid(value);
212 } else if (BufnCmp(totalbuffer.CurWord(), totalbuffer.CurWordSize(), "PPid", strlen("PPid"))) {
213 totalbuffer.NextWord('\n');
214 if (!totalbuffer.CurWord()) {
215 return;
216 }
217 char* end = nullptr;
218 int32_t value = static_cast<int32_t>(strtoul(totalbuffer.CurWord(), &end, DEC_BASE));
219 if (value < 0) {
220 HILOG_ERROR(LOG_CORE, "%s:strtoull value failed", __func__);
221 }
222 processinfo->set_ppid(value);
223 } else if (BufnCmp(totalbuffer.CurWord(), totalbuffer.CurWordSize(), "Uid", strlen("Uid"))) {
224 totalbuffer.NextWord('\n');
225 if (!totalbuffer.CurWord()) {
226 return;
227 }
228 std::string curWord = std::string(totalbuffer.CurWord(), totalbuffer.CurWordSize());
229 curWord = curWord.substr(0, curWord.find(" "));
230 char* end = nullptr;
231 int32_t value = static_cast<int32_t>(strtoul(curWord.c_str(), &end, DEC_BASE));
232 if (value < 0) {
233 HILOG_ERROR(LOG_CORE, "%s:strtoull value failed", __func__);
234 }
235 processinfo->set_uid(value);
236 break;
237 } else {
238 totalbuffer.NextWord('\n');
239 if (!totalbuffer.CurWord()) {
240 continue;
241 }
242 }
243 } while (totalbuffer.NextLine());
244 // update process name
245 int32_t ret = ReadProcPidFile(pid, "cmdline");
246 if (ret > 0) {
247 processinfo->set_name(reinterpret_cast<char*>(buffer_.get()), strlen(reinterpret_cast<char*>(buffer_.get())));
248 }
249 }
250
WriteProcessInfo(ProcessData & data,int32_t pid)251 void ProcessDataPlugin::WriteProcessInfo(ProcessData& data, int32_t pid)
252 {
253 int32_t ret = ReadProcPidFile(pid, "status");
254 if (ret == RET_FAIL) {
255 return;
256 }
257 if ((buffer_.get() == nullptr) || (ret == 0)) {
258 return;
259 }
260 auto* processinfo = data.add_processesinfo();
261 WriteProcess(processinfo, (char*)buffer_.get(), ret, pid);
262 if (protoConfig_.report_cpu()) {
263 auto cpuProto = processinfo->mutable_cpuinfo();
264 std::vector<uint64_t> cpuUsageVec;
265 std::vector<uint64_t> bootTime;
266 WriteCpuUsageData(pid, cpuProto);
267 WriteThreadData(pid, cpuProto);
268 }
269 if (protoConfig_.report_diskio()) {
270 auto diskProto = processinfo->mutable_diskinfo();
271 WriteDiskioData(pid, diskProto);
272 }
273 if (protoConfig_.report_pss()) {
274 auto pssProto = processinfo->mutable_pssinfo();
275 WritePssData(pid, pssProto);
276 }
277 }
278
WriteProcesseList(ProcessData & data)279 bool ProcessDataPlugin::WriteProcesseList(ProcessData& data)
280 {
281 DIR* procDir = nullptr;
282
283 procDir = OpenDestDir(path_.c_str());
284 if (procDir == nullptr) {
285 return false;
286 }
287
288 pids_.clear();
289 while (int32_t pid = GetValidPid(procDir)) {
290 if (pid <= 0) {
291 closedir(procDir);
292 HILOG_WARN(LOG_CORE, "%s: get pid[%d] failed", __func__, pid);
293 return false;
294 }
295 addPidBySort(pid);
296 }
297
298 for (unsigned int i = 0; i < pids_.size(); i++) {
299 WriteProcessInfo(data, pids_[i]);
300 }
301
302 closedir(procDir);
303 return true;
304 }
305
WriteThreadData(int pid,CpuInfo * protoc)306 bool ProcessDataPlugin::WriteThreadData(int pid, CpuInfo* protoc)
307 {
308 DIR* procDir = nullptr;
309 std::string path = path_ + std::to_string(pid) + "/task";
310 procDir = OpenDestDir(path.c_str());
311 if (procDir == nullptr) {
312 return false;
313 }
314
315 uint32_t i = 0;
316 while (int32_t tid = GetValidPid(procDir)) {
317 if (tid <= 0) {
318 closedir(procDir);
319 HILOG_WARN(LOG_CORE, "%s: get pid[%d] failed", __func__, tid);
320 return false;
321 }
322 i++;
323 }
324 protoc->set_thread_sum(i);
325 closedir(procDir);
326 return true;
327 }
328
GetUserHz()329 int64_t ProcessDataPlugin::GetUserHz()
330 {
331 int64_t hz = -1;
332 int64_t user_hz = sysconf(_SC_CLK_TCK);
333 switch (user_hz) {
334 case CPU_USER_HZ_L:
335 hz = CPU_HZ_H;
336 break;
337 case CPU_USER_HZ_H:
338 hz = 1;
339 break;
340 default:
341 break;
342 }
343 return hz;
344 }
345
WriteCpuUsageData(int pid,CpuInfo * protoc)346 bool ProcessDataPlugin::WriteCpuUsageData(int pid, CpuInfo* protoc)
347 {
348 uint64_t prevCpuTime = 0;
349 uint64_t cpuTime = 0;
350 uint64_t prevBootTime = 0;
351 uint64_t bootTime = 0;
352 double usage = 0.0;
353 ReadCpuUsage(pid, protoc, cpuTime);
354 ReadBootTime(pid, protoc, bootTime);
355 if (cpuTime_.find(pid) != cpuTime_.end()) {
356 prevCpuTime = cpuTime_[pid];
357 }
358 if (bootTime_.find(pid) != bootTime_.end()) {
359 prevBootTime = bootTime_[pid];
360 }
361 if (bootTime - prevBootTime == 0 || bootTime == 0) {
362 protoc->set_cpu_usage(0);
363 return false;
364 }
365 if (prevCpuTime == 0) {
366 usage = static_cast<double>(cpuTime) / (bootTime);
367 } else {
368 usage = static_cast<double>(cpuTime - prevCpuTime) / (bootTime - prevBootTime);
369 }
370
371 protoc->set_cpu_usage(usage * PERCENT);
372 protoc->set_cpu_time_ms(cpuTime);
373 cpuTime_[pid] = cpuTime;
374 bootTime_[pid] = bootTime;
375 return true;
376 }
377
ReadBootTime(int pid,CpuInfo * protoc,uint64_t & bootTime)378 bool ProcessDataPlugin::ReadBootTime(int pid, CpuInfo* protoc, uint64_t& bootTime)
379 {
380 std::string path = path_ + "stat";
381 std::ifstream input(path, std::ios::in);
382 if (input.fail()) {
383 HILOG_ERROR(LOG_CORE, "%s open %s failed, errno = %d", __func__, path.c_str(), errno);
384 return false;
385 }
386 do {
387 if (!input.good()) {
388 return false;
389 }
390 std::string line;
391 getline(input, line);
392
393 auto pos = line.find("cpu ");
394 if (pos != std::string::npos) {
395 line += '\n';
396 GetBootData(line, protoc, bootTime);
397 }
398 } while (0);
399 input.close();
400
401 return true;
402 }
403
GetBootData(const std::string & line,CpuInfo * protoc,uint64_t & bootTime)404 uint32_t ProcessDataPlugin::GetBootData(const std::string& line, CpuInfo* protoc, uint64_t& bootTime)
405 {
406 uint64_t num;
407 uint32_t count = 0;
408 char* end = nullptr;
409 char* pTmp = const_cast<char*>(line.c_str());
410 constexpr uint32_t cntVec = 8;
411
412 std::vector<uint64_t> bootTimeVec;
413 bootTime = 0;
414 while (pTmp != nullptr && *pTmp != '\n') {
415 CHECK_TRUE(FindFirstNum(&pTmp), count, "%s: FindFirstNum failed", __func__);
416 num = strtoull(pTmp, &end, DEC_BASE);
417 CHECK_TRUE(num >= 0, count, "%s:strtoull failed", __func__);
418 bootTimeVec.push_back(num);
419 bootTime += num;
420 pTmp = end;
421 if (++count >= cntVec) {
422 break;
423 }
424 }
425 bootTime = bootTime * (uint64_t)GetUserHz();
426 return count;
427 }
428
ReadCpuUsage(int pid,CpuInfo * protoc,uint64_t & cpuTime)429 bool ProcessDataPlugin::ReadCpuUsage(int pid, CpuInfo* protoc, uint64_t& cpuTime)
430 {
431 std::string path = path_ + std::to_string(pid) + "/stat";
432 std::ifstream input(path, std::ios::in);
433 if (input.fail()) {
434 HILOG_ERROR(LOG_CORE, "%s open %s failed, errno = %d", __func__, path.c_str(), errno);
435 return false;
436 }
437 do {
438 if (!input.good()) {
439 return false;
440 }
441 std::string line;
442 getline(input, line);
443 line += '\n';
444 GetCpuUsageData(line, protoc, cpuTime);
445 } while (0);
446 input.close();
447
448 return true;
449 }
450
GetCpuUsageData(const std::string & line,CpuInfo * protoc,uint64_t & cpuTime)451 uint32_t ProcessDataPlugin::GetCpuUsageData(const std::string& line, CpuInfo* protoc, uint64_t& cpuTime)
452 {
453 uint64_t num;
454 uint32_t count = 0;
455 char* end = nullptr;
456 char* pTmp = const_cast<char*>(line.c_str());
457 int i = 0;
458 constexpr uint32_t cntVec = 4;
459
460 while (FindFirstSpace(&pTmp)) {
461 pTmp++;
462 if (++i >= STAT_COUNT) {
463 break;
464 }
465 }
466 std::vector<uint64_t> cpuUsageVec;
467 cpuTime = 0;
468 while (pTmp != nullptr && *pTmp != '\n') {
469 CHECK_TRUE(FindFirstNum(&pTmp), count, "%s: FindFirstNum failed", __func__);
470 num = strtoull(pTmp, &end, DEC_BASE);
471 cpuUsageVec.push_back(num);
472 cpuTime += num;
473 pTmp = end;
474 if (++count >= cntVec) {
475 break;
476 }
477 }
478 cpuTime = cpuTime * (uint64_t)GetUserHz();
479 return count;
480 }
481
WriteDiskioData(int pid,DiskioInfo * protoc)482 bool ProcessDataPlugin::WriteDiskioData(int pid, DiskioInfo* protoc)
483 {
484 std::string path = path_ + std::to_string(pid) + "/io";
485 std::ifstream input(path, std::ios::in);
486 if (input.fail()) {
487 return false;
488 }
489 do {
490 if (!input.good()) {
491 return false;
492 }
493 std::string line;
494 getline(input, line);
495 line += '\n';
496 GetDiskioData(line, protoc);
497 } while (!input.eof());
498 input.close();
499
500 return true;
501 }
502
GetDiskioData(std::string & line,DiskioInfo * protoc)503 bool ProcessDataPlugin::GetDiskioData(std::string& line, DiskioInfo* protoc)
504 {
505 char* pTmp = const_cast<char*>(line.c_str());
506 CHECK_NOTNULL(pTmp, false, "param invalid!");
507
508 uint64_t num;
509 if (!std::strncmp(pTmp, "rchar:", strlen("rchar:"))) {
510 CHECK_TRUE(GetValidValue(pTmp, num), false, "%s: get rchar failed", __func__);
511 protoc->set_rchar(num);
512 } else if (!std::strncmp(pTmp, "wchar:", strlen("wchar:"))) {
513 CHECK_TRUE(GetValidValue(pTmp, num), false, "%s: get wchar failed", __func__);
514 protoc->set_wchar(num);
515 } else if (!std::strncmp(pTmp, "syscr:", strlen("syscr:"))) {
516 CHECK_TRUE(GetValidValue(pTmp, num), false, "%s: get syscr failed", __func__);
517 protoc->set_syscr(num);
518 } else if (!std::strncmp(pTmp, "syscw:", strlen("syscw:"))) {
519 CHECK_TRUE(GetValidValue(pTmp, num), false, "%s: get syscw failed", __func__);
520 protoc->set_syscw(num);
521 } else if (!std::strncmp(pTmp, "read_bytes:", strlen("read_bytes:"))) {
522 CHECK_TRUE(GetValidValue(pTmp, num), false, "%s: get read_bytes failed", __func__);
523 protoc->set_rbytes(num);
524 } else if (!std::strncmp(pTmp, "write_bytes:", strlen("write_bytes:"))) {
525 CHECK_TRUE(GetValidValue(pTmp, num), false, "%s: get write_bytes failed", __func__);
526 protoc->set_wbytes(num);
527 } else if (!std::strncmp(pTmp, "cancelled_write_bytes:", strlen("cancelled_write_bytes:"))) {
528 CHECK_TRUE(GetValidValue(pTmp, num), false, "%s: get cancelled_write_bytes failed", __func__);
529 protoc->set_cancelled_wbytes(num);
530 }
531
532 return true;
533 }
534
FindFirstSpace(char ** p)535 bool ProcessDataPlugin::FindFirstSpace(char** p)
536 {
537 CHECK_NOTNULL(*p, false, "ProcessDataPlugin:%s", __func__);
538 while (**p != ' ') {
539 if (**p == '\0' || **p == '\n') {
540 return false;
541 }
542 (*p)++;
543 }
544 return true;
545 }
546
FindFirstNum(char ** p)547 bool ProcessDataPlugin::FindFirstNum(char** p)
548 {
549 CHECK_NOTNULL(*p, false, "ProcessDataPlugin:%s", __func__);
550 while (**p > '9' || **p < '0') {
551 if (**p == '\0' || **p == '\n') {
552 return false;
553 }
554 (*p)++;
555 }
556 return true;
557 }
558
GetValidValue(char * p,uint64_t & num)559 bool ProcessDataPlugin::GetValidValue(char* p, uint64_t& num)
560 {
561 char* end = nullptr;
562 CHECK_TRUE(FindFirstNum(&p), false, "%s: FindFirstNum failed", __func__);
563 num = strtoull(p, &end, DEC_BASE);
564 CHECK_TRUE(num >= 0, false, "%s:strtoull failed", __func__);
565 return true;
566 }
567
568 // read /proc/pid/smaps_rollup
WritePssData(int pid,PssInfo * protoc)569 bool ProcessDataPlugin::WritePssData(int pid, PssInfo* protoc)
570 {
571 std::string path = path_ + std::to_string(pid) + "/smaps_rollup";
572 std::ifstream input(path, std::ios::in);
573 if (input.fail()) {
574 HILOG_ERROR(LOG_CORE, "%s open %s failed, errno = %d", __func__, path.c_str(), errno);
575 return false;
576 }
577 do {
578 if (!input.good()) {
579 return false;
580 }
581 std::string line;
582 getline(input, line);
583 line += '\n';
584 std::string::size_type pos = 0u;
585 if ((pos = line.find("Pss:", pos)) != std::string::npos) {
586 char* pTmp = const_cast<char*>(line.c_str());
587 uint64_t num;
588 CHECK_TRUE(GetValidValue(pTmp, num), false, "%s: FindFirstNum failed", __func__);
589 protoc->set_pss_info(num);
590 return true;
591 }
592 } while (!input.eof());
593 input.close();
594
595 return false;
596 }
597