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