1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <android-base/stringprintf.h>
18 
19 #include "meminfo_private.h"
20 
21 namespace android {
22 namespace meminfo {
23 
ExtractAndroidHeapStats(int pid,AndroidHeapStats * stats,bool * foundSwapPss)24 bool ExtractAndroidHeapStats(int pid, AndroidHeapStats* stats, bool* foundSwapPss) {
25     std::string smaps_path = base::StringPrintf("/proc/%d/smaps", pid);
26     return ExtractAndroidHeapStatsFromFile(smaps_path, stats, foundSwapPss);
27 }
28 
ExtractAndroidHeapStatsFromFile(const std::string & smaps_path,AndroidHeapStats * stats,bool * foundSwapPss)29 bool ExtractAndroidHeapStatsFromFile(const std::string& smaps_path, AndroidHeapStats* stats,
30                                      bool* foundSwapPss) {
31     *foundSwapPss = false;
32     uint64_t prev_end = 0;
33     int prev_heap = HEAP_UNKNOWN;
34 
35     auto vma_scan = [&](const Vma& vma) {
36         int which_heap = HEAP_UNKNOWN;
37         int sub_heap = HEAP_UNKNOWN;
38         bool is_swappable = false;
39         std::string name;
40         if (vma.name.ends_with(" (deleted)")) {
41             name = vma.name.substr(0, vma.name.size() - strlen(" (deleted)"));
42         } else {
43             name = vma.name;
44         }
45 
46         uint32_t namesz = name.size();
47         if (name.starts_with("[heap]")) {
48             which_heap = HEAP_NATIVE;
49         } else if (name.starts_with("[anon:libc_malloc]")) {
50             which_heap = HEAP_NATIVE;
51         } else if (name.starts_with("[anon:scudo:")) {
52             which_heap = HEAP_NATIVE;
53         } else if (name.starts_with("[anon:GWP-ASan")) {
54             which_heap = HEAP_NATIVE;
55         } else if (name.starts_with("[stack")) {
56             which_heap = HEAP_STACK;
57         } else if (name.starts_with("[anon:stack_and_tls:")) {
58             which_heap = HEAP_STACK;
59         } else if (name.ends_with(".so")) {
60             which_heap = HEAP_SO;
61             is_swappable = true;
62         } else if (name.ends_with(".jar")) {
63             which_heap = HEAP_JAR;
64             is_swappable = true;
65         } else if (name.ends_with(".apk")) {
66             which_heap = HEAP_APK;
67             is_swappable = true;
68         } else if (name.ends_with(".ttf")) {
69             which_heap = HEAP_TTF;
70             is_swappable = true;
71         } else if ((name.ends_with(".odex")) ||
72                    (namesz > 4 && strstr(name.c_str(), ".dex") != nullptr)) {
73             which_heap = HEAP_DEX;
74             sub_heap = HEAP_DEX_APP_DEX;
75             is_swappable = true;
76         } else if (name.ends_with(".vdex")) {
77             which_heap = HEAP_DEX;
78             // Handle system@framework@boot and system/framework/boot|apex
79             if ((strstr(name.c_str(), "@boot") != nullptr) ||
80                 (strstr(name.c_str(), "/boot") != nullptr) ||
81                 (strstr(name.c_str(), "/apex") != nullptr)) {
82                 sub_heap = HEAP_DEX_BOOT_VDEX;
83             } else {
84                 sub_heap = HEAP_DEX_APP_VDEX;
85             }
86             is_swappable = true;
87         } else if (name.ends_with(".oat")) {
88             which_heap = HEAP_OAT;
89             is_swappable = true;
90         } else if (name.ends_with(".art") || name.ends_with(".art]")) {
91             which_heap = HEAP_ART;
92             // Handle system@framework@boot* and system/framework/boot|apex*
93             if ((strstr(name.c_str(), "@boot") != nullptr) ||
94                 (strstr(name.c_str(), "/boot") != nullptr) ||
95                 (strstr(name.c_str(), "/apex") != nullptr)) {
96                 sub_heap = HEAP_ART_BOOT;
97             } else {
98                 sub_heap = HEAP_ART_APP;
99             }
100             is_swappable = true;
101         } else if (name.find("kgsl-3d0") != std::string::npos) {
102             which_heap = HEAP_GL_DEV;
103         } else if (name.starts_with("/dev/")) {
104             which_heap = HEAP_UNKNOWN_DEV;
105             if (name.starts_with("/dev/ashmem/CursorWindow")) {
106                 which_heap = HEAP_CURSOR;
107             } else if (name.starts_with("/dev/ashmem/jit-zygote-cache")) {
108                 which_heap = HEAP_DALVIK_OTHER;
109                 sub_heap = HEAP_DALVIK_OTHER_ZYGOTE_CODE_CACHE;
110             } else if (name.starts_with("/dev/ashmem")) {
111                 which_heap = HEAP_ASHMEM;
112             }
113         } else if (name.starts_with("/memfd:jit-cache")) {
114             which_heap = HEAP_DALVIK_OTHER;
115             sub_heap = HEAP_DALVIK_OTHER_APP_CODE_CACHE;
116         } else if (name.starts_with("/memfd:jit-zygote-cache")) {
117             which_heap = HEAP_DALVIK_OTHER;
118             sub_heap = HEAP_DALVIK_OTHER_ZYGOTE_CODE_CACHE;
119         } else if (name.starts_with("[anon:")) {
120             which_heap = HEAP_UNKNOWN;
121             if (name.starts_with("[anon:dalvik-")) {
122                 which_heap = HEAP_DALVIK_OTHER;
123                 if (name.starts_with("[anon:dalvik-LinearAlloc")) {
124                     sub_heap = HEAP_DALVIK_OTHER_LINEARALLOC;
125                 } else if (name.starts_with("[anon:dalvik-alloc space") ||
126                            name.starts_with("[anon:dalvik-main space")) {
127                     // This is the regular Dalvik heap.
128                     which_heap = HEAP_DALVIK;
129                     sub_heap = HEAP_DALVIK_NORMAL;
130                 } else if (name.starts_with("[anon:dalvik-large object space") ||
131                            name.starts_with("[anon:dalvik-free list large object space")) {
132                     which_heap = HEAP_DALVIK;
133                     sub_heap = HEAP_DALVIK_LARGE;
134                 } else if (name.starts_with("[anon:dalvik-non moving space")) {
135                     which_heap = HEAP_DALVIK;
136                     sub_heap = HEAP_DALVIK_NON_MOVING;
137                 } else if (name.starts_with("[anon:dalvik-zygote space")) {
138                     which_heap = HEAP_DALVIK;
139                     sub_heap = HEAP_DALVIK_ZYGOTE;
140                 } else if (name.starts_with("[anon:dalvik-indirect ref")) {
141                     sub_heap = HEAP_DALVIK_OTHER_INDIRECT_REFERENCE_TABLE;
142                 } else if (name.starts_with("[anon:dalvik-jit-code-cache") ||
143                            name.starts_with("[anon:dalvik-data-code-cache")) {
144                     sub_heap = HEAP_DALVIK_OTHER_APP_CODE_CACHE;
145                 } else if (name.starts_with("[anon:dalvik-CompilerMetadata")) {
146                     sub_heap = HEAP_DALVIK_OTHER_COMPILER_METADATA;
147                 } else {
148                     sub_heap = HEAP_DALVIK_OTHER_ACCOUNTING;  // Default to accounting.
149                 }
150             }
151         } else if (namesz > 0) {
152             which_heap = HEAP_UNKNOWN_MAP;
153         } else if (vma.start == prev_end && prev_heap == HEAP_SO) {
154             // bss section of a shared library
155             which_heap = HEAP_SO;
156         }
157 
158         prev_end = vma.end;
159         prev_heap = which_heap;
160 
161         const MemUsage& usage = vma.usage;
162         if (usage.swap_pss > 0 && !*foundSwapPss) {
163             *foundSwapPss = true;
164         }
165 
166         uint64_t swapable_pss = 0;
167         if (is_swappable && (usage.pss > 0)) {
168             float sharing_proportion = 0.0;
169             if ((usage.shared_clean > 0) || (usage.shared_dirty > 0)) {
170                 sharing_proportion =
171                         (usage.pss - usage.uss) / (usage.shared_clean + usage.shared_dirty);
172             }
173             swapable_pss = (sharing_proportion * usage.shared_clean) + usage.private_clean;
174         }
175 
176         stats[which_heap].pss += usage.pss;
177         stats[which_heap].swappablePss += swapable_pss;
178         stats[which_heap].rss += usage.rss;
179         stats[which_heap].privateDirty += usage.private_dirty;
180         stats[which_heap].sharedDirty += usage.shared_dirty;
181         stats[which_heap].privateClean += usage.private_clean;
182         stats[which_heap].sharedClean += usage.shared_clean;
183         stats[which_heap].swappedOut += usage.swap;
184         stats[which_heap].swappedOutPss += usage.swap_pss;
185         if (which_heap == HEAP_DALVIK || which_heap == HEAP_DALVIK_OTHER ||
186             which_heap == HEAP_DEX || which_heap == HEAP_ART) {
187             stats[sub_heap].pss += usage.pss;
188             stats[sub_heap].swappablePss += swapable_pss;
189             stats[sub_heap].rss += usage.rss;
190             stats[sub_heap].privateDirty += usage.private_dirty;
191             stats[sub_heap].sharedDirty += usage.shared_dirty;
192             stats[sub_heap].privateClean += usage.private_clean;
193             stats[sub_heap].sharedClean += usage.shared_clean;
194             stats[sub_heap].swappedOut += usage.swap;
195             stats[sub_heap].swappedOutPss += usage.swap_pss;
196         }
197         return true;
198     };
199 
200     return ForEachVmaFromFile(smaps_path, vma_scan);
201 }
202 }  // namespace meminfo
203 }  // namespace android
204