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
GetMemFromSmapsrollup(std::string & line,MemUsageInfo & memusage)270 bool SmapsStats::GetMemFromSmapsrollup(std::string& line, MemUsageInfo& memusage)
271 {
272 char field[64];
273 int len = 0;
274 const char* pLine = line.c_str();
275 int ret = sscanf_s(pLine, "%63s %n", field, sizeof(field), &len);
276 size_t dataIndex = strlen(field) > 1 ? strlen(field) - 1 : 0;
277 if (ret == 1 && *field && field[dataIndex] == ':') {
278 const char* c = pLine + len;
279 std::string strfield(field);
280 switch (field[0]) {
281 case 'P':
282 if (MatchHead(strfield, "Pss:")) {
283 memusage.pss = strtoull(c, nullptr, DEC_BASE);
284 } else if (MatchHead(strfield, "Private_Clean:")) {
285 uint64_t prcl = strtoull(c, nullptr, DEC_BASE);
286 memusage.privateClean = prcl;
287 memusage.uss += prcl;
288 } else if (MatchHead(strfield, "Private_Dirty:")) {
289 uint64_t prdi = strtoull(c, nullptr, DEC_BASE);
290 memusage.privateDirty = prdi;
291 memusage.uss += prdi;
292 }
293 break;
294 case 'S':
295 if (MatchHead(strfield, "Size:")) {
296 memusage.vss = strtoull(c, nullptr, DEC_BASE);
297 } else if (MatchHead(strfield, "Shared_Clean:")) {
298 memusage.sharedClean = strtoull(c, nullptr, DEC_BASE);
299 } else if (MatchHead(strfield, "Shared_Dirty:")) {
300 memusage.sharedDirty = strtoull(c, nullptr, DEC_BASE);
301 } else if (MatchHead(strfield, "Swap:")) {
302 memusage.swap = strtoull(c, nullptr, DEC_BASE);
303 } else if (MatchHead(strfield, "SwapPss:")) {
304 memusage.swapPss = strtoull(c, nullptr, DEC_BASE);
305 }
306 break;
307 case 'R':
308 if (MatchHead(strfield, "Rss:")) {
309 memusage.rss = strtoull(c, nullptr, DEC_BASE);
310 }
311 break;
312 default:
313 break;
314 }
315 return true;
316 }
317
318 return false;
319 }
320
GetSwapablepssValue(const MemUsageInfo & memusage,bool swappable)321 uint64_t SmapsStats::GetSwapablepssValue(const MemUsageInfo& memusage, bool swappable)
322 {
323 if (!swappable || (memusage.pss == 0)) {
324 return 0;
325 }
326
327 if ((memusage.sharedClean == 0) && (memusage.sharedDirty == 0)) {
328 return memusage.privateClean;
329 }
330 int proportion = (memusage.pss - memusage.uss) / (memusage.sharedClean + memusage.sharedDirty);
331
332 return (proportion * memusage.sharedClean) + memusage.privateClean;
333 }
334
SetVmemAreasData(int index,uint64_t swapablePss,const MemUsageInfo & usage)335 void SmapsStats::SetVmemAreasData(int index, uint64_t swapablePss, const MemUsageInfo& usage)
336 {
337 StatsInfo oobj(swapablePss, usage);
338 stats_[index] += oobj;
339 }
340
HeapIndexFix(std::string name,const char * key,int32_t heapIndex[2])341 void SmapsStats::HeapIndexFix(std::string name, const char* key, int32_t heapIndex[2])
342 {
343 if (!strncmp(key, ".vdex", sizeof(".vdex"))) {
344 if ((strstr(name.c_str(), "@boot") != nullptr) || (strstr(name.c_str(), "/boot") != nullptr) ||
345 (strstr(name.c_str(), "/apex") != nullptr)) {
346 heapIndex[0] = VMHEAP_SENSITIVE_JVBIN;
347 heapIndex[1] = VMHEAP_SENSITIVE_JVBIN_BOOT_VDEX;
348 } else {
349 heapIndex[0] = VMHEAP_SENSITIVE_JVBIN;
350 heapIndex[1] = VMHEAP_SENSITIVE_JVBIN_APP_VDEX;
351 }
352 } else if (!strncmp(key, ".hrt", sizeof(".hrt")) || !strncmp(key, ".hrt]", sizeof(".hrt]"))) {
353 if ((strstr(name.c_str(), "@boot") != nullptr) || (strstr(name.c_str(), "/boot") != nullptr) ||
354 (strstr(name.c_str(), "/apex") != nullptr)) {
355 heapIndex[0] = VMHEAP_HRT;
356 heapIndex[1] = VMHEAP_HRT_BOOT;
357 } else {
358 heapIndex[0] = VMHEAP_HRT;
359 heapIndex[1] = VMHEAP_HRT_APP;
360 }
361 }
362 }
363
GetProcessJavaHeap()364 int SmapsStats::GetProcessJavaHeap()
365 {
366 return stats_[VMHEAP_SENSITIVE_VM].privateDirty_ + GetPrivate(VMHEAP_HRT);
367 }
368
GetProcessNativeHeap()369 int SmapsStats::GetProcessNativeHeap()
370 {
371 return stats_[VMHEAP_NATIVE].privateDirty_;
372 }
373
GetProcessCode()374 int SmapsStats::GetProcessCode()
375 {
376 return GetPrivate(VMHEAP_SO) + GetPrivate(VMHEAP_JAR) + GetPrivate(VMHEAP_TTF) +
377 GetPrivate(VMHEAP_SENSITIVE_JVBIN) + GetPrivate(VMHEAP_OAT) +
378 GetPrivate(VMHEAP_SENSITIVE_VM_OTHER_ZYGOTE_CODE_CACHE) +
379 GetPrivate(VMHEAP_SENSITIVE_VM_OTHER_APP_CODE_CACHE);
380 }
381
GetProcessStack()382 int SmapsStats::GetProcessStack()
383 {
384 return stats_[VMHEAP_STACK].privateDirty_;
385 }
386
GetProcessGraphics()387 int SmapsStats::GetProcessGraphics()
388 {
389 return GetPrivate(VMHEAP_GL_DEV) + GetPrivate(VMHEAP_GRAPHICS) + GetPrivate(VMHEAP_GL);
390 }
391
GetProcessPrivateOther()392 int SmapsStats::GetProcessPrivateOther()
393 {
394 return GetTotalPrivateClean() + GetTotalPrivateDirty() - GetProcessJavaHeap() - GetProcessNativeHeap() -
395 GetProcessCode() - GetProcessStack() - GetProcessGraphics();
396 }
397
GetProcessSystem()398 int SmapsStats::GetProcessSystem()
399 {
400 return GetTotalPss() - GetTotalPrivateClean() - GetTotalPrivateDirty();
401 }
402
GetTotalPrivateClean()403 int SmapsStats::GetTotalPrivateClean()
404 {
405 return stats_[VMHEAP_UNKNOWN].privateClean_ + stats_[VMHEAP_NATIVE].privateClean_ +
406 stats_[VMHEAP_SENSITIVE_VM].privateClean_;
407 }
408
GetTotalPrivateDirty()409 int SmapsStats::GetTotalPrivateDirty()
410 {
411 return stats_[VMHEAP_UNKNOWN].privateDirty_ + stats_[VMHEAP_NATIVE].privateDirty_ +
412 stats_[VMHEAP_SENSITIVE_VM].privateDirty_;
413 }
414
GetPrivate(int type)415 int SmapsStats::GetPrivate(int type)
416 {
417 return stats_[type].privateDirty_ + stats_[type].privateClean_;
418 }
419
GetTotalPss()420 int SmapsStats::GetTotalPss()
421 {
422 return stats_[VMHEAP_UNKNOWN].pss_ + stats_[VMHEAP_NATIVE].pss_ + stats_[VMHEAP_SENSITIVE_VM].pss_
423 + GetTotalSwappedOutPss();
424 }
425
GetTotalSwappedOutPss()426 int SmapsStats::GetTotalSwappedOutPss()
427 {
428 return stats_[VMHEAP_UNKNOWN].swappedOutPss_ + stats_[VMHEAP_NATIVE].swappedOutPss_ +
429 stats_[VMHEAP_SENSITIVE_VM].swappedOutPss_;
430 }
431