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 "memory_data_plugin.h"
16
17 #include <cmath>
18 #include <sstream>
19
20 #include "memory_plugin_result.pbencoder.h"
21 #include "securec.h"
22 #include "smaps_stats.h"
23
24 namespace {
25 using namespace OHOS::HDI::Memorytracker::V1_0;
26 using namespace OHOS::Developtools::Profiler;
27 using namespace OHOS::HiviewDFX::UCollectUtil;
28 using namespace OHOS::HiviewDFX::UCollect;
29 using OHOS::HiviewDFX::CollectResult;
30 using OHOS::HiviewDFX::GraphicType;
31
32 const char* CMD_FORMAT = "memory service meminfo --local ";
33 constexpr size_t READ_BUFFER_SIZE = 1024 * 16;
34 constexpr int BUF_MAX_LEN = 2048;
35 constexpr int MAX_ZRAM_DEVICES = 256;
36 constexpr int ZRAM_KB = 1024;
37 constexpr size_t DEFAULT_READ_SIZE = 4096;
38 const std::string FAKE_DATA_PATH = "/data/local/tmp";
39 constexpr int DATA_START_LINES = 3;
40 constexpr size_t PAGE_SIZE = 4096;
41 constexpr size_t KB_TO_BYTES = 1024;
42 constexpr size_t MB_TO_BYTES = 1024 * 1024;
43 constexpr int INDENT_CATEGORY_NUM = 2;
44 constexpr int INDENT_SUB_TYPE_NUM = 4;
45 const std::string TOTAL_DMA_STR = "Total dma"; // flag for total of DMA memory size
46 const std::string RS_IMAGE_CACHE_START_STR = "RSImageCache:"; // flag for start RSImageCache data
47 const std::string RS_IMAGE_CACHE_END_STR = " pixelmap:"; // flag for end RSImageCache data
48 const std::string TOTAL_CPU_STR = "Total CPU memory usage"; // flag for total of CPU memory size
49 const std::string SKIA_GPU_STR = "Skia GPU Caches"; // flag for GPU
50 const std::string GPU_LIMIT_STR = "gpu limit"; // flag for gpu limit size
51 const std::string RENDER_SERVICE_NAME = "render_service";
52 const std::string MGR_SVC_START_STR = "----------------------------------WindowManagerService------------";
53 const std::string MGR_SVC_END_STR = "Focus window";
54 const std::string MGR_SVC_INTERVAL_STR = "----------------------------------------------------------";
55 const std::string MEM_PROFILE_STR = "Channel:";
56 } // namespace
57
MemoryDataPlugin()58 MemoryDataPlugin::MemoryDataPlugin() : meminfoFd_(-1), vmstatFd_(-1), err_(-1)
59 {
60 InitProto2StrVector();
61 SetPath(const_cast<char*>("/proc"));
62 buffer_ = std::make_unique<uint8_t[]>(READ_BUFFER_SIZE);
63 }
64
~MemoryDataPlugin()65 MemoryDataPlugin::~MemoryDataPlugin()
66 {
67 PROFILER_LOG_INFO(LOG_CORE, "%s:~MemoryDataPlugin!", __func__);
68
69 buffer_ = nullptr;
70
71 if (meminfoFd_ > 0) {
72 close(meminfoFd_);
73 meminfoFd_ = -1;
74 }
75 if (vmstatFd_ > 0) {
76 close(vmstatFd_);
77 vmstatFd_ = -1;
78 }
79 for (auto it = pidFds_.begin(); it != pidFds_.end(); it++) {
80 for (int i = FILE_STATUS; i <= FILE_SMAPS; i++) {
81 if (it->second[i] != -1) {
82 close(it->second[i]);
83 }
84 }
85 }
86 return;
87 }
88
InitProto2StrVector()89 void MemoryDataPlugin::InitProto2StrVector()
90 {
91 int maxprotobufid = 0;
92 for (unsigned int i = 0; i < sizeof(meminfoMapping) / sizeof(meminfoMapping[0]); i++) {
93 maxprotobufid = std::max(meminfoMapping[i].protobufid, maxprotobufid);
94 }
95 meminfoStrList_.resize(maxprotobufid + 1);
96
97 for (unsigned int i = 0; i < sizeof(meminfoMapping) / sizeof(meminfoMapping[0]); i++) {
98 meminfoStrList_[meminfoMapping[i].protobufid] = meminfoMapping[i].procstr;
99 }
100
101 maxprotobufid = 0;
102 for (unsigned int i = 0; i < sizeof(vmeminfoMapping) / sizeof(vmeminfoMapping[0]); i++) {
103 maxprotobufid = std::max(vmeminfoMapping[i].protobufid, maxprotobufid);
104 }
105 vmstatStrList_.resize(maxprotobufid + 1);
106
107 for (unsigned int i = 0; i < sizeof(vmeminfoMapping) / sizeof(vmeminfoMapping[0]); i++) {
108 vmstatStrList_[vmeminfoMapping[i].protobufid] = vmeminfoMapping[i].procstr;
109 }
110
111 return;
112 }
113
InitMemVmemFd()114 int MemoryDataPlugin::InitMemVmemFd()
115 {
116 if (protoConfig_.report_sysmem_mem_info()) {
117 char fileName[PATH_MAX + 1] = {0};
118 char realPath[PATH_MAX + 1] = {0};
119 CHECK_TRUE(snprintf_s(fileName, sizeof(fileName), sizeof(fileName) - 1, "%s/meminfo", testpath_) >= 0, RET_FAIL,
120 "%s:snprintf_s error", __func__);
121 if (realpath(fileName, realPath) == nullptr) {
122 const int bufSize = 256;
123 char buf[bufSize] = { 0 };
124 strerror_r(errno, buf, bufSize);
125 PROFILER_LOG_ERROR(LOG_CORE, "%s:realpath failed, errno(%d:%s)", __func__, errno, buf);
126 return RET_FAIL;
127 }
128 meminfoFd_ = open(realPath, O_RDONLY | O_CLOEXEC);
129 if (meminfoFd_ == -1) {
130 const int bufSize = 256;
131 char buf[bufSize] = { 0 };
132 strerror_r(errno, buf, bufSize);
133 PROFILER_LOG_ERROR(LOG_CORE, "%s:open failed, fileName, errno(%d:%s)", __func__, errno, buf);
134 return RET_FAIL;
135 }
136 }
137
138 if (protoConfig_.report_sysmem_vmem_info()) {
139 char fileName[PATH_MAX + 1] = {0};
140 char realPath[PATH_MAX + 1] = {0};
141 CHECK_TRUE(snprintf_s(fileName, sizeof(fileName), sizeof(fileName) - 1, "%s/vmstat", testpath_) >= 0, RET_FAIL,
142 "%s:snprintf_s error", __func__);
143 if (realpath(fileName, realPath) == nullptr) {
144 const int bufSize = 256;
145 char buf[bufSize] = { 0 };
146 strerror_r(errno, buf, bufSize);
147 PROFILER_LOG_ERROR(LOG_CORE, "%s:realpath failed, errno(%d:%s)", __func__, errno, buf);
148 return RET_FAIL;
149 }
150 vmstatFd_ = open(realPath, O_RDONLY | O_CLOEXEC);
151 if (vmstatFd_ == -1) {
152 const int bufSize = 256;
153 char buf[bufSize] = { 0 };
154 strerror_r(errno, buf, bufSize);
155 PROFILER_LOG_ERROR(LOG_CORE, "%s:failed to open(/proc/vmstat), errno(%d:%s)", __func__, errno, buf);
156 return RET_FAIL;
157 }
158 }
159
160 return RET_SUCC;
161 }
162
Start(const uint8_t * configData,uint32_t configSize)163 int MemoryDataPlugin::Start(const uint8_t* configData, uint32_t configSize)
164 {
165 CHECK_NOTNULL(buffer_, RET_FAIL, "%s:buffer_ == null", __func__);
166
167 CHECK_TRUE(protoConfig_.ParseFromArray(configData, configSize) > 0, RET_FAIL,
168 "%s:parseFromArray failed!", __func__);
169
170 CHECK_TRUE(InitMemVmemFd() == RET_SUCC, RET_FAIL, "InitMemVmemFd fail");
171
172 if (protoConfig_.sys_meminfo_counters().size() > 0) {
173 for (int i = 0; i < protoConfig_.sys_meminfo_counters().size(); i++) {
174 CHECK_TRUE((size_t)protoConfig_.sys_meminfo_counters(i) < meminfoStrList_.size(), RET_FAIL,
175 "%s:sys meminfo counter index invalid!", __func__);
176 if (meminfoStrList_[protoConfig_.sys_meminfo_counters(i)]) {
177 meminfoCounters_.emplace(meminfoStrList_[protoConfig_.sys_meminfo_counters(i)],
178 protoConfig_.sys_meminfo_counters(i));
179 }
180 }
181 }
182
183 if (protoConfig_.sys_vmeminfo_counters().size() > 0) {
184 for (int i = 0; i < protoConfig_.sys_vmeminfo_counters().size(); i++) {
185 CHECK_TRUE((size_t)protoConfig_.sys_vmeminfo_counters(i) < vmstatStrList_.size(), RET_FAIL,
186 "%s:vmstat counter index invalid!", __func__);
187 if (vmstatStrList_[protoConfig_.sys_vmeminfo_counters(i)]) {
188 vmstatCounters_.emplace(vmstatStrList_[protoConfig_.sys_vmeminfo_counters(i)],
189 protoConfig_.sys_vmeminfo_counters(i));
190 }
191 }
192 }
193
194 if (protoConfig_.pid().size() > 0) {
195 for (int i = 0; i < protoConfig_.pid().size(); i++) {
196 int32_t pid = protoConfig_.pid(i);
197 pidFds_.emplace(pid, OpenProcPidFiles(pid));
198 }
199 }
200
201 PROFILER_LOG_INFO(LOG_CORE, "%s:start success!", __func__);
202 return RET_SUCC;
203 }
204
WriteMeminfo(T & memoryData)205 template <typename T> void MemoryDataPlugin::WriteMeminfo(T& memoryData)
206 {
207 int readsize = ReadFile(meminfoFd_);
208 if (readsize == RET_FAIL) {
209 return;
210 }
211 BufferSplitter totalbuffer((const char*)buffer_.get(), readsize);
212
213 do {
214 if (!totalbuffer.NextWord(':')) {
215 continue;
216 }
217 const_cast<char *>(totalbuffer.CurWord())[totalbuffer.CurWordSize()] = '\0';
218 auto it = meminfoCounters_.find(totalbuffer.CurWord());
219 if (it == meminfoCounters_.end()) {
220 continue;
221 }
222
223 int counter_id = it->second;
224 if (!totalbuffer.NextWord(' ')) {
225 continue;
226 }
227 auto value = static_cast<uint64_t>(strtoll(totalbuffer.CurWord(), nullptr, DEC_BASE));
228 auto* meminfo = memoryData.add_meminfo();
229
230 meminfo->set_key(static_cast<SysMeminfoType>(counter_id));
231 meminfo->set_value(value);
232 } while (totalbuffer.NextLine());
233
234 return;
235 }
236
WriteZramData(T & memoryData)237 template <typename T> void MemoryDataPlugin::WriteZramData(T& memoryData)
238 {
239 uint64_t zramSum = 0;
240 for (int i = 0; i < MAX_ZRAM_DEVICES; i++) {
241 std::string path = "/sys/block/zram" + std::to_string(i);
242 if (access(path.c_str(), F_OK) == 0) {
243 uint64_t zramValue = 0;
244 std::string file = path + "/mm_stat";
245 auto fptr = std::unique_ptr<FILE, decltype(&fclose)>{fopen(file.c_str(), "rb"), fclose};
246 if (fptr != nullptr) {
247 int ret = fscanf_s(fptr.get(), "%*" PRIu64 " %*" PRIu64 " %" PRIu64, &zramValue);
248 if (ret != 1) {
249 file = path + "/mem_used_total";
250 std::string content = ReadFile(file);
251 char* end = nullptr;
252 uint64_t value = strtoull(content.c_str(), &end, DEC_BASE);
253 zramValue = (value > 0) ? value : 0;
254 }
255 }
256
257 zramSum += zramValue;
258 }
259 }
260
261 memoryData.set_zram(zramSum / ZRAM_KB);
262 }
263
WriteVmstat(T & memoryData)264 template <typename T> void MemoryDataPlugin::WriteVmstat(T& memoryData)
265 {
266 int readsize = ReadFile(vmstatFd_);
267 if (readsize == RET_FAIL) {
268 return;
269 }
270 BufferSplitter totalbuffer((const char*)buffer_.get(), readsize);
271
272 do {
273 if (!totalbuffer.NextWord(' ')) {
274 continue;
275 }
276 const_cast<char *>(totalbuffer.CurWord())[totalbuffer.CurWordSize()] = '\0';
277 auto it = vmstatCounters_.find(totalbuffer.CurWord());
278 if (it == vmstatCounters_.end()) {
279 continue;
280 }
281
282 int counter_id = it->second;
283 char* valuestr = const_cast<char *>(totalbuffer.CurWord() + totalbuffer.CurWordSize() + 1);
284 valuestr[totalbuffer.CurLineSize() - (valuestr - totalbuffer.CurLine())] = '\0';
285
286 auto value = static_cast<uint64_t>(strtoll(valuestr, nullptr, DEC_BASE));
287 auto* vmeminfo = memoryData.add_vmeminfo();
288
289 vmeminfo->set_key(static_cast<SysVMeminfoType>(counter_id));
290 vmeminfo->set_value(value);
291 } while (totalbuffer.NextLine());
292
293 return;
294 }
295
WriteAppsummary(T & processMemoryInfo,SmapsStats & smapInfo)296 template <typename T> void MemoryDataPlugin::WriteAppsummary(T& processMemoryInfo, SmapsStats& smapInfo)
297 {
298 auto* memsummary = processMemoryInfo.mutable_memsummary();
299 memsummary->set_java_heap(smapInfo.GetProcessJavaHeap());
300 memsummary->set_native_heap(smapInfo.GetProcessNativeHeap());
301 memsummary->set_code(smapInfo.GetProcessCode());
302 memsummary->set_stack(smapInfo.GetProcessStack());
303 memsummary->set_graphics(smapInfo.GetProcessGraphics());
304 memsummary->set_private_other(smapInfo.GetProcessPrivateOther());
305 memsummary->set_system(smapInfo.GetProcessSystem());
306 }
307
ParseNumber(std::string line)308 int MemoryDataPlugin::ParseNumber(std::string line)
309 {
310 return atoi(line.substr(line.find_first_of("01234567890")).c_str());
311 }
312
GetMemInfoByMemoryService(uint32_t pid,T & processMemoryInfo)313 template <typename T> bool MemoryDataPlugin::GetMemInfoByMemoryService(uint32_t pid, T& processMemoryInfo)
314 {
315 std::string fullCmd = CMD_FORMAT + std::to_string(pid);
316
317 std::unique_ptr<uint8_t[]> buffer {new (std::nothrow) uint8_t[BUF_MAX_LEN]};
318 std::unique_ptr<FILE, int (*)(FILE*)> fp(popen(fullCmd.c_str(), "r"), pclose);
319 CHECK_TRUE(fp, false, "%s:popen error", __func__);
320
321 size_t ret = fread(buffer.get(), 1, BUF_MAX_LEN, fp.get());
322 if (ret == 0) {
323 PROFILER_LOG_ERROR(LOG_CORE, "%s:fread failed", __func__);
324 }
325 buffer.get()[BUF_MAX_LEN - 1] = '\0';
326
327 return ParseMemInfo(reinterpret_cast<char*>(buffer.get()), processMemoryInfo);
328 }
329
WriteMemoryData(T & memoryDataProto,S smapsInfo)330 template <typename T, typename S> void MemoryDataPlugin::WriteMemoryData(T& memoryDataProto, S smapsInfo)
331 {
332 if (protoConfig_.report_process_tree()) {
333 WriteProcesseList(memoryDataProto);
334 }
335
336 if (protoConfig_.report_sysmem_mem_info()) {
337 WriteMeminfo(memoryDataProto);
338 WriteZramData(memoryDataProto);
339 }
340
341 if (protoConfig_.report_sysmem_vmem_info()) {
342 WriteVmstat(memoryDataProto);
343 }
344
345 for (int i = 0; i < protoConfig_.pid().size(); i++) {
346 int32_t pid = protoConfig_.pid(i);
347 auto* processinfo = memoryDataProto.add_processesinfo();
348 if (protoConfig_.report_process_mem_info()) {
349 WriteProcinfoByPidfds(*processinfo, pid);
350 }
351
352 bool isReportApp = protoConfig_.report_app_mem_info() && !protoConfig_.report_app_mem_by_memory_service();
353 bool isReportSmaps = protoConfig_.report_smaps_mem_info();
354 if (i == 0 && (isReportApp || isReportSmaps)) {
355 SmapsStats smapInfo;
356 smapInfo.ParseMaps(pid, *processinfo, smapsInfo, isReportApp, isReportSmaps);
357 if (isReportApp) {
358 WriteAppsummary(*processinfo, smapInfo);
359 }
360 }
361 }
362
363 if (protoConfig_.report_purgeable_ashmem_info()) {
364 WriteAshmemInfo(memoryDataProto);
365 }
366
367 if (protoConfig_.report_dma_mem_info()) {
368 WriteDmaInfo(memoryDataProto);
369 }
370
371 if (protoConfig_.report_gpu_mem_info()) {
372 WriteGpuMemInfo(memoryDataProto);
373 }
374
375 WriteDumpProcessInfo(memoryDataProto);
376
377 if (protoConfig_.report_gpu_dump_info()) {
378 WriteGpuDumpInfo(memoryDataProto);
379 WriteManagerServiceInfo(memoryDataProto);
380 WriteProfileMemInfo(memoryDataProto);
381 }
382 }
383
ReportOptimize(RandomWriteCtx * randomWrite)384 int MemoryDataPlugin::ReportOptimize(RandomWriteCtx* randomWrite)
385 {
386 ProtoEncoder::MemoryData dataProto(randomWrite);
387 ProtoEncoder::SmapsInfo* smapsInfo = nullptr;
388 WriteMemoryData(dataProto, smapsInfo);
389
390 int msgSize = dataProto.Finish();
391 return msgSize;
392 }
393
Report(uint8_t * data,uint32_t dataSize)394 int MemoryDataPlugin::Report(uint8_t* data, uint32_t dataSize)
395 {
396 MemoryData dataProto;
397 SmapsInfo* smapsInfo = nullptr;
398 WriteMemoryData(dataProto, smapsInfo);
399
400 uint32_t length = dataProto.ByteSizeLong();
401 if (length > dataSize) {
402 return -length;
403 }
404 if (dataProto.SerializeToArray(data, length) > 0) {
405 return length;
406 }
407 return 0;
408 }
409
Stop()410 int MemoryDataPlugin::Stop()
411 {
412 if (meminfoFd_ > 0) {
413 close(meminfoFd_);
414 meminfoFd_ = -1;
415 }
416 if (vmstatFd_ > 0) {
417 close(vmstatFd_);
418 vmstatFd_ = -1;
419 }
420 for (auto it = pidFds_.begin(); it != pidFds_.end(); it++) {
421 for (int i = FILE_STATUS; i <= FILE_SMAPS; i++) {
422 if (it->second[i] != -1) {
423 close(it->second[i]);
424 it->second[i] = -1;
425 }
426 }
427 }
428 PROFILER_LOG_INFO(LOG_CORE, "%s:stop success!", __func__);
429 return 0;
430 }
431
WriteProcinfoByPidfds(T & processMemoryInfo,int32_t pid)432 template <typename T> void MemoryDataPlugin::WriteProcinfoByPidfds(T& processMemoryInfo, int32_t pid)
433 {
434 char* end = nullptr;
435 int32_t readSize;
436
437 readSize = ReadFile(pidFds_[pid][FILE_STATUS]);
438 if (readSize != RET_FAIL) {
439 WriteProcess(processMemoryInfo, reinterpret_cast<char*>(buffer_.get()), readSize, pid);
440 }
441
442 if (ReadFile(pidFds_[pid][FILE_OOM]) != RET_FAIL) {
443 processMemoryInfo.set_oom_score_adj(static_cast<int64_t>(strtol(reinterpret_cast<char*>(buffer_.get()),
444 &end, DEC_BASE)));
445 }
446 return;
447 }
448
ReadFile(int fd)449 int32_t MemoryDataPlugin::ReadFile(int fd)
450 {
451 if ((buffer_.get() == nullptr) || (fd == -1)) {
452 return RET_FAIL;
453 }
454 int readsize = pread(fd, buffer_.get(), READ_BUFFER_SIZE - 1, 0);
455 if (readsize <= 0) {
456 const int bufSize = 256;
457 char buf[bufSize] = { 0 };
458 strerror_r(errno, buf, bufSize);
459 PROFILER_LOG_ERROR(LOG_CORE, "%s:failed to read(%d), errno(%d:%s)", __func__, fd, errno, buf);
460 err_ = errno;
461 return RET_FAIL;
462 }
463 return readsize;
464 }
465
ReadFile(const std::string & path)466 std::string MemoryDataPlugin::ReadFile(const std::string& path)
467 {
468 char realPath[PATH_MAX] = {0};
469 CHECK_TRUE((path.length() < PATH_MAX) && (realpath(path.c_str(), realPath) != nullptr), "",
470 "%s:path is invalid: %s, errno=%d", __func__, path.c_str(), errno);
471 int fd = open(realPath, O_RDONLY);
472 if (fd == -1) {
473 const int maxSize = 256;
474 char buf[maxSize] = { 0 };
475 strerror_r(errno, buf, maxSize);
476 PROFILER_LOG_WARN(LOG_CORE, "open file %s FAILED: %s!", path.c_str(), buf);
477 return "";
478 }
479
480 std::string content;
481 size_t count = 0;
482 while (true) {
483 if (content.size() - count < DEFAULT_READ_SIZE) {
484 content.resize(content.size() + DEFAULT_READ_SIZE);
485 }
486 ssize_t nBytes = read(fd, &content[count], content.size() - count);
487 if (nBytes <= 0) {
488 break;
489 }
490 count += static_cast<size_t>(nBytes);
491 }
492 content.resize(count);
493 CHECK_TRUE(close(fd) != -1, content, "close %s failed, %d", path.c_str(), errno);
494 return content;
495 }
496
OpenProcPidFiles(int32_t pid)497 std::vector<int> MemoryDataPlugin::OpenProcPidFiles(int32_t pid)
498 {
499 char fileName[PATH_MAX + 1] = {0};
500 char realPath[PATH_MAX + 1] = {0};
501 int count = sizeof(procfdMapping) / sizeof(procfdMapping[0]);
502 std::vector<int> profds;
503
504 for (int i = 0; i < count; i++) {
505 if (snprintf_s(fileName, sizeof(fileName), sizeof(fileName) - 1,
506 "%s/%d/%s", testpath_, pid, procfdMapping[i].file) < 0) {
507 PROFILER_LOG_ERROR(LOG_CORE, "%s:snprintf_s error", __func__);
508 }
509 if (realpath(fileName, realPath) == nullptr) {
510 const int bufSize = 256;
511 char buf[bufSize] = { 0 };
512 strerror_r(errno, buf, bufSize);
513 PROFILER_LOG_ERROR(LOG_CORE, "%s:realpath failed, errno(%d:%s)", __func__, errno, buf);
514 }
515 int fd = open(realPath, O_RDONLY | O_CLOEXEC);
516 if (fd == -1) {
517 const int bufSize = 256;
518 char buf[bufSize] = { 0 };
519 strerror_r(errno, buf, bufSize);
520 PROFILER_LOG_ERROR(LOG_CORE, "%s:failed to open(%s), errno(%d:%s)", __func__, fileName, errno, buf);
521 }
522 profds.emplace(profds.begin() + i, fd);
523 }
524 return profds;
525 }
526
OpenDestDir(const char * dirPath)527 DIR* MemoryDataPlugin::OpenDestDir(const char* dirPath)
528 {
529 DIR* destDir = nullptr;
530
531 destDir = opendir(dirPath);
532 if (destDir == nullptr) {
533 const int bufSize = 256;
534 char buf[bufSize] = { 0 };
535 strerror_r(errno, buf, bufSize);
536 PROFILER_LOG_ERROR(LOG_CORE, "%s:failed to opendir(%s), errno(%d:%s)", __func__, dirPath, errno, buf);
537 }
538
539 return destDir;
540 }
541
GetValidPid(DIR * dirp)542 int32_t MemoryDataPlugin::GetValidPid(DIR* dirp)
543 {
544 if (!dirp) return 0;
545 while (struct dirent* dirEnt = readdir(dirp)) {
546 if (dirEnt->d_type != DT_DIR) {
547 continue;
548 }
549
550 int32_t pid = atoi(dirEnt->d_name);
551 if (pid) {
552 return pid;
553 }
554 }
555 return 0;
556 }
557
ReadProcPidFile(int32_t pid,const char * pFileName)558 int32_t MemoryDataPlugin::ReadProcPidFile(int32_t pid, const char* pFileName)
559 {
560 char fileName[PATH_MAX + 1] = {0};
561 char realPath[PATH_MAX + 1] = {0};
562 int fd = -1;
563 ssize_t bytesRead = 0;
564 CHECK_TRUE(snprintf_s(fileName, sizeof(fileName), sizeof(fileName) - 1, "%s/%d/%s", testpath_, pid, pFileName) >= 0,
565 RET_FAIL, "%s:snprintf_s error", __func__);
566 if (realpath(fileName, realPath) == nullptr) {
567 const int bufSize = 256;
568 char buf[bufSize] = { 0 };
569 strerror_r(errno, buf, bufSize);
570 PROFILER_LOG_ERROR(LOG_CORE, "%s:realpath failed, errno(%d:%s)", __func__, errno, buf);
571 return RET_FAIL;
572 }
573 fd = open(realPath, O_RDONLY | O_CLOEXEC);
574 if (fd == -1) {
575 const int bufSize = 256;
576 char buf[bufSize] = { 0 };
577 strerror_r(errno, buf, bufSize);
578 PROFILER_LOG_INFO(LOG_CORE, "%s:failed to open(%s), errno(%d:%s)", __func__, fileName, errno, buf);
579 err_ = errno;
580 return RET_FAIL;
581 }
582 if (buffer_.get() == nullptr) {
583 PROFILER_LOG_INFO(LOG_CORE, "%s:empty address, buffer_ is NULL", __func__);
584 err_ = RET_NULL_ADDR;
585 close(fd);
586 return RET_FAIL;
587 }
588 bytesRead = read(fd, buffer_.get(), READ_BUFFER_SIZE - 1);
589 if (bytesRead < 0) {
590 close(fd);
591 const int bufSize = 256;
592 char buf[bufSize] = { 0 };
593 strerror_r(errno, buf, bufSize);
594 PROFILER_LOG_INFO(LOG_CORE, "%s:failed to read(%s), errno(%d:%s)", __func__, fileName, errno, buf);
595 err_ = errno;
596 return RET_FAIL;
597 }
598 buffer_.get()[bytesRead] = '\0';
599 close(fd);
600
601 return bytesRead;
602 }
603
BufnCmp(const char * src,int srcLen,const char * key,int keyLen)604 bool MemoryDataPlugin::BufnCmp(const char* src, int srcLen, const char* key, int keyLen)
605 {
606 if (!src || !key || (srcLen < keyLen)) {
607 return false;
608 }
609 for (int i = 0; i < keyLen; i++) {
610 if (*src++ != *key++) {
611 return false;
612 }
613 }
614 return true;
615 }
616
addPidBySort(int32_t pid)617 bool MemoryDataPlugin::addPidBySort(int32_t pid)
618 {
619 auto pidsEnd = seenPids_.end();
620 auto it = std::lower_bound(seenPids_.begin(), pidsEnd, pid);
621 if (it != pidsEnd && *it == pid) {
622 return false;
623 }
624 it = seenPids_.insert(it, std::move(pid));
625 return true;
626 }
627
GetProcStatusId(const char * src,int srcLen)628 int MemoryDataPlugin::GetProcStatusId(const char* src, int srcLen)
629 {
630 int count = sizeof(procStatusMapping) / sizeof(procStatusMapping[0]);
631 for (int i = 0; i < count; i++) {
632 if (BufnCmp(src, srcLen, procStatusMapping[i].procstr, strlen(procStatusMapping[i].procstr))) {
633 return procStatusMapping[i].procid;
634 }
635 }
636 return RET_FAIL;
637 }
638
StringToUll(const char * word,uint64_t & value)639 bool MemoryDataPlugin::StringToUll(const char* word, uint64_t& value)
640 {
641 char* end = nullptr;
642 errno = 0;
643 value = strtoull(word, &end, DEC_BASE);
644 if ((errno == ERANGE && (value == ULLONG_MAX)) || (errno != 0 && value == 0)) {
645 return false;
646 } else if (end == word && (*word >= '0' && *word <= '9')) {
647 return false;
648 }
649
650 return true;
651 }
652
SetProcessInfo(T & processMemoryInfo,int key,const char * word)653 template <typename T> void MemoryDataPlugin::SetProcessInfo(T& processMemoryInfo, int key, const char* word)
654 {
655 uint64_t value;
656
657 if ((key >= PRO_TGID && key <= PRO_PURGPIN && key != PRO_NAME) && !StringToUll(word, value)) {
658 PROFILER_LOG_ERROR(LOG_CORE, "MemoryDataPlugin:%s, strtoull failed, key(%d), word(%s)", __func__, key, word);
659 return;
660 }
661
662 switch (key) {
663 case PRO_TGID:
664 processMemoryInfo.set_pid(static_cast<int32_t>(value));
665 break;
666 case PRO_VMSIZE:
667 processMemoryInfo.set_vm_size_kb(value);
668 break;
669 case PRO_VMRSS:
670 processMemoryInfo.set_vm_rss_kb(value);
671 break;
672 case PRO_RSSANON:
673 processMemoryInfo.set_rss_anon_kb(value);
674 break;
675 case PRO_RSSFILE:
676 processMemoryInfo.set_rss_file_kb(value);
677 break;
678 case PRO_RSSSHMEM:
679 processMemoryInfo.set_rss_shmem_kb(value);
680 break;
681 case PRO_VMSWAP:
682 processMemoryInfo.set_vm_swap_kb(value);
683 break;
684 case PRO_VMLCK:
685 processMemoryInfo.set_vm_locked_kb(value);
686 break;
687 case PRO_VMHWM:
688 processMemoryInfo.set_vm_hwm_kb(value);
689 break;
690 case PRO_PURGSUM:
691 processMemoryInfo.set_purg_sum_kb(value);
692 break;
693 case PRO_PURGPIN:
694 processMemoryInfo.set_purg_pin_kb(value);
695 break;
696 default:
697 break;
698 }
699 return;
700 }
701
702 template <typename T>
WriteProcess(T & processMemoryInfo,const char * pFile,uint32_t fileLen,int32_t pid)703 void MemoryDataPlugin::WriteProcess(T& processMemoryInfo, const char* pFile, uint32_t fileLen, int32_t pid)
704 {
705 BufferSplitter totalbuffer(const_cast<const char*>(pFile), fileLen + 1);
706
707 do {
708 totalbuffer.NextWord(':');
709 if (!totalbuffer.CurWord()) {
710 return;
711 }
712
713 int key = GetProcStatusId(totalbuffer.CurWord(), totalbuffer.CurWordSize());
714 totalbuffer.NextWord('\n');
715 if (!totalbuffer.CurWord()) {
716 continue;
717 }
718 if (key == PRO_NAME) {
719 processMemoryInfo.set_name(totalbuffer.CurWord(), totalbuffer.CurWordSize());
720 }
721 SetProcessInfo(processMemoryInfo, key, totalbuffer.CurWord());
722 } while (totalbuffer.NextLine());
723 // update process name
724 int32_t ret = ReadProcPidFile(pid, "cmdline");
725 if (ret > 0) {
726 processMemoryInfo.set_name(reinterpret_cast<char*>(buffer_.get()),
727 strlen(reinterpret_cast<char*>(buffer_.get())));
728 }
729 }
730
WriteOomInfo(T & processMemoryInfo,int32_t pid)731 template <typename T> void MemoryDataPlugin::WriteOomInfo(T& processMemoryInfo, int32_t pid)
732 {
733 char* end = nullptr;
734
735 if (ReadProcPidFile(pid, "oom_score_adj") == RET_FAIL) {
736 return;
737 }
738 if (buffer_.get() == nullptr) {
739 PROFILER_LOG_ERROR(LOG_CORE, "%s:invalid params, read buffer_ is NULL", __func__);
740 return;
741 }
742 processMemoryInfo.set_oom_score_adj(static_cast<int64_t>(strtol(reinterpret_cast<char*>(buffer_.get()),
743 &end, DEC_BASE)));
744 }
745
WriteProcessInfo(T & memoryData,int32_t pid)746 template <typename T> void MemoryDataPlugin::WriteProcessInfo(T& memoryData, int32_t pid)
747 {
748 int32_t ret = ReadProcPidFile(pid, "status");
749 if (ret == RET_FAIL) {
750 return;
751 }
752 if ((buffer_.get() == nullptr) || (ret == 0)) {
753 return;
754 }
755 auto* processinfo = memoryData.add_processesinfo();
756 WriteProcess(*processinfo, reinterpret_cast<char*>(buffer_.get()), ret, pid);
757 WriteOomInfo(*processinfo, pid);
758 }
759
WriteProcesseList(T & memoryData)760 template <typename T> void MemoryDataPlugin::WriteProcesseList(T& memoryData)
761 {
762 DIR* procDir = nullptr;
763
764 procDir = OpenDestDir(testpath_);
765 if (procDir == nullptr) {
766 return;
767 }
768
769 seenPids_.clear();
770 while (int32_t pid = GetValidPid(procDir)) {
771 addPidBySort(pid);
772 }
773
774 for (unsigned int i = 0; i < seenPids_.size(); i++) {
775 WriteProcessInfo(memoryData, seenPids_[i]);
776 }
777 closedir(procDir);
778 }
779
SetAshmemInfo(T & ashmemInfo,int key,const char * word)780 template <typename T> void MemoryDataPlugin::SetAshmemInfo(T& ashmemInfo, int key, const char* word)
781 {
782 int64_t value = 0;
783 int64_t size = 0;
784 switch (key) {
785 case ASHMEM_PROCESS_NAME:
786 ashmemInfo.set_name(word);
787 break;
788 case ASHMEM_PID:
789 value = strtol(word, nullptr, DEC_BASE);
790 CHECK_TRUE(value > 0, NO_RETVAL, "%s:strtoull pid failed", __func__);
791 ashmemInfo.set_pid(value);
792 break;
793 case ASHMEM_FD:
794 value = strtol(word, nullptr, DEC_BASE);
795 CHECK_TRUE(value >= 0, NO_RETVAL, "%s:strtoull fd failed", __func__);
796 ashmemInfo.set_fd(value);
797 break;
798 case ASHMEM_ADJ:
799 value = static_cast<int64_t>(strtoull(word, nullptr, DEC_BASE));
800 CHECK_TRUE(value >= 0, NO_RETVAL, "%s:strtoull adj failed", __func__);
801 ashmemInfo.set_adj(value);
802 break;
803 case ASHMEM_NAME:
804 ashmemInfo.set_ashmem_name(word);
805 break;
806 case ASHMEM_SIZE:
807 size = strtol(word, nullptr, DEC_BASE);
808 CHECK_TRUE(size >= 0, NO_RETVAL, "%s:strtoull size failed", __func__);
809 ashmemInfo.set_size(size);
810 break;
811 case ASHMEM_ID:
812 value = strtol(word, nullptr, DEC_BASE);
813 CHECK_TRUE(value >= 0, NO_RETVAL, "%s:strtoull id failed", __func__);
814 ashmemInfo.set_id(value);
815 break;
816 case ASHMEM_TIME:
817 size = strtol(word, nullptr, DEC_BASE);
818 CHECK_TRUE(size >= 0, NO_RETVAL, "%s:strtoull time failed", __func__);
819 ashmemInfo.set_time(size);
820 break;
821 case ASHMEM_REF_COUNT:
822 size = strtol(word, nullptr, DEC_BASE);
823 CHECK_TRUE(size >= 0, NO_RETVAL, "%s:strtoull ref_count failed", __func__);
824 ashmemInfo.set_ref_count(size);
825 break;
826 case ASHMEM_PURGED:
827 size = strtol(word, nullptr, DEC_BASE);
828 CHECK_TRUE(size >= 0, NO_RETVAL, "%s:strtoull purged failed", __func__);
829 ashmemInfo.set_purged(size);
830 break;
831 default:
832 break;
833 }
834 }
835
WriteAshmemInfo(T & dataProto)836 template <typename T> void MemoryDataPlugin::WriteAshmemInfo(T& dataProto)
837 {
838 std::string path = protoConfig_.report_fake_data() ? FAKE_DATA_PATH : std::string(testpath_);
839 std::string file = path + "/purgeable_ashmem_trigger";
840 std::ifstream input(file, std::ios::in);
841 if (input.fail()) {
842 PROFILER_LOG_ERROR(LOG_CORE, "%s:open %s failed, errno = %d", __func__, file.c_str(), errno);
843 return;
844 }
845
846 int lines = 0;
847 do {
848 if (!input.good()) {
849 return;
850 }
851
852 std::string line;
853 getline(input, line);
854 line += '\n';
855 if (++lines <= DATA_START_LINES) {
856 // The first three lines are not data.
857 continue;
858 }
859
860 BufferSplitter totalBuffer(static_cast<const char*>(line.c_str()), line.size() + 1);
861 if (!totalBuffer.NextWord(',')) {
862 break;
863 }
864 auto* ashmemInfo = dataProto.add_ashmeminfo();
865 for (int i = ASHMEM_PROCESS_NAME; i <= ASHMEM_PURGED; i++) {
866 std::string curWord = std::string(totalBuffer.CurWord(), totalBuffer.CurWordSize());
867 SetAshmemInfo(*ashmemInfo, i, curWord.c_str());
868 char delimiter = (i == ASHMEM_REF_COUNT) ? '\n' : ',';
869 if (!totalBuffer.NextWord(delimiter)) {
870 break;
871 }
872 }
873 } while (!input.eof());
874 input.close();
875 }
876
SetDmaInfo(T & dmaInfo,int key,const char * word)877 template <typename T> void MemoryDataPlugin::SetDmaInfo(T& dmaInfo, int key, const char* word)
878 {
879 int64_t value = 0;
880 int64_t size = 0;
881 switch (key) {
882 case DMA_NAME:
883 dmaInfo.set_name(word);
884 break;
885 case DMA_PID:
886 value = strtol(word, nullptr, DEC_BASE);
887 CHECK_TRUE(value > 0, NO_RETVAL, "%s:strtoull pid failed", __func__);
888 dmaInfo.set_pid(value);
889 break;
890 case DMA_FD:
891 value = strtol(word, nullptr, DEC_BASE);
892 CHECK_TRUE(value >= 0, NO_RETVAL, "%s:strtoull fd failed", __func__);
893 dmaInfo.set_fd(value);
894 break;
895 case DMA_SIZE:
896 size = strtol(word, nullptr, DEC_BASE);
897 CHECK_TRUE(size >= 0, NO_RETVAL, "%s:strtoull size failed", __func__);
898 dmaInfo.set_size(size);
899 break;
900 case DMA_INO:
901 value = strtol(word, nullptr, DEC_BASE);
902 CHECK_TRUE(value >= 0, NO_RETVAL, "%s:strtoull magic failed", __func__);
903 dmaInfo.set_ino(value);
904 break;
905 case DMA_EXP_PID:
906 value = strtol(word, nullptr, DEC_BASE);
907 CHECK_TRUE(value > 0, NO_RETVAL, "%s:strtoull exp_pid failed", __func__);
908 dmaInfo.set_exp_pid(value);
909 break;
910 case DMA_EXP_TASK_COMM:
911 dmaInfo.set_exp_task_comm(word);
912 break;
913 case DMA_BUF_NAME:
914 dmaInfo.set_buf_name(word);
915 break;
916 case DMA_EXP_NAME:
917 if (*(word + strlen(word) - 1) == '\r') {
918 dmaInfo.set_exp_name(std::string(word, strlen(word) - 1));
919 } else {
920 dmaInfo.set_exp_name(word);
921 }
922 break;
923 default:
924 break;
925 }
926 }
927
WriteDmaInfo(T & dataProto)928 template <typename T> void MemoryDataPlugin::WriteDmaInfo(T& dataProto)
929 {
930 std::string file = std::string(testpath_) + "/process_dmabuf_info";
931 std::ifstream input(file, std::ios::in);
932 if (input.fail()) {
933 PROFILER_LOG_ERROR(LOG_CORE, "%s:open %s failed, errno = %d", __func__, file.c_str(), errno);
934 return;
935 }
936
937 int lines = 0;
938 do {
939 if (!input.good()) {
940 return;
941 }
942
943 std::string line;
944 getline(input, line);
945 line += '\n';
946 if (++lines < DATA_START_LINES
947 || strncmp(line.c_str(), TOTAL_DMA_STR.c_str(), TOTAL_DMA_STR.size()) == 0) {
948 continue; // not data.
949 }
950
951 BufferSplitter totalBuffer(static_cast<const char*>(line.c_str()), line.size() + 1);
952 if (!totalBuffer.NextWord(' ')) {
953 break;
954 }
955 auto* dmaInfo = dataProto.add_dmainfo();
956 for (int i = DMA_NAME; i <= DMA_EXP_NAME; i++) {
957 std::string curWord = std::string(totalBuffer.CurWord(), totalBuffer.CurWordSize());
958 SetDmaInfo(*dmaInfo, i, curWord.c_str());
959 char delimiter = (i == DMA_BUF_NAME) ? '\n' : ' ';
960 if (!totalBuffer.NextWord(delimiter)) {
961 break;
962 }
963 }
964 } while (!input.eof());
965 input.close();
966 }
967
SetGpuProcessInfo(T & gpuProcessInfo,int key,const char * word)968 template <typename T> void MemoryDataPlugin::SetGpuProcessInfo(T& gpuProcessInfo, int key, const char* word)
969 {
970 int64_t value = 0;
971 int64_t size = 0;
972 switch (key) {
973 case GPU_ADDR:
974 gpuProcessInfo.set_addr(word);
975 break;
976 case GPU_TID:
977 value = strtol(word, nullptr, DEC_BASE);
978 CHECK_TRUE(value > 0, NO_RETVAL, "%s:strtoull tid failed", __func__);
979 gpuProcessInfo.set_tid(value);
980 break;
981 case GPU_PID:
982 value = strtol(word, nullptr, DEC_BASE);
983 CHECK_TRUE(value > 0, NO_RETVAL, "%s:strtoull pid failed", __func__);
984 gpuProcessInfo.set_pid(value);
985 break;
986 case GPU_USED_SIZE:
987 size = strtol(word, nullptr, DEC_BASE);
988 CHECK_TRUE(size >= 0, NO_RETVAL, "%s:strtoull used_gpu_size failed", __func__);
989 gpuProcessInfo.set_used_gpu_size(size * PAGE_SIZE);
990 break;
991 default:
992 break;
993 }
994 }
995
WriteGpuMemInfo(T & dataProto)996 template <typename T> void MemoryDataPlugin::WriteGpuMemInfo(T& dataProto)
997 {
998 std::string path = protoConfig_.report_fake_data() ? FAKE_DATA_PATH : std::string(testpath_);
999 std::string file = path + "/gpu_memory";
1000 std::ifstream input(file, std::ios::in);
1001 if (input.fail()) {
1002 PROFILER_LOG_ERROR(LOG_CORE, "%s:open %s failed, errno = %d", __func__, file.c_str(), errno);
1003 return;
1004 }
1005
1006 std::string line;
1007 getline(input, line);
1008 line += '\n';
1009
1010 BufferSplitter totalBuffer(static_cast<const char*>(line.c_str()), line.size() + 1);
1011 auto* gpuMemoryInfo = dataProto.add_gpumemoryinfo();
1012 if (totalBuffer.NextWord(' ')) {
1013 gpuMemoryInfo->set_gpu_name(std::string(totalBuffer.CurWord(), totalBuffer.CurWordSize()));
1014 }
1015 if (totalBuffer.NextWord('\n')) {
1016 gpuMemoryInfo->set_all_gpu_size((strtoull(totalBuffer.CurWord(), nullptr, DEC_BASE) * PAGE_SIZE));
1017 }
1018
1019 do {
1020 if (!input.good()) {
1021 return;
1022 }
1023
1024 getline(input, line);
1025 line += '\n';
1026
1027 BufferSplitter buffer(static_cast<const char*>(line.c_str()), line.size() + 1);
1028 if (!buffer.NextWord(' ')) {
1029 break;
1030 }
1031 auto* gpuProcessInfo = gpuMemoryInfo->add_gpu_process_info();
1032 for (int i = GPU_ADDR; i <= GPU_USED_SIZE; i++) {
1033 std::string curWord = std::string(buffer.CurWord(), buffer.CurWordSize());
1034 SetGpuProcessInfo(*gpuProcessInfo, i, curWord.c_str());
1035 if (!buffer.NextWord(' ')) {
1036 break;
1037 }
1038 }
1039 } while (!input.eof());
1040 input.close();
1041 }
1042
RunCommand(const std::string & cmd)1043 std::string MemoryDataPlugin::RunCommand(const std::string& cmd)
1044 {
1045 std::string ret = "";
1046 std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd.c_str(), "r"), pclose);
1047 if (!pipe) {
1048 PROFILER_LOG_ERROR(LOG_CORE, "%s:popen(%s) error", __func__, cmd.c_str());
1049 return ret;
1050 }
1051
1052 std::array<char, READ_BUFFER_SIZE> buffer;
1053 while (fgets(buffer.data(), buffer.size(), pipe.get()) != nullptr) {
1054 ret += buffer.data();
1055 }
1056 return ret;
1057 }
1058
GetIndentNum(const std::string & line)1059 int MemoryDataPlugin::GetIndentNum(const std::string& line)
1060 {
1061 int indentNum = 0;
1062 while (isspace(line[indentNum])) {
1063 indentNum++;
1064 }
1065 return indentNum;
1066 }
1067
SizeToBytes(const std::string & sizeStr,const std::string & type)1068 uint64_t MemoryDataPlugin::SizeToBytes(const std::string& sizeStr, const std::string& type)
1069 {
1070 auto size = std::atof(sizeStr.c_str());
1071 uint64_t byteSize = round(size);
1072 if (type == "KB") {
1073 byteSize = round(size * KB_TO_BYTES);
1074 } else if (type == "MB") {
1075 byteSize = round(size * MB_TO_BYTES);
1076 }
1077 return byteSize;
1078 }
1079
SetGpuDumpInfo(T & gpuDumpInfo,BufferSplitter & totalBuffer)1080 template <typename T> bool MemoryDataPlugin::SetGpuDumpInfo(T& gpuDumpInfo, BufferSplitter& totalBuffer)
1081 {
1082 int ret = totalBuffer.NextLine();
1083 CHECK_TRUE(ret, false, "totalBuffer is end!");
1084 std::string line = std::string(totalBuffer.CurLine());
1085 int indentNum = GetIndentNum(line);
1086 while (indentNum > 0) {
1087 if (indentNum == INDENT_CATEGORY_NUM && totalBuffer.NextWord(':')) {
1088 auto* gpuDetailInfo = gpuDumpInfo.add_gpu_detail_info();
1089 gpuDetailInfo->set_module_name(std::string(totalBuffer.CurWord(), totalBuffer.CurWordSize()));
1090 ret = totalBuffer.NextLine();
1091 CHECK_TRUE(ret, false, "totalBuffer is end!");
1092 line = std::string(totalBuffer.CurLine());
1093 indentNum = GetIndentNum(line);
1094 while (indentNum == INDENT_SUB_TYPE_NUM && totalBuffer.NextWord(':')) {
1095 auto* gpuSubInfo = gpuDetailInfo->add_gpu_sub_info();
1096 gpuSubInfo->set_category_name(std::string(totalBuffer.CurWord(), totalBuffer.CurWordSize()));
1097 std::string size = "";
1098 if (totalBuffer.NextWord(' ')) {
1099 size = std::string(totalBuffer.CurWord(), totalBuffer.CurWordSize());
1100 }
1101 if (totalBuffer.NextWord(' ')) {
1102 std::string type = std::string(totalBuffer.CurWord(), totalBuffer.CurWordSize());
1103 gpuSubInfo->set_size(SizeToBytes(size, type));
1104 }
1105 if (totalBuffer.NextWord(' ')) {
1106 gpuSubInfo->set_entry_num(strtoull(totalBuffer.CurWord() + 1, nullptr, DEC_BASE));
1107 }
1108 ret = totalBuffer.NextLine();
1109 CHECK_TRUE(ret, false, "totalBuffer is end!");
1110 line = std::string(totalBuffer.CurLine());
1111 indentNum = GetIndentNum(line);
1112 }
1113 }
1114 }
1115
1116 ret = totalBuffer.NextLine();
1117 CHECK_TRUE(ret, false, "totalBuffer is end!");
1118 std::string size = "";
1119 if (totalBuffer.NextWord('(') && totalBuffer.NextWord(' ')) {
1120 size = std::string(totalBuffer.CurWord(), totalBuffer.CurWordSize());
1121 }
1122 if (totalBuffer.NextWord(' ')) {
1123 std::string type = std::string(totalBuffer.CurWord(), totalBuffer.CurWordSize());
1124 gpuDumpInfo.set_gpu_purgeable_size(SizeToBytes(size, type));
1125 }
1126 return true;
1127 }
1128
SetRSImageDumpInfo(T & rsDumpInfo,int key,const char * word)1129 template <typename T> void MemoryDataPlugin::SetRSImageDumpInfo(T& rsDumpInfo, int key, const char* word)
1130 {
1131 int64_t pid = 0;
1132 int64_t size = 0;
1133 size_t dataLen = strlen(word) > 1 ? strlen(word) - 1 : 0;
1134 switch (key) {
1135 case RS_SIZE:
1136 size = strtol(word, nullptr, DEC_BASE);
1137 CHECK_TRUE(size >= 0, NO_RETVAL, "%s:strtoull size failed", __func__);
1138 rsDumpInfo.set_size(size);
1139 break;
1140 case RS_TYPE:
1141 if (*(word + dataLen) == '\t') {
1142 rsDumpInfo.set_type(std::string(word, dataLen));
1143 } else {
1144 rsDumpInfo.set_type(word);
1145 }
1146 break;
1147 case RS_PID:
1148 pid = strtol(word, nullptr, DEC_BASE);
1149 CHECK_TRUE(pid >= 0, NO_RETVAL, "%s:strtoull pid failed", __func__);
1150 rsDumpInfo.set_pid(pid);
1151 break;
1152 case RS_SURFACENAME:
1153 if (*(word + dataLen) == '\t') {
1154 rsDumpInfo.set_surface_name(std::string(word, dataLen));
1155 } else {
1156 rsDumpInfo.set_surface_name(word);
1157 }
1158 break;
1159 default:
1160 break;
1161 }
1162 }
1163
WriteGpuDumpInfo(T & dataProto)1164 template <typename T> void MemoryDataPlugin::WriteGpuDumpInfo(T& dataProto)
1165 {
1166 std::string content = "";
1167 if (!protoConfig_.report_fake_data()) {
1168 if (strcmp(testpath_, "/proc") == 0) {
1169 content = RunCommand("hidumper -s 10 '-a dumpMem'");
1170 } else {
1171 // for UT
1172 content = ReadFile(std::string(testpath_) + "/dumpMem.txt");
1173 }
1174 } else {
1175 content = ReadFile(FAKE_DATA_PATH + "/dumpMem.txt");
1176 }
1177 CHECK_TRUE(content != "", NO_RETVAL, "hidumper no data!");
1178
1179 BufferSplitter totalBuffer((const char*)content.c_str(), content.size() + 1);
1180 do {
1181 std::string line = totalBuffer.CurLine();
1182 if (strncmp(line.c_str(), RS_IMAGE_CACHE_START_STR.c_str(), RS_IMAGE_CACHE_START_STR.size()) == 0) {
1183 totalBuffer.NextLine(); // data starts from the next line
1184 while (totalBuffer.NextLine()) {
1185 line = totalBuffer.CurLine();
1186 if (strncmp(line.c_str(), RS_IMAGE_CACHE_END_STR.c_str(), RS_IMAGE_CACHE_END_STR.size()) == 0) {
1187 break;
1188 }
1189 auto* rsDumpInfo = dataProto.add_rsdumpinfo();
1190 for (int i = RS_SIZE; i <= RS_SURFACENAME; i++) {
1191 if (!totalBuffer.NextWord(' ')) {
1192 break;
1193 }
1194 std::string curWord = std::string(totalBuffer.CurWord(), totalBuffer.CurWordSize());
1195 SetRSImageDumpInfo(*rsDumpInfo, i, curWord.c_str());
1196 }
1197 }
1198 } else if (strncmp(line.c_str(), TOTAL_CPU_STR.c_str(), TOTAL_CPU_STR.size()) == 0) {
1199 totalBuffer.NextLine(); // data starts from the next line
1200 if (!totalBuffer.NextWord(' ')) {
1201 break;
1202 }
1203 auto* cpuDumpInfo = dataProto.add_cpudumpinfo();
1204 cpuDumpInfo->set_total_cpu_memory_size(strtoull(totalBuffer.CurWord(), nullptr, DEC_BASE));
1205 } else if (strncmp(line.c_str(), SKIA_GPU_STR.c_str(), SKIA_GPU_STR.size()) == 0) {
1206 auto* gpuDumpInfo = dataProto.add_gpudumpinfo();
1207 if (totalBuffer.NextWord(':') && (totalBuffer.NextWord(' ') || totalBuffer.NextWord('\n'))) {
1208 std::string name = std::string(totalBuffer.CurWord(), totalBuffer.CurWordSize());
1209 gpuDumpInfo->set_window_name(name.substr(0, name.find("\r")));
1210 }
1211 if (totalBuffer.NextWord('\n')) {
1212 gpuDumpInfo->set_id(strtoull(totalBuffer.CurWord(), nullptr, DEC_BASE));
1213 }
1214 SetGpuDumpInfo(*gpuDumpInfo, totalBuffer);
1215 } else if (strncmp(line.c_str(), GPU_LIMIT_STR.c_str(), GPU_LIMIT_STR.size()) == 0) {
1216 if (totalBuffer.NextWord('=') && totalBuffer.NextWord(' ')) {
1217 dataProto.set_gpu_limit_size(strtoull(totalBuffer.CurWord(), nullptr, DEC_BASE));
1218 }
1219 if (totalBuffer.NextWord('=') && totalBuffer.NextWord(' ')) {
1220 dataProto.set_gpu_used_size(strtoull(totalBuffer.CurWord(), nullptr, DEC_BASE));
1221 }
1222 break;
1223 }
1224 } while (totalBuffer.NextLine());
1225 }
1226
isRenderService(int pid)1227 bool MemoryDataPlugin::isRenderService(int pid)
1228 {
1229 if (ReadProcPidFile(pid, "cmdline") > 0) {
1230 std::string processName(reinterpret_cast<char*>(buffer_.get()), strlen(reinterpret_cast<char*>(buffer_.get())));
1231 int index = static_cast<int>(processName.size()) - static_cast<int>(RENDER_SERVICE_NAME.size());
1232 if (index >= 0 && processName.substr(index) == RENDER_SERVICE_NAME) {
1233 return true;
1234 }
1235 }
1236 return false;
1237 }
1238
GetRenderServiceGlSize(int32_t pid,GraphicsMemory & graphicsMemory)1239 bool MemoryDataPlugin::GetRenderServiceGlSize(int32_t pid, GraphicsMemory& graphicsMemory)
1240 {
1241 // refers hidumper
1242 // https://gitee.com/openharmony/hiviewdfx_hidumper/blob/master/frameworks/native/src/executor/memory/memory_info.cpp#L203
1243 bool ret = false;
1244 sptr<IMemoryTrackerInterface> memtrack = IMemoryTrackerInterface::Get(true);
1245 if (memtrack == nullptr) {
1246 PROFILER_LOG_ERROR(LOG_CORE, "%s: get IMemoryTrackerInterface failed", __func__);
1247 return ret;
1248 }
1249
1250 std::vector<MemoryRecord> records;
1251 if (memtrack->GetDevMem(pid, MEMORY_TRACKER_TYPE_GL, records) == HDF_SUCCESS) {
1252 uint64_t value = 0;
1253 for (const auto& record : records) {
1254 if ((static_cast<uint32_t>(record.flags) & FLAG_UNMAPPED) == FLAG_UNMAPPED) {
1255 value = static_cast<uint64_t>(record.size / KB_TO_BYTES);
1256 break;
1257 }
1258 }
1259 graphicsMemory.gl = value;
1260 ret = true;
1261 }
1262 return ret;
1263 }
1264
GetGraphicsMemory(int32_t pid,GraphicsMemory & graphicsMemory,GraphicType graphicType)1265 bool MemoryDataPlugin::GetGraphicsMemory(int32_t pid, GraphicsMemory &graphicsMemory, GraphicType graphicType)
1266 {
1267 std::shared_ptr<GraphicMemoryCollector> collector = GraphicMemoryCollector::Create();
1268 OHOS::HiviewDFX::CollectResult<int32_t> data;
1269 data = collector->GetGraphicUsage(pid, graphicType);
1270 if (data.retCode != UcError::SUCCESS) {
1271 PROFILER_LOG_ERROR(LOG_CORE, "%s:collect progress GL or Graph error, ret:%d.", __func__, data.retCode);
1272 return false;
1273 }
1274 if (graphicType == GraphicType::GL) {
1275 graphicsMemory.gl = data.data;
1276 } else if (graphicType == GraphicType::GRAPH) {
1277 graphicsMemory.graph = data.data;
1278 } else {
1279 PROFILER_LOG_ERROR(LOG_CORE, "%s:graphic type is not support.", __func__);
1280 return false;
1281 }
1282 return true;
1283 }
1284
WriteDumpProcessInfo(T & dataProto)1285 template <typename T> void MemoryDataPlugin::WriteDumpProcessInfo(T& dataProto)
1286 {
1287 for (int i = 0; i < protoConfig_.pid().size(); i++) {
1288 int32_t pid = protoConfig_.pid(i);
1289 auto* processesInfo = dataProto.add_processesinfo();
1290 processesInfo->set_pid(pid);
1291
1292 // refers hidumper
1293 // https://gitee.com/openharmony/hiviewdfx_hidumper/blob/master/frameworks/native/src/executor/memory/memory_info.cpp#L260
1294 GraphicsMemory graphicsMemory;
1295 if (GetGraphicsMemory(pid, graphicsMemory, GraphicType::GL)) {
1296 processesInfo->set_gl_pss_kb(graphicsMemory.gl);
1297 }
1298 if (GetGraphicsMemory(pid, graphicsMemory, GraphicType::GRAPH)) {
1299 processesInfo->set_graph_pss_kb(graphicsMemory.graph);
1300 }
1301 }
1302 }
1303
SetManagerServiceInfo(T & windowinfo,int key,const char * word)1304 template <typename T> void MemoryDataPlugin::SetManagerServiceInfo(T& windowinfo, int key, const char* word)
1305 {
1306 int64_t pid = 0;
1307 switch (key) {
1308 case WINDOW_NAME:
1309 windowinfo.set_window_name(word);
1310 break;
1311 case WINDOW_PID:
1312 pid = strtol(word, nullptr, DEC_BASE);
1313 CHECK_TRUE(pid >= 0, NO_RETVAL, "%s:strtoull pid failed", __func__);
1314 windowinfo.set_pid(pid);
1315 break;
1316 default:
1317 break;
1318 }
1319 }
1320
WriteManagerServiceInfo(T & dataProto)1321 template <typename T> void MemoryDataPlugin::WriteManagerServiceInfo(T& dataProto)
1322 {
1323 std::string content = "";
1324 if (strcmp(testpath_, "/proc") == 0) {
1325 content = RunCommand("hidumper -s WindowManagerService -a '-a'");
1326 } else {
1327 // for UT
1328 content = ReadFile(std::string(testpath_) + "/window_manager_service.txt");
1329 }
1330 CHECK_TRUE(content != "", NO_RETVAL, "hidumper WindowManagerService no data!");
1331
1332 BufferSplitter totalBuffer((const char*)content.c_str(), content.size() + 1);
1333 do {
1334 std::string line = totalBuffer.CurLine();
1335 if (strncmp(line.c_str(), MGR_SVC_START_STR.c_str(), MGR_SVC_START_STR.size()) == 0) {
1336 totalBuffer.NextLine();
1337 totalBuffer.NextLine(); // data starts from the next line
1338 while (totalBuffer.NextLine()) {
1339 line = totalBuffer.CurLine();
1340 if (strncmp(line.c_str(), MGR_SVC_INTERVAL_STR.c_str(), MGR_SVC_INTERVAL_STR.size()) == 0) {
1341 continue;
1342 }
1343
1344 if (strncmp(line.c_str(), MGR_SVC_END_STR.c_str(), MGR_SVC_END_STR.size()) == 0) {
1345 return;
1346 }
1347
1348 auto* windowinfo = dataProto.add_windowinfo();
1349 for (int i = WINDOW_NAME; i <= WINDOW_PID; i++) {
1350 if (!totalBuffer.NextWord(' ')) {
1351 break;
1352 }
1353 std::string curWord = std::string(totalBuffer.CurWord(), totalBuffer.CurWordSize());
1354 SetManagerServiceInfo(*windowinfo, i, curWord.c_str());
1355 }
1356 }
1357 }
1358 } while (totalBuffer.NextLine());
1359 }
1360
WriteProfileMemInfo(T & dataProto)1361 template <typename T> void MemoryDataPlugin::WriteProfileMemInfo(T& dataProto)
1362 {
1363 for (int i = 0; i < protoConfig_.pid().size(); i++) {
1364 std::string file = "";
1365 if (!protoConfig_.report_fake_data()) {
1366 if (strcmp(testpath_, "/proc") == 0) {
1367 file = "/sys/kernel/debug/mali0/ctx/" + std::to_string(protoConfig_.pid(i)) + "_0/mem_profile";
1368 } else {
1369 // for UT
1370 file = std::string(testpath_) + "/mem_profile.txt";
1371 }
1372 } else {
1373 file = FAKE_DATA_PATH + "/mem_profile.txt";
1374 }
1375
1376 std::ifstream input(file, std::ios::in);
1377 if (input.fail()) {
1378 PROFILER_LOG_ERROR(LOG_CORE, "%s:open %s failed, errno = %d", __func__, file.c_str(), errno);
1379 return;
1380 }
1381
1382 do {
1383 if (!input.good()) {
1384 return;
1385 }
1386
1387 std::string line;
1388 getline(input, line);
1389 line += '\n';
1390
1391 if (strncmp(line.c_str(), MEM_PROFILE_STR.c_str(), MEM_PROFILE_STR.size()) == 0) {
1392 auto* profileMemInfo = dataProto.add_profilememinfo();
1393 BufferSplitter totalBuffer(static_cast<const char*>(line.c_str()), line.size() + 1);
1394 if (totalBuffer.NextWord(':') && totalBuffer.NextWord('(')) {
1395 size_t dataLen =
1396 totalBuffer.CurWordSize() > 1 ? static_cast<size_t>(totalBuffer.CurWordSize() - 1) : 0;
1397 profileMemInfo->set_channel(std::string(totalBuffer.CurWord(), dataLen));
1398 }
1399 if (totalBuffer.NextWord(':') && totalBuffer.NextWord(')')) {
1400 profileMemInfo->set_total_memory_size(strtoull(totalBuffer.CurWord(), nullptr, DEC_BASE));
1401 }
1402 }
1403 } while (!input.eof());
1404 input.close();
1405 }
1406 }
1407