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