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