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