• 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 <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