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