• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2021 Huawei Device Co., Ltd.
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 "smaps_stats.h"
16 
17 #include "securec.h"
18 
19 namespace {
20 const int PERCENT = 100;
21 
MatchHead(const std::string & name,const char * str)22 bool MatchHead(const std::string& name, const char* str)
23 {
24     return strncmp(name.c_str(), str, strlen(str)) == 0;
25 }
26 
MatchTail(const std::string & name,const char * str)27 bool MatchTail(const std::string& name, const char* str)
28 {
29     CHECK_TRUE(name.size() >= strlen(str), false, "str is larger than name");
30     int index = name.size() - strlen(str);
31     if (index < 0) {
32         return false;
33     }
34     return (name.substr(index) == str);
35 }
36 } // namespace
ParseCategory(const SmapsHeadInfo & smapsHeadInfo)37 std::string SmapsStats::ParseCategory(const SmapsHeadInfo& smapsHeadInfo)
38 {
39     std::string category;
40     if (GetCategoryFromMap(smapsHeadInfo.path, category, endMap_, &MatchTail) ||
41         GetCategoryFromMap(smapsHeadInfo.path, category, beginMap_, &MatchHead)) {
42         return category;
43     }
44     category = smapsHeadInfo.iNode > 0 ? FILE_PAGE_TAG : ANON_PAGE_TAG;
45     return category;
46 }
47 
GetCategoryFromMap(const std::string & name,std::string & category,const std::map<std::string,std::string> & map,MatchFunc func)48 bool SmapsStats::GetCategoryFromMap(const std::string &name, std::string &category,
49                                     const std::map<std::string, std::string> &map, MatchFunc func)
50 {
51     for (const auto &p : map) {
52         if (func(name, p.first.c_str())) {
53             category = p.second;
54             return true;
55         }
56     }
57     return false;
58 }
59 
ParseMaps(int pid,ProcessMemoryInfo & processinfo,bool isReportApp,bool isReportSmaps)60 bool SmapsStats::ParseMaps(int pid, ProcessMemoryInfo& processinfo, bool isReportApp, bool isReportSmaps)
61 {
62     std::string smaps_path = std::string("/proc/") + std::to_string(pid) + std::string("/smaps");
63     if (testpath_.size() > 0) {
64         smaps_path = testpath_ + std::to_string(pid) + std::string("/smaps");
65     }
66     ReadVmemareasFile(smaps_path, processinfo, isReportApp, isReportSmaps);
67     if (isReportApp) {
68         ReviseStatsData();
69     }
70     return true;
71 }
72 
ReadVmemareasFile(const std::string & path,ProcessMemoryInfo & processinfo,bool isReportApp,bool isReportSmaps)73 bool SmapsStats::ReadVmemareasFile(const std::string& path, ProcessMemoryInfo& processinfo, bool isReportApp,
74                                    bool isReportSmaps)
75 {
76     SmapsInfo* smapsInfo = nullptr;
77     bool findMapHead = false;
78     MapPiecesInfo mappic = {0};
79     MemUsageInfo memusage = {0};
80     SmapsHeadInfo smapsHeadInfo = {};
81     uint64_t prevEnd = 0;
82     int prevHeap = 0;
83     std::ifstream input(path, std::ios::in);
84     CHECK_TRUE(!input.fail(), false, "%s:open %s failed, errno = %d", __func__, path.c_str(), errno);
85     do {
86         if (!input.good()) {
87             return false;
88         }
89         std::string line;
90         getline(input, line);
91         line += '\n';
92         if (!findMapHead) {
93             // 00400000-00409000 r-xp 00000000 fc:00 426998  /usr/lib/gvfs/gvfsd-http
94             ParseMapHead(line, mappic, smapsHeadInfo);
95             findMapHead = true;
96             if (isReportSmaps) {
97                 smapsInfo = processinfo.add_smapinfo();
98                 smapsInfo->set_start_addr(smapsHeadInfo.startAddrStr);
99                 smapsInfo->set_end_addr(smapsHeadInfo.endAddrStr);
100                 smapsInfo->set_permission(smapsHeadInfo.permission);
101                 smapsInfo->set_path(smapsHeadInfo.path);
102                 smapsInfo->set_category(ParseCategory(smapsHeadInfo));
103             }
104             continue;
105         }
106         if (findMapHead && GetMemUsageField(line, memusage)) {
107             if (!lastline_) {
108                 continue;
109             }
110             if (isReportSmaps) {
111                 smapsInfo->set_size(memusage.vss);
112                 smapsInfo->set_rss(memusage.rss);
113                 smapsInfo->set_pss(memusage.pss);
114                 smapsInfo->set_dirty(memusage.privateDirty + memusage.sharedDirty);
115                 smapsInfo->set_swapper(memusage.swap + memusage.swapPss);
116                 smapsInfo->set_reside(static_cast<double>(memusage.rss) / memusage.vss * PERCENT);
117                 smapsInfo->set_private_clean(memusage.privateClean);
118                 smapsInfo->set_private_dirty(memusage.privateDirty);
119                 smapsInfo->set_shared_clean(memusage.sharedClean);
120                 smapsInfo->set_shared_dirty(memusage.sharedDirty);
121                 smapsInfo->set_swap(memusage.swap);
122                 smapsInfo->set_swap_pss(memusage.swapPss);
123             }
124         }
125         if (isReportApp) {
126             CollectVmemAreasData(mappic, memusage, prevEnd, prevHeap);
127         }
128         findMapHead = false;
129         lastline_ = false;
130     } while (!input.eof());
131     input.close();
132     return true;
133 }
134 
GetVMAStuId(int ops,std::string name,const VmeminfoAreaMapping vma[],int count,int32_t heapIndex[2],bool & swappable)135 bool SmapsStats::GetVMAStuId(int ops,
136                              std::string name,
137                              const VmeminfoAreaMapping vma[],
138                              int count,
139                              int32_t heapIndex[2],
140                              bool& swappable)
141 {
142     for (int i = 0; i < count; i++) {
143         if (ops == OPS_START) {
144             if (MatchHead(name, vma[i].heapstr)) {
145                 heapIndex[0] = vma[i].heapid[0];
146                 heapIndex[1] = vma[i].heapid[1];
147                 swappable = false;
148                 return true;
149             }
150         } else if (ops == OPS_END) {
151             if (MatchTail(name, vma[i].heapstr)) {
152                 if (vma[i].heapid[1] == VMHEAP_NEEDFIX) {
153                     HeapIndexFix(name, vma[i].heapstr, heapIndex);
154                 } else {
155                     heapIndex[0] = vma[i].heapid[0];
156                     heapIndex[1] = vma[i].heapid[1];
157                 }
158                 swappable = true;
159                 return true;
160             }
161         }
162     }
163     return false;
164 }
165 
GetVmaIndex(std::string name,uint32_t namesz,int32_t heapIndex[2],bool & swappable)166 bool SmapsStats::GetVmaIndex(std::string name, uint32_t namesz, int32_t heapIndex[2], bool& swappable)
167 {
168     switch (name[0]) {
169         case '[':
170             if (MatchHead(name, "[heap]") || MatchHead(name, "[stack")) {
171                 return GetVMAStuId(OPS_START, name, g_vmaMemHeap, sizeof(g_vmaMemHeap) /
172                                     sizeof(g_vmaMemHeap[0]), heapIndex, swappable);
173             } else if (MatchHead(name, "[anon:")) {
174                 if (MatchHead(name, "[anon:sensitive_vm-") &&
175                     GetVMAStuId(OPS_END, name, g_vmaMemSuffix, sizeof(g_vmaMemSuffix) /
176                     sizeof(g_vmaMemSuffix[0]), heapIndex, swappable)) {
177                         return true;
178                 }
179                 return GetVMAStuId(OPS_START, name, g_vmaMemAnon, sizeof(g_vmaMemAnon) /
180                                     sizeof(g_vmaMemAnon[0]), heapIndex, swappable);
181             }
182             break;
183         case '/':
184             if (MatchHead(name, "/memfd:")) {
185                 return GetVMAStuId(OPS_START, name, g_vmaMemFd, sizeof(g_vmaMemFd) /
186                                     sizeof(g_vmaMemFd[0]), heapIndex, swappable);
187             } else if (MatchHead(name, "/dev/")) {
188                 return GetVMAStuId(OPS_START, name, g_vmaMemDev, sizeof(g_vmaMemDev) /
189                                     sizeof(g_vmaMemDev[0]), heapIndex, swappable);
190             } else {
191                 return GetVMAStuId(OPS_END, name, g_vmaMemSuffix, sizeof(g_vmaMemSuffix) /
192                                     sizeof(g_vmaMemSuffix[0]), heapIndex, swappable);
193             }
194             break;
195         default:
196             return GetVMAStuId(OPS_END, name, g_vmaMemSuffix, sizeof(g_vmaMemSuffix) /
197                                 sizeof(g_vmaMemSuffix[0]), heapIndex, swappable);
198             break;
199     }
200     if (namesz > strlen(".sensitive_jvbin") && strstr(name.c_str(), ".sensitive_jvbin") != nullptr) {
201         heapIndex[0] = VMHEAP_SENSITIVE_JVBIN;
202         heapIndex[1] = VMHEAP_SENSITIVE_JVBIN_APP_SENSITIVE_JVBIN;
203         swappable = true;
204         return true;
205     }
206     return false;
207 }
208 
CollectVmemAreasData(const MapPiecesInfo & mempic,const MemUsageInfo & memusage,uint64_t & prevEnd,int & prevHeap)209 void SmapsStats::CollectVmemAreasData(const MapPiecesInfo& mempic,
210                                       const MemUsageInfo& memusage,
211                                       uint64_t& prevEnd,
212                                       int& prevHeap)
213 {
214     std::string name;
215     int32_t heapIndex[2] = {VMHEAP_UNKNOWN, VMHEAP_NULL};
216     bool swappable = false;
217     uint64_t swapablePss = 0;
218 
219     if (MatchTail(mempic.name, " (deleted)")) {
220         name = mempic.name.substr(0, mempic.name.size() - strlen(" (deleted)"));
221     } else {
222         name = mempic.name;
223     }
224     uint32_t namesz = name.size();
225     if (!GetVmaIndex(name, namesz, heapIndex, swappable)) {
226         if (namesz > 0) {
227             heapIndex[0] = VMHEAP_UNKNOWN_MAP;
228         } else if (mempic.startAddr == prevEnd && prevHeap == VMHEAP_SO) {
229             // bss section of a shared library
230             heapIndex[0] = VMHEAP_SO;
231         }
232     }
233     prevEnd = mempic.endAddr;
234     prevHeap = heapIndex[0];
235     swapablePss = GetSwapablepssValue(memusage, swappable);
236     SetVmemAreasData(heapIndex[0], swapablePss, memusage);
237     if ((heapIndex[1] != VMHEAP_NULL) && (heapIndex[1] != VMHEAP_NEEDFIX)) {
238         SetVmemAreasData(heapIndex[1], swapablePss, memusage);
239     }
240 }
241 
ReviseStatsData()242 void SmapsStats::ReviseStatsData()
243 {
244     // Summary data to VMHEAP_UNKNOWN
245     for (int i = VMHEAP_NUM_CORE_HEAP; i < VMHEAP_NUM_EXCLUSIVE_HEAP; i++) {
246         stats_[VMHEAP_UNKNOWN] += stats_[i];
247     }
248 }
249 
SetMapAddrInfo(std::string & line,MapPiecesInfo & head)250 bool SmapsStats::SetMapAddrInfo(std::string& line, MapPiecesInfo& head)
251 {
252     const char* pStr = line.c_str();
253     char* end = nullptr;
254     head.startAddr = strtoull(pStr, &end, HEX_BASE);
255     CHECK_TRUE(!(end == pStr || *end != '-'), false, "SetMapAddrInfo fail");
256     pStr = end + 1;
257     head.endAddr = strtoull(pStr, &end, HEX_BASE);
258     CHECK_TRUE(end != pStr, false, "end == pStr");
259     return true;
260 }
261 
ParseMapHead(std::string & line,MapPiecesInfo & head,SmapsHeadInfo & smapsHeadInfo)262 bool SmapsStats::ParseMapHead(std::string& line, MapPiecesInfo& head, SmapsHeadInfo& smapsHeadInfo)
263 {
264     std::string newline = line;
265     for (int i = 0; i < FIFTH_FIELD; i++) {
266         std::string word = newline;
267         size_t wordsz = word.find(" ");
268         CHECK_TRUE(wordsz != std::string::npos, false, "wordsz == std::string::npos");
269         word = newline.substr(0, wordsz);
270         if (i == 0) {
271             size_t pos = word.find("-");
272             if (pos != std::string::npos) {
273                 smapsHeadInfo.startAddrStr = word.substr(0, pos);
274                 smapsHeadInfo.endAddrStr = word.substr(pos + 1, word.size());
275                 head.startAddr = strtoull(smapsHeadInfo.startAddrStr.c_str(), nullptr, HEX_BASE);
276                 head.endAddr = strtoull(smapsHeadInfo.endAddrStr.c_str(), nullptr, HEX_BASE);
277             }
278         } else if (i == 1) {
279             smapsHeadInfo.permission = word.substr(0, word.size() - 1);
280         } else if (i == 4) { // 4: iNode index
281             smapsHeadInfo.iNode = strtoll(word.substr(0, word.size()).c_str(), nullptr, DEC_BASE);
282         }
283         size_t newlineops = newline.find_first_not_of(" ", wordsz);
284         newline = newline.substr(newlineops);
285     }
286     head.name = newline.substr(0, newline.size() - 1);
287     smapsHeadInfo.path = head.name;
288     return true;
289 }
290 
GetMemUsageField(std::string & line,MemUsageInfo & memusage)291 bool SmapsStats::GetMemUsageField(std::string& line, MemUsageInfo& memusage)
292 {
293     char field[64];
294     int len = 0;
295     const char* pLine = line.c_str();
296 
297     int ret = sscanf_s(pLine, "%63s %n", field, sizeof(field), &len);
298     if (ret == 1 && *field && field[strlen(field) - 1] == ':') {
299         const char* c = pLine + len;
300         std::string strfield(field);
301         switch (field[0]) {
302             case 'P':
303                 if (MatchHead(strfield, "Pss:")) {
304                     memusage.pss = strtoull(c, nullptr, DEC_BASE);
305                 } else if (MatchHead(strfield, "Private_Clean:")) {
306                     uint64_t prcl = strtoull(c, nullptr, DEC_BASE);
307                     memusage.privateClean = prcl;
308                     memusage.uss += prcl;
309                 } else if (MatchHead(strfield, "Private_Dirty:")) {
310                     uint64_t prdi = strtoull(c, nullptr, DEC_BASE);
311                     memusage.privateDirty = prdi;
312                     memusage.uss += prdi;
313                 }
314                 break;
315             case 'S':
316                 if (MatchHead(strfield, "Size:")) {
317                     memusage.vss = strtoull(c, nullptr, DEC_BASE);
318                 } else if (MatchHead(strfield, "Shared_Clean:")) {
319                     memusage.sharedClean = strtoull(c, nullptr, DEC_BASE);
320                 } else if (MatchHead(strfield, "Shared_Dirty:")) {
321                     memusage.sharedDirty = strtoull(c, nullptr, DEC_BASE);
322                 } else if (MatchHead(strfield, "Swap:")) {
323                     memusage.swap = strtoull(c, nullptr, DEC_BASE);
324                 } else if (MatchHead(strfield, "SwapPss:")) {
325                     memusage.swapPss = strtoull(c, nullptr, DEC_BASE);
326                 }
327                 break;
328             case 'R':
329                 if (MatchHead(strfield, "Rss:")) {
330                     memusage.rss = strtoull(c, nullptr, DEC_BASE);
331                 }
332                 break;
333             case 'V':
334                 if (MatchHead(strfield, "VmFlags:")) {
335                     lastline_ = true;
336                 }
337                 break;
338             default:
339                 break;
340         }
341         return true;
342     }
343 
344     return false;
345 }
346 
GetSwapablepssValue(const MemUsageInfo & memusage,bool swappable)347 uint64_t SmapsStats::GetSwapablepssValue(const MemUsageInfo& memusage, bool swappable)
348 {
349     if (!swappable || (memusage.pss == 0)) {
350         return 0;
351     }
352 
353     if ((memusage.sharedClean == 0) && (memusage.sharedDirty == 0)) {
354         return memusage.privateClean;
355     }
356     int proportion = (memusage.pss - memusage.uss) / (memusage.sharedClean + memusage.sharedDirty);
357 
358     return (proportion * memusage.sharedClean) + memusage.privateClean;
359 }
360 
SetVmemAreasData(int index,uint64_t swapablePss,const MemUsageInfo & usage)361 void SmapsStats::SetVmemAreasData(int index, uint64_t swapablePss, const MemUsageInfo& usage)
362 {
363     StatsInfo oobj(swapablePss, usage);
364     stats_[index] += oobj;
365 }
366 
HeapIndexFix(std::string name,const char * key,int32_t heapIndex[2])367 void SmapsStats::HeapIndexFix(std::string name, const char* key, int32_t heapIndex[2])
368 {
369     if (!strncmp(key, ".vdex", sizeof(".vdex"))) {
370         if ((strstr(name.c_str(), "@boot") != nullptr) || (strstr(name.c_str(), "/boot") != nullptr) ||
371             (strstr(name.c_str(), "/apex") != nullptr)) {
372             heapIndex[0] = VMHEAP_SENSITIVE_JVBIN;
373             heapIndex[1] = VMHEAP_SENSITIVE_JVBIN_BOOT_VDEX;
374         } else {
375             heapIndex[0] = VMHEAP_SENSITIVE_JVBIN;
376             heapIndex[1] = VMHEAP_SENSITIVE_JVBIN_APP_VDEX;
377         }
378     } else if (!strncmp(key, ".hrt", sizeof(".hrt")) || !strncmp(key, ".hrt]", sizeof(".hrt]"))) {
379         if ((strstr(name.c_str(), "@boot") != nullptr) || (strstr(name.c_str(), "/boot") != nullptr) ||
380             (strstr(name.c_str(), "/apex") != nullptr)) {
381             heapIndex[0] = VMHEAP_HRT;
382             heapIndex[1] = VMHEAP_HRT_BOOT;
383         } else {
384             heapIndex[0] = VMHEAP_HRT;
385             heapIndex[1] = VMHEAP_HRT_APP;
386         }
387     }
388 }
389 
GetProcessJavaHeap()390 int SmapsStats::GetProcessJavaHeap()
391 {
392     return stats_[VMHEAP_SENSITIVE_VM].privateDirty_ + GetPrivate(VMHEAP_HRT);
393 }
394 
GetProcessNativeHeap()395 int SmapsStats::GetProcessNativeHeap()
396 {
397     return stats_[VMHEAP_NATIVE].privateDirty_;
398 }
399 
GetProcessCode()400 int SmapsStats::GetProcessCode()
401 {
402     return GetPrivate(VMHEAP_SO) + GetPrivate(VMHEAP_JAR) + GetPrivate(VMHEAP_TTF) +
403            GetPrivate(VMHEAP_SENSITIVE_JVBIN) + GetPrivate(VMHEAP_OAT) +
404            GetPrivate(VMHEAP_SENSITIVE_VM_OTHER_ZYGOTE_CODE_CACHE) +
405            GetPrivate(VMHEAP_SENSITIVE_VM_OTHER_APP_CODE_CACHE);
406 }
407 
GetProcessStack()408 int SmapsStats::GetProcessStack()
409 {
410     return stats_[VMHEAP_STACK].privateDirty_;
411 }
412 
GetProcessGraphics()413 int SmapsStats::GetProcessGraphics()
414 {
415     return GetPrivate(VMHEAP_GL_DEV) + GetPrivate(VMHEAP_GRAPHICS) + GetPrivate(VMHEAP_GL);
416 }
417 
GetProcessPrivateOther()418 int SmapsStats::GetProcessPrivateOther()
419 {
420     return GetTotalPrivateClean() + GetTotalPrivateDirty() - GetProcessJavaHeap() - GetProcessNativeHeap() -
421            GetProcessCode() - GetProcessStack() - GetProcessGraphics();
422 }
423 
GetProcessSystem()424 int SmapsStats::GetProcessSystem()
425 {
426     return GetTotalPss() - GetTotalPrivateClean() - GetTotalPrivateDirty();
427 }
428 
GetTotalPrivateClean()429 int SmapsStats::GetTotalPrivateClean()
430 {
431     return stats_[VMHEAP_UNKNOWN].privateClean_ + stats_[VMHEAP_NATIVE].privateClean_ +
432            stats_[VMHEAP_SENSITIVE_VM].privateClean_;
433 }
434 
GetTotalPrivateDirty()435 int SmapsStats::GetTotalPrivateDirty()
436 {
437     return stats_[VMHEAP_UNKNOWN].privateDirty_ + stats_[VMHEAP_NATIVE].privateDirty_ +
438            stats_[VMHEAP_SENSITIVE_VM].privateDirty_;
439 }
440 
GetPrivate(int type)441 int SmapsStats::GetPrivate(int type)
442 {
443     return stats_[type].privateDirty_ + stats_[type].privateClean_;
444 }
445 
GetTotalPss()446 int SmapsStats::GetTotalPss()
447 {
448     return stats_[VMHEAP_UNKNOWN].pss_ + stats_[VMHEAP_NATIVE].pss_ + stats_[VMHEAP_SENSITIVE_VM].pss_
449             + GetTotalSwappedOutPss();
450 }
451 
GetTotalSwappedOutPss()452 int SmapsStats::GetTotalSwappedOutPss()
453 {
454     return stats_[VMHEAP_UNKNOWN].swappedOutPss_ + stats_[VMHEAP_NATIVE].swappedOutPss_ +
455            stats_[VMHEAP_SENSITIVE_VM].swappedOutPss_;
456 }
457