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 
16 #include "cpu_data_plugin.h"
17 
18 #include <ctime>
19 #include <vector>
20 #include <sstream>
21 #include "cpu_plugin_result.pbencoder.h"
22 #include "buffer_splitter.h"
23 
24 namespace {
25 using namespace OHOS::Developtools::Profiler;
26 constexpr size_t READ_BUFFER_SIZE = 1024 * 16;
27 constexpr int SYSTEM_STAT_COUNT = 9;
28 constexpr int STAT_COUNT = 17;
29 constexpr int STAT_START = 13;
30 constexpr int THREAD_NAME_POS = 1;
31 constexpr int THREAD_STATE_POS = 2;
32 constexpr int CPU_USER_HZ_L = 100;
33 constexpr int CPU_USER_HZ_H = 1000;
34 constexpr int CPU_HZ_H = 10;
35 const int PERCENT = 100;
36 
37 const std::string FREQUENCY_PATH = "/sys/devices/system/cpu";
38 const std::string FREQUENCY_MIN_PATH = "/cpufreq/cpuinfo_min_freq";
39 const std::string FREQUENCY_MAX_PATH = "/cpufreq/cpuinfo_max_freq";
40 const std::string FREQUENCY_CUR_PATH = "/cpufreq/cpuinfo_cur_freq";
41 } // namespace
42 
CpuDataPlugin()43 CpuDataPlugin::CpuDataPlugin()
44 {
45     buffer_ = nullptr;
46     path_ = "/proc/";
47     err_ = -1;
48     pid_ = -1;
49     prevProcessCpuTime_ = 0;
50     prevCpuTimeData_ = {};
51     maxFreqIndex_ = -1;
52     freqPath_ = FREQUENCY_PATH;
53 }
54 
~CpuDataPlugin()55 CpuDataPlugin::~CpuDataPlugin()
56 {
57     PROFILER_LOG_INFO(LOG_CORE, "%s:~CpuDataPlugin!", __func__);
58     if (buffer_ != nullptr) {
59         free(buffer_);
60         buffer_ = nullptr;
61     }
62 
63     tidVec_.clear();
64     prevThreadCpuTimeMap_.clear();
65     prevCoreSystemCpuTimeMap_.clear();
66     prevCoreSystemBootTimeMap_.clear();
67     maxFrequencyVec_.clear();
68     minFrequencyVec_.clear();
69 }
70 
Start(const uint8_t* configData, uint32_t configSize)71 int CpuDataPlugin::Start(const uint8_t* configData, uint32_t configSize)
72 {
73     buffer_ = malloc(READ_BUFFER_SIZE);
74     CHECK_NOTNULL(buffer_, RET_FAIL, "%s:malloc buffer_ failed!", __func__);
75     if (memset_s(buffer_, READ_BUFFER_SIZE, 0, READ_BUFFER_SIZE) != EOK) {
76         PROFILER_LOG_ERROR(LOG_CORE, "%s:memset_s error!", __func__);
77     }
78     CHECK_TRUE(protoConfig_.ParseFromArray(configData, configSize) > 0, RET_FAIL,
79                "%s:parseFromArray failed!", __func__);
80     if (protoConfig_.pid() > 0) {
81         pid_ = protoConfig_.pid();
82     } else if (protoConfig_.report_process_info()) {
83         PROFILER_LOG_INFO(LOG_CORE, "%s:need report process info", __func__);
84     } else {
85         PROFILER_LOG_ERROR(LOG_CORE, "%s:invalid pid", __func__);
86         return RET_FAIL;
87     }
88     PROFILER_LOG_INFO(LOG_CORE, "%s:start success!", __func__);
89     return RET_SUCC;
90 }
91 
ReportOptimize(RandomWriteCtx* randomWrite)92 int CpuDataPlugin::ReportOptimize(RandomWriteCtx* randomWrite)
93 {
94     ProtoEncoder::CpuData dataProto(randomWrite);
95 
96     ProtoEncoder::CpuUsageInfo* cpuUsageInfo = nullptr;
97     WriteCpuUsageInfo(dataProto, cpuUsageInfo);
98 
99     if (pid_ > 0) {
100         WriteThreadInfo(dataProto);
101     }
102     if (protoConfig_.report_process_info()) {
103         WriteProcnum(dataProto);
104     }
105 
106     int msgSize = dataProto.Finish();
107     return msgSize;
108 }
109 
Report(uint8_t* data, uint32_t dataSize)110 int CpuDataPlugin::Report(uint8_t* data, uint32_t dataSize)
111 {
112     CpuData dataProto;
113     uint32_t length;
114 
115     CpuUsageInfo* cpuUsageInfo = nullptr;
116     WriteCpuUsageInfo(dataProto, cpuUsageInfo);
117 
118     if (pid_ > 0) {
119         WriteThreadInfo(dataProto);
120     }
121     if (protoConfig_.report_process_info()) {
122         WriteProcnum(dataProto);
123     }
124 
125     length = dataProto.ByteSizeLong();
126     if (length > dataSize) {
127         return -length;
128     }
129     if (dataProto.SerializeToArray(data, length) > 0) {
130         return length;
131     }
132     return 0;
133 }
134 
WriteProcnum(T& cpuData)135 template <typename T> bool CpuDataPlugin::WriteProcnum(T& cpuData)
136 {
137     DIR* procDir = nullptr;
138     procDir = OpenDestDir(path_);
139     if (procDir == nullptr) {
140         return false;
141     }
142 
143     uint32_t i = 0;
144     while (int32_t tid = GetValidTid(procDir)) {
145         if (tid <= 0) {
146             closedir(procDir);
147             PROFILER_LOG_WARN(LOG_CORE, "%s: get pid[%d] failed", __func__, tid);
148             return false;
149         }
150         i++;
151     }
152     cpuData.set_process_num(i);
153     closedir(procDir);
154 
155     return true;
156 }
157 
Stop()158 int CpuDataPlugin::Stop()
159 {
160     if (buffer_ != nullptr) {
161         free(buffer_);
162         buffer_ = nullptr;
163     }
164 
165     tidVec_.clear();
166     prevThreadCpuTimeMap_.clear();
167     prevCoreSystemCpuTimeMap_.clear();
168     prevCoreSystemBootTimeMap_.clear();
169     PROFILER_LOG_INFO(LOG_CORE, "%s:stop success!", __func__);
170     return 0;
171 }
172 
ReadFile(std::string& fileName)173 int32_t CpuDataPlugin::ReadFile(std::string& fileName)
174 {
175     int fd = -1;
176     ssize_t bytesRead = 0;
177     char filePath[PATH_MAX + 1] = {0};
178     char realPath[PATH_MAX + 1] = {0};
179 
180     if (snprintf_s(filePath, sizeof(filePath), sizeof(filePath) - 1, "%s", fileName.c_str()) < 0) {
181         const int bufSize = 256;
182         char buf[bufSize] = { 0 };
183         strerror_r(errno, buf, bufSize);
184         PROFILER_LOG_ERROR(LOG_CORE, "snprintf_s(%s) error, errno(%d:%s)", fileName.c_str(), errno, buf);
185         return RET_FAIL;
186     }
187     if (realpath(filePath, realPath) == nullptr) {
188         const int bufSize = 256;
189         char buf[bufSize] = { 0 };
190         strerror_r(errno, buf, bufSize);
191         PROFILER_LOG_ERROR(LOG_CORE, "realpath(%s) failed, errno(%d:%s)", fileName.c_str(), errno, buf);
192         return RET_FAIL;
193     }
194 
195     fd = open(realPath, O_RDONLY | O_CLOEXEC);
196     if (fd == -1) {
197         const int bufSize = 256;
198         char buf[bufSize] = { 0 };
199         strerror_r(errno, buf, bufSize);
200         PROFILER_LOG_ERROR(LOG_CORE, "%s:failed to open(%s), errno(%d:%s)", __func__, realPath, errno, buf);
201         err_ = errno;
202         return RET_FAIL;
203     }
204     if (buffer_ == nullptr) {
205         PROFILER_LOG_ERROR(LOG_CORE, "%s:empty address, buffer_ is NULL", __func__);
206         err_ = RET_NULL_ADDR;
207         close(fd);
208         return RET_FAIL;
209     }
210     if (memset_s(buffer_, READ_BUFFER_SIZE, 0, READ_BUFFER_SIZE) != EOK) {
211         PROFILER_LOG_ERROR(LOG_CORE, "%s:memset_s error!", __func__);
212     }
213     bytesRead = read(fd, buffer_, READ_BUFFER_SIZE - 1);
214     if (bytesRead <= 0) {
215         close(fd);
216         PROFILER_LOG_ERROR(LOG_CORE, "%s:failed to read(%s), errno=%d", __func__, realPath, errno);
217         err_ = errno;
218         return RET_FAIL;
219     }
220     close(fd);
221 
222     return bytesRead;
223 }
224 
SetTimestamp(T& sampleTimeStamp)225 template <typename T> void CpuDataPlugin::SetTimestamp(T& sampleTimeStamp)
226 {
227     timespec time;
228     clock_gettime(CLOCK_MONOTONIC, &time);
229     sampleTimeStamp.set_tv_sec(time.tv_sec);
230     sampleTimeStamp.set_tv_nsec(time.tv_nsec);
231 }
232 
GetUserHz()233 int64_t CpuDataPlugin::GetUserHz()
234 {
235     int64_t hz = -1;
236     int64_t user_hz = sysconf(_SC_CLK_TCK);
237     switch (user_hz) {
238         case CPU_USER_HZ_L:
239             hz = CPU_HZ_H;
240             break;
241         case CPU_USER_HZ_H:
242             hz = 1;
243             break;
244         default:
245             break;
246     }
247     return hz;
248 }
249 
GetCpuUsageTime(std::vector<std::string>& cpuUsageVec)250 int64_t CpuDataPlugin::GetCpuUsageTime(std::vector<std::string>& cpuUsageVec)
251 {
252     int64_t utime = 0;
253     int64_t stime = 0;
254     int64_t usageTime = 0;
255     utime = atoi(cpuUsageVec[PROCESS_UTIME].c_str());
256     stime = atoi(cpuUsageVec[PROCESS_STIME].c_str());
257     // 进程,线程CPU占用率只计算utime(用户态时间),stime(核心态时间)
258     usageTime = (utime + stime) * GetUserHz();
259 
260     return usageTime;
261 }
262 
WriteProcessCpuUsage(T& cpuUsageInfo, const char* pFile, uint32_t fileLen)263 template <typename T> void CpuDataPlugin::WriteProcessCpuUsage(T& cpuUsageInfo, const char* pFile, uint32_t fileLen)
264 {
265     BufferSplitter totalbuffer(const_cast<char*>(pFile), fileLen + 1);
266     std::vector<std::string> cpuUsageVec;
267     for (int i = 0; i < STAT_COUNT; i++) {
268         totalbuffer.NextWord(' ');
269         if (!totalbuffer.CurWord()) {
270             return;
271         }
272 
273         if (i < STAT_START) {
274             continue;
275         } else {
276             std::string curWord = std::string(totalbuffer.CurWord(), totalbuffer.CurWordSize());
277             cpuUsageVec.push_back(curWord);
278         }
279     }
280 
281     // 获取到的数据不包含utime、stime、cutime、cstime四个数值时返回
282     if (cpuUsageVec.size() != PROCESS_UNSPECIFIED) {
283         PROFILER_LOG_ERROR(LOG_CORE, "%s:failed to get process cpu usage, size=%zu", __func__, cpuUsageVec.size());
284         return;
285     }
286 
287     int64_t usageTime = GetCpuUsageTime(cpuUsageVec);
288     cpuUsageInfo.set_prev_process_cpu_time_ms(prevProcessCpuTime_);
289     cpuUsageInfo.set_process_cpu_time_ms(usageTime);
290     prevProcessCpuTime_ = usageTime;
291 }
292 
GetCpuFrequency(std::string fileName)293 int32_t CpuDataPlugin::GetCpuFrequency(std::string fileName)
294 {
295     int32_t frequency = 0;
296     int32_t ret = ReadFile(fileName);
297     if (ret != RET_FAIL) {
298         std::string tempStr(static_cast<char*>(buffer_));
299         // 去掉首尾特殊字符
300         size_t start = tempStr.find_first_not_of(" \t\n\r");
301         if (start == std::string::npos) {
302             return frequency;
303         }
304         size_t end = tempStr.find_last_not_of(" \t\n\r");
305         tempStr = tempStr.substr(start, end - start + 1);
306         if (std::all_of(tempStr.begin(), tempStr.end(), ::isdigit)) {
307             frequency = atoi(static_cast<char*>(buffer_));
308         }
309     }
310     return frequency;
311 }
312 
GetCpuCoreSize()313 int CpuDataPlugin::GetCpuCoreSize()
314 {
315     int coreSize = 0;
316     DIR* procDir = nullptr;
317     procDir = OpenDestDir(freqPath_);
318     CHECK_NOTNULL(procDir, -1, "procDir is nullptr");
319 
320     while (struct dirent* dirEnt = readdir(procDir)) {
321         if (dirEnt->d_type != DT_DIR) {
322             continue;
323         }
324         if (strncmp(dirEnt->d_name, "cpu", strlen("cpu")) == 0) {
325             coreSize++;
326         }
327     }
328     closedir(procDir);
329     return coreSize;
330 }
331 
GetMaxCpuFrequencyIndex()332 int32_t CpuDataPlugin::GetMaxCpuFrequencyIndex()
333 {
334     int coreSize = GetCpuCoreSize();
335     int index = -1;
336     int32_t maxFreq = -1;
337     maxFrequencyVec_.clear();
338     minFrequencyVec_.clear();
339     for (int i = 0; i < coreSize; i++) {
340         std::string fileName = freqPath_ + "/cpu" + std::to_string(i) + FREQUENCY_MAX_PATH;
341         int32_t maxFrequency = GetCpuFrequency(fileName);
342         maxFrequencyVec_.push_back(maxFrequency);
343         fileName = freqPath_ + "/cpu" + std::to_string(i) + FREQUENCY_MIN_PATH;
344         int32_t minFrequency = GetCpuFrequency(fileName);
345         minFrequencyVec_.push_back(minFrequency);
346 
347         if (maxFreq < maxFrequency) {
348             maxFreq = maxFrequency;
349             index = i;
350         }
351     }
352 
353     // 单核或所有核最大频率相同,默认小核
354     if (coreSize == 1 || (coreSize > 1 && index == 0 && maxFreq == maxFrequencyVec_[1])) {
355         index = -1;
356     }
357 
358     return index;
359 }
360 
SetCpuFrequency(T& cpuCoreUsageInfo, int32_t coreNum)361 template <typename T> void CpuDataPlugin::SetCpuFrequency(T& cpuCoreUsageInfo, int32_t coreNum)
362 {
363     // 第一次获取最大频率核位置,并保存各核最大最小频率到vector
364     if (maxFrequencyVec_.empty() || minFrequencyVec_.empty()) {
365         maxFreqIndex_ = GetMaxCpuFrequencyIndex();
366     }
367     std::string fileName = freqPath_ + "/cpu" + std::to_string(coreNum) + FREQUENCY_CUR_PATH;
368     int32_t curFrequency = GetCpuFrequency(fileName);
369     int32_t maxFrequency = maxFrequencyVec_[coreNum];
370     int32_t minFrequency = minFrequencyVec_[coreNum];
371 
372     if (coreNum == maxFreqIndex_) {
373         cpuCoreUsageInfo.set_is_little_core(false);
374     } else {
375         cpuCoreUsageInfo.set_is_little_core(true);
376     }
377     auto* frequency = cpuCoreUsageInfo.mutable_frequency();
378     frequency->set_min_frequency_khz(minFrequency);
379     frequency->set_max_frequency_khz(maxFrequency);
380     frequency->set_cur_frequency_khz(curFrequency);
381 }
382 
GetSystemCpuTime(std::vector<std::string>& cpuUsageVec, CpuTimeData& cpuTimeData)383 bool CpuDataPlugin::GetSystemCpuTime(std::vector<std::string>& cpuUsageVec, CpuTimeData& cpuTimeData)
384 {
385     // 获取到的数据不包含user, nice, system, idle, iowait, irq, softirq, steal八个数值时返回
386     CHECK_TRUE(cpuUsageVec.size() == SYSTEM_UNSPECIFIED, false,
387                "%s:failed to get system cpu usage, size=%zu", __func__, cpuUsageVec.size());
388 
389     int64_t user = 0;
390     int64_t nice = 0;
391     int64_t system = 0;
392     int64_t idle = 0;
393     int64_t iowait = 0;
394     int64_t irq = 0;
395     int64_t softirq = 0;
396     int64_t steal = 0;
397     user = atoi(cpuUsageVec[SYSTEM_USER].c_str());
398     nice = atoi(cpuUsageVec[SYSTEM_NICE].c_str());
399     system = atoi(cpuUsageVec[SYSTEM_SYSTEM].c_str());
400     idle = atoi(cpuUsageVec[SYSTEM_IDLE].c_str());
401     iowait = atoi(cpuUsageVec[SYSTEM_IOWAIT].c_str());
402     irq = atoi(cpuUsageVec[SYSTEM_IRQ].c_str());
403     softirq = atoi(cpuUsageVec[SYSTEM_SOFTIRQ].c_str());
404     steal = atoi(cpuUsageVec[SYSTEM_STEAL].c_str());
405 
406     cpuTimeData.userModeUsageTime = user * GetUserHz();
407     cpuTimeData.systemModeUsageTime = system * GetUserHz();
408     cpuTimeData.systemUsageTime = (user + nice + system + irq + softirq + steal) * GetUserHz();
409     cpuTimeData.systemBootTime = cpuTimeData.systemUsageTime + (idle + iowait) * GetUserHz();
410     return true;
411 }
412 
413 template <typename T>
WriteSystemCpuUsage(T& cpuUsageInfo, CpuLoadData& cpuLoadData, const char* pFile, uint32_t fileLen)414 void CpuDataPlugin::WriteSystemCpuUsage(T& cpuUsageInfo, CpuLoadData& cpuLoadData, const char* pFile, uint32_t fileLen)
415 {
416     std::vector<std::string> cpuUsageVec;
417     size_t cpuLength = strlen("cpu");
418     std::stringstream ss(pFile);
419     std::string line;
420     while (std::getline(ss, line)) {
421         BufferSplitter totalbuffer(const_cast<char*>(line.c_str()), line.length());
422         totalbuffer.NextWord(' ');
423         if (!totalbuffer.CurWord() || strncmp(totalbuffer.CurWord(), "cpu", cpuLength) != 0) {
424             return;
425         }
426 
427         for (int i = 0; i < SYSTEM_STAT_COUNT; i++) {
428             if (!totalbuffer.CurWord()) {
429                 return;
430             }
431             std::string curWord = std::string(totalbuffer.CurWord(), totalbuffer.CurWordSize());
432             cpuUsageVec.push_back(curWord);
433             totalbuffer.NextWord(' ');
434         }
435 
436         // 获取数据失败返回
437         CpuTimeData cpuTimeData;
438         if (!GetSystemCpuTime(cpuUsageVec, cpuTimeData)) {
439             return;
440         }
441 
442         if (strcmp(cpuUsageVec[0].c_str(), "cpu") == 0) {
443             cpuUsageInfo.set_prev_system_cpu_time_ms(prevCpuTimeData_.systemUsageTime);
444             cpuUsageInfo.set_prev_system_boot_time_ms(prevCpuTimeData_.systemBootTime);
445             cpuUsageInfo.set_system_cpu_time_ms(cpuTimeData.systemUsageTime);
446             cpuUsageInfo.set_system_boot_time_ms(cpuTimeData.systemBootTime);
447             bool isTest = false;
448             if (strncmp(path_.c_str(), "/proc/", strlen("/proc/")) != 0) {
449                 isTest = true; // UT needs report load data for the first time
450             }
451             if ((protoConfig_.report_process_info() && prevCpuTimeData_.systemBootTime != 0) || isTest) {
452                 cpuLoadData.userLoad = static_cast<double>(cpuTimeData.userModeUsageTime -
453                     prevCpuTimeData_.userModeUsageTime) /
454                     static_cast<double>(cpuTimeData.systemBootTime -
455                     prevCpuTimeData_.systemBootTime) * PERCENT;
456                 cpuLoadData.sysLoad = static_cast<double>(cpuTimeData.systemModeUsageTime -
457                     prevCpuTimeData_.systemModeUsageTime) /
458                     static_cast<double>(cpuTimeData.systemBootTime -
459                     prevCpuTimeData_.systemBootTime) * PERCENT;
460                 cpuLoadData.totalLoad = static_cast<double>(cpuTimeData.systemUsageTime -
461                     prevCpuTimeData_.systemUsageTime) /
462                     static_cast<double>(cpuTimeData.systemBootTime -
463                     prevCpuTimeData_.systemBootTime) * PERCENT;
464             }
465             prevCpuTimeData_ = cpuTimeData;
466             if (pid_ < 0 && protoConfig_.report_process_info()) {
467                 return;
468             }
469         } else {
470             std::string core = std::string(cpuUsageVec[0].c_str() + cpuLength, cpuUsageVec[0].size() - cpuLength);
471             int32_t coreNum = atoi(core.c_str());
472             // 第一次获取数据时需要将前一个数据置为0
473             if (prevCoreSystemCpuTimeMap_.size() == static_cast<size_t>(coreNum)) {
474                 prevCoreSystemCpuTimeMap_[coreNum] = 0;
475                 prevCoreSystemBootTimeMap_[coreNum] = 0;
476             }
477             auto* cpuCore = cpuUsageInfo.add_cores();
478             cpuCore->set_cpu_core(coreNum);
479             cpuCore->set_prev_system_cpu_time_ms(prevCoreSystemCpuTimeMap_[coreNum]);
480             cpuCore->set_prev_system_boot_time_ms(prevCoreSystemBootTimeMap_[coreNum]);
481             cpuCore->set_system_cpu_time_ms(cpuTimeData.systemUsageTime);
482             cpuCore->set_system_boot_time_ms(cpuTimeData.systemBootTime);
483 
484             SetCpuFrequency(*cpuCore, coreNum);
485             prevCoreSystemCpuTimeMap_[coreNum] = cpuTimeData.systemUsageTime;
486             prevCoreSystemBootTimeMap_[coreNum] = cpuTimeData.systemBootTime;
487         }
488 
489         cpuUsageVec.clear();
490     }
491 }
492 
WriteCpuUsageInfo(T& cpuData, I cpuUsageInfo)493 template <typename T, typename I> void CpuDataPlugin::WriteCpuUsageInfo(T& cpuData, I cpuUsageInfo)
494 {
495     // write process info
496     if (pid_ > 0) {
497         std::string fileName = path_ + std::to_string(pid_) + "/stat";
498         int32_t ret = ReadFile(fileName);
499         if (ret == RET_FAIL) {
500             return;
501         }
502         if ((buffer_ == nullptr) || (ret == 0)) {
503             return;
504         }
505 
506         cpuUsageInfo = cpuData.mutable_cpu_usage_info();
507         WriteProcessCpuUsage(*cpuUsageInfo, reinterpret_cast<char*>(buffer_), ret);
508     }
509 
510     // write system info
511     std::string fileName = path_ + "stat";
512     int32_t ret = ReadFile(fileName);
513     if (ret == RET_FAIL) {
514         return;
515     }
516     if ((buffer_ == nullptr) || (ret == 0)) {
517         return;
518     }
519 
520     CpuLoadData cpuLoadData;
521     if (cpuUsageInfo == nullptr) {
522         cpuUsageInfo = cpuData.mutable_cpu_usage_info();
523     }
524     WriteSystemCpuUsage(*cpuUsageInfo, cpuLoadData, reinterpret_cast<char*>(buffer_), ret);
525 
526     auto* timestamp = cpuUsageInfo->mutable_timestamp();
527     SetTimestamp(*timestamp);
528 
529     cpuData.set_user_load(cpuLoadData.userLoad);
530     cpuData.set_sys_load(cpuLoadData.sysLoad);
531     cpuData.set_total_load(cpuLoadData.totalLoad);
532 }
533 
addTidBySort(int32_t tid)534 bool CpuDataPlugin::addTidBySort(int32_t tid)
535 {
536     auto tidsEnd = tidVec_.end();
537     auto it = std::lower_bound(tidVec_.begin(), tidsEnd, tid);
538     CHECK_TRUE(!(it != tidsEnd && *it == tid), false, "addTidBySort failed");
539     it = tidVec_.insert(it, std::move(tid));
540     return true;
541 }
542 
OpenDestDir(std::string& dirPath)543 DIR* CpuDataPlugin::OpenDestDir(std::string& dirPath)
544 {
545     DIR* destDir = nullptr;
546 
547     destDir = opendir(dirPath.c_str());
548     CHECK_NOTNULL(destDir, nullptr, "%s:failed to opendir(%s), errno=%d", __func__, dirPath.c_str(), errno);
549 
550     return destDir;
551 }
552 
GetValidTid(DIR* dirp)553 int32_t CpuDataPlugin::GetValidTid(DIR* dirp)
554 {
555     CHECK_TRUE(dirp, 0, "dirp is nullptr");
556     while (struct dirent* dirEnt = readdir(dirp)) {
557         if (dirEnt->d_type != DT_DIR) {
558             continue;
559         }
560 
561         int32_t tid = atoi(dirEnt->d_name);
562         if (tid) {
563             return tid;
564         }
565     }
566     return 0;
567 }
568 
GetThreadState(const char threadState)569 ThreadState CpuDataPlugin::GetThreadState(const char threadState)
570 {
571     ThreadState state = THREAD_UNSPECIFIED;
572     switch (threadState) {
573         case 'R':
574             state = THREAD_RUNNING;
575             break;
576         case 'S':
577             state = THREAD_SLEEPING;
578             break;
579         case 'T':
580             state = THREAD_STOPPED;
581             break;
582         case 'D':
583             state = THREAD_WAITING;
584             break;
585         default:
586             break;
587     }
588 
589     return state;
590 }
591 
WriteThread(T& threadInfo, const char* pFile, uint32_t fileLen, int32_t tid)592 template <typename T> void CpuDataPlugin::WriteThread(T& threadInfo, const char* pFile, uint32_t fileLen, int32_t tid)
593 {
594     BufferSplitter totalbuffer(const_cast<char*>(pFile), fileLen + 1);
595     std::vector<std::string> cpuUsageVec;
596     for (int i = 0; i < STAT_COUNT; i++) {
597         if (i == THREAD_NAME_POS) { // 线程名是')'作为结束符
598             totalbuffer.NextWord(')');
599         } else {
600             totalbuffer.NextWord(' ');
601         }
602 
603         if (!totalbuffer.CurWord()) {
604             return;
605         }
606 
607         if (i == THREAD_NAME_POS) {
608             size_t nameLeng = totalbuffer.CurWordSize() > 1 ? static_cast<size_t>(totalbuffer.CurWordSize() - 1) : 0;
609             std::string curWord = std::string(totalbuffer.CurWord() + 1, nameLeng);
610             threadInfo.set_thread_name(curWord);
611         } else if (i == THREAD_STATE_POS) {
612             std::string curWord = std::string(totalbuffer.CurWord(), totalbuffer.CurWordSize());
613             ThreadState state = GetThreadState(curWord[0]);
614             threadInfo.set_thread_state(state);
615         } else if (i >= STAT_START) {
616             std::string curWord = std::string(totalbuffer.CurWord(), totalbuffer.CurWordSize());
617             cpuUsageVec.push_back(curWord);
618         }
619     }
620 
621     // 获取到的数据不包含utime、stime、cutime、cstime四个数值时返回
622     if (cpuUsageVec.size() != PROCESS_UNSPECIFIED) {
623         PROFILER_LOG_ERROR(LOG_CORE, "%s:failed to get thread cpu usage, size=%zu", __func__, cpuUsageVec.size());
624         return;
625     }
626 
627     // 第一次获取该线程数据时需要将前一个数据置为0
628     if (prevThreadCpuTimeMap_.find(tid) == prevThreadCpuTimeMap_.end()) {
629         prevThreadCpuTimeMap_[tid] = 0;
630     }
631 
632     int64_t usageTime = GetCpuUsageTime(cpuUsageVec);
633     threadInfo.set_prev_thread_cpu_time_ms(prevThreadCpuTimeMap_[tid]);
634     threadInfo.set_thread_cpu_time_ms(usageTime);
635     prevThreadCpuTimeMap_[tid] = usageTime;
636     threadInfo.set_tid(tid);
637 
638     auto* timestamp = threadInfo.mutable_timestamp();
639     SetTimestamp(*timestamp);
640 }
641 
WriteSingleThreadInfo(T& cpuData, int32_t tid)642 template <typename T> void CpuDataPlugin::WriteSingleThreadInfo(T& cpuData, int32_t tid)
643 {
644     std::string fileName = path_ + std::to_string(pid_) + "/task/" + std::to_string(tid) + "/stat";
645     int32_t ret = ReadFile(fileName);
646     if (ret == RET_FAIL) {
647         return;
648     }
649     if ((buffer_ == nullptr) || (ret == 0)) {
650         return;
651     }
652     auto* threadInfo = cpuData.add_thread_info();
653     WriteThread(*threadInfo, reinterpret_cast<char*>(buffer_), ret, tid);
654 }
655 
WriteThreadInfo(T& cpuData)656 template <typename T> void CpuDataPlugin::WriteThreadInfo(T& cpuData)
657 {
658     DIR* procDir = nullptr;
659     std::string path = path_ + std::to_string(pid_) + "/task";
660     procDir = OpenDestDir(path);
661     if (procDir == nullptr) {
662         return;
663     }
664 
665     tidVec_.clear();
666     while (int32_t tid = GetValidTid(procDir)) {
667         addTidBySort(tid);
668     }
669 
670     for (unsigned int i = 0; i < tidVec_.size(); i++) {
671         WriteSingleThreadInfo(cpuData, tidVec_[i]);
672     }
673     closedir(procDir);
674 }
675 
676 // for UT
SetFreqPath(std::string path)677 void CpuDataPlugin::SetFreqPath(std::string path)
678 {
679     freqPath_ = path + FREQUENCY_PATH;
680 }
681