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