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