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