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