1 /*
2 * Copyright (c) 2024-2025 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
16 #include "memory_utils.h"
17
18 #include <fstream>
19 #include <list>
20 #include <map>
21 #include <securec.h>
22
23 #include "common_utils.h"
24 #include "graphic_memory_collector.h"
25 #include "hiview_logger.h"
26 #include "memory.h"
27 #include "parameter_ex.h"
28 #include "string_util.h"
29
30 namespace OHOS {
31 namespace HiviewDFX {
32 namespace {
33 DEFINE_LOG_TAG("MemoryUtils");
34 const std::list<std::pair<std::string, MemoryItemType>> PREFIX_LIST = {
35 {"[heap]", MemoryItemType::MEMORY_ITEM_TYPE_HEAP},
36 {"[anon:native_heap:brk", MemoryItemType::MEMORY_ITEM_TYPE_ANON_NATIVE_HEAP_BRK},
37 {"[anon:native_heap:jemalloc]", MemoryItemType::MEMORY_ITEM_TYPE_ANON_NATIVE_HEAP_JEMALLOC},
38 {"[anon:native_heap:jemalloc meta]", MemoryItemType::MEMORY_ITEM_TYPE_ANON_NATIVE_HEAP_JEMALLOC_META},
39 {"[anon:native_heap:jemalloc tsd]", MemoryItemType::MEMORY_ITEM_TYPE_ANON_NATIVE_HEAP_JEMALLOC_TSD},
40 {"[anon:native_heap:meta]", MemoryItemType::MEMORY_ITEM_TYPE_ANON_NATIVE_HEAP_META},
41 {"[anon:native_heap:mmap]", MemoryItemType::MEMORY_ITEM_TYPE_ANON_NATIVE_HEAP_MMAP},
42 {"[anon:native_heap:", MemoryItemType::MEMORY_ITEM_TYPE_ANON_NATIVE_HEAP_OTHER}, // do not adjust forward
43 {"[anon:libc_malloc]", MemoryItemType::MEMORY_ITEM_TYPE_MALLOC},
44 {"[stack]", MemoryItemType::MEMORY_ITEM_TYPE_STACK},
45 {"[anon:stack", MemoryItemType::MEMORY_ITEM_TYPE_ANON_STACK},
46 {"[anon:signal_stack", MemoryItemType::MEMORY_ITEM_TYPE_ANON_SIGNAL_STACK},
47 {"[anon:ArkTS Code", MemoryItemType::MEMORY_ITEM_TYPE_ANON_ARKTS_CODE},
48 {"[anon:ArkTS Heap", MemoryItemType::MEMORY_ITEM_TYPE_ANON_ARKTS_HEAP},
49 {"[anon:guard", MemoryItemType::MEMORY_ITEM_TYPE_ANON_GUARD},
50 {"/dev/__parameters__/", MemoryItemType::MEMORY_ITEM_ENTITY_DEV_PARAMETER},
51 {"/dev/", MemoryItemType::MEMORY_ITEM_ENTITY_DEV_OTHER},
52 {"/dmabuf", MemoryItemType::MEMORY_ITEM_ENTITY_DMABUF},
53 {"/", MemoryItemType::MEMORY_ITEM_ENTITY_OTHER}, // do not adjust forward, because it has sub-path like /dev
54 {"anon_inode", MemoryItemType::MEMORY_ITEM_TYPE_ANON_INODE},
55 {"[anon:v8", MemoryItemType::MEMORY_ITEM_TYPE_ANON_V8},
56 {"[anon:", MemoryItemType::MEMORY_ITEM_TYPE_ANONYMOUS_OTHER},
57 {"[contiguous", MemoryItemType::MEMORY_ITEM_TYPE_CONTIGUOUS},
58 {"[copage", MemoryItemType::MEMORY_ITEM_TYPE_COPAGE},
59 {"[file", MemoryItemType::MEMORY_ITEM_TYPE_FILE},
60 {"[guard", MemoryItemType::MEMORY_ITEM_TYPE_GUARD},
61 {"[io", MemoryItemType::MEMORY_ITEM_TYPE_IO},
62 {"[kshare", MemoryItemType::MEMORY_ITEM_TYPE_KSHARE},
63 {"[prehistoric", MemoryItemType::MEMORY_ITEM_TYPE_PREHISTORIC},
64 {"[reserve", MemoryItemType::MEMORY_ITEM_TYPE_RESERVE},
65 {"[shmm", MemoryItemType::MEMORY_ITEM_TYPE_SHMM},
66 {"[unknown", MemoryItemType::MEMORY_ITEM_TYPE_UNKNOWN},
67 {"[vnodes", MemoryItemType::MEMORY_ITEM_TYPE_VNODES},
68 };
69
70 const std::list<std::pair<std::string, MemoryItemType>> HIGH_PRIORITY_SUFFIX_LIST = {
71 {".db-shm", MemoryItemType::MEMORY_ITEM_ENTITY_DB_SHM},
72 {".so", MemoryItemType::MEMORY_ITEM_ENTITY_SO},
73 {".so.1", MemoryItemType::MEMORY_ITEM_ENTITY_SO1},
74 {".ttf", MemoryItemType::MEMORY_ITEM_ENTITY_TTF},
75 {".db", MemoryItemType::MEMORY_ITEM_ENTITY_DB},
76 };
77
78 const std::list<std::pair<std::string, MemoryItemType>> HIGH_PRIORITY_PREFIX_LIST = {
79 {"/data/storage", MemoryItemType::MEMORY_ITEM_ENTITY_DATA_STORAGE}
80 };
81
82 const std::list<std::pair<std::string, MemoryItemType>> SUFFIX_LIST = {
83 {".hap", MemoryItemType::MEMORY_ITEM_ENTITY_HAP},
84 {".hsp", MemoryItemType::MEMORY_ITEM_ENTITY_HSP},
85 {".so.1.bss]", MemoryItemType::MEMORY_ITEM_TYPE_ANON_BSS},
86 };
87
88 const std::map<MemoryItemType, MemoryClass> TYPE_TO_CLASS_MAP = {
89 {MemoryItemType::MEMORY_ITEM_ENTITY_DB, MemoryClass::MEMORY_CLASS_DB},
90 {MemoryItemType::MEMORY_ITEM_ENTITY_DB_SHM, MemoryClass::MEMORY_CLASS_DB},
91 {MemoryItemType::MEMORY_ITEM_ENTITY_HAP, MemoryClass::MEMORY_CLASS_OTHER},
92 {MemoryItemType::MEMORY_ITEM_ENTITY_HSP, MemoryClass::MEMORY_CLASS_OTHER},
93 {MemoryItemType::MEMORY_ITEM_ENTITY_SO, MemoryClass::MEMORY_CLASS_SO},
94 {MemoryItemType::MEMORY_ITEM_ENTITY_SO1, MemoryClass::MEMORY_CLASS_SO},
95 {MemoryItemType::MEMORY_ITEM_ENTITY_TTF, MemoryClass::MEMORY_CLASS_TTF},
96 {MemoryItemType::MEMORY_ITEM_ENTITY_DEV_PARAMETER, MemoryClass::MEMORY_CLASS_DEV},
97 {MemoryItemType::MEMORY_ITEM_ENTITY_DEV_OTHER, MemoryClass::MEMORY_CLASS_DEV},
98 {MemoryItemType::MEMORY_ITEM_ENTITY_DATA_STORAGE, MemoryClass::MEMORY_CLASS_HAP},
99 {MemoryItemType::MEMORY_ITEM_ENTITY_DMABUF, MemoryClass::MEMORY_CLASS_DMABUF},
100 {MemoryItemType::MEMORY_ITEM_ENTITY_OTHER, MemoryClass::MEMORY_CLASS_OTHER},
101 {MemoryItemType::MEMORY_ITEM_TYPE_ANON_INODE, MemoryClass::MEMORY_CLASS_OTHER},
102 {MemoryItemType::MEMORY_ITEM_TYPE_ANON_ARKTS_CODE, MemoryClass::MEMORY_CLASS_OTHER},
103 {MemoryItemType::MEMORY_ITEM_TYPE_ANON_ARKTS_HEAP, MemoryClass::MEMORY_CLASS_ARK_TS_HEAP},
104 {MemoryItemType::MEMORY_ITEM_TYPE_ANON_GUARD, MemoryClass::MEMORY_CLASS_GUARD},
105 {MemoryItemType::MEMORY_ITEM_TYPE_ANON_BSS, MemoryClass::MEMORY_CLASS_OTHER},
106 {MemoryItemType::MEMORY_ITEM_TYPE_ANON_NATIVE_HEAP_BRK, MemoryClass::MEMORY_CLASS_NATIVE_HEAP},
107 {MemoryItemType::MEMORY_ITEM_TYPE_ANON_NATIVE_HEAP_JEMALLOC, MemoryClass::MEMORY_CLASS_NATIVE_HEAP},
108 {MemoryItemType::MEMORY_ITEM_TYPE_ANON_NATIVE_HEAP_JEMALLOC_META, MemoryClass::MEMORY_CLASS_NATIVE_HEAP},
109 {MemoryItemType::MEMORY_ITEM_TYPE_ANON_NATIVE_HEAP_JEMALLOC_TSD, MemoryClass::MEMORY_CLASS_NATIVE_HEAP},
110 {MemoryItemType::MEMORY_ITEM_TYPE_ANON_NATIVE_HEAP_META, MemoryClass::MEMORY_CLASS_NATIVE_HEAP},
111 {MemoryItemType::MEMORY_ITEM_TYPE_ANON_NATIVE_HEAP_MMAP, MemoryClass::MEMORY_CLASS_NATIVE_HEAP},
112 {MemoryItemType::MEMORY_ITEM_TYPE_ANON_NATIVE_HEAP_OTHER, MemoryClass::MEMORY_CLASS_NATIVE_HEAP},
113 {MemoryItemType::MEMORY_ITEM_TYPE_ANON_SIGNAL_STACK, MemoryClass::MEMORY_CLASS_STACK},
114 {MemoryItemType::MEMORY_ITEM_TYPE_ANON_STACK, MemoryClass::MEMORY_CLASS_STACK},
115 {MemoryItemType::MEMORY_ITEM_TYPE_ANON_V8, MemoryClass::MEMORY_CLASS_OTHER},
116 {MemoryItemType::MEMORY_ITEM_TYPE_ANONYMOUS_OTHER, MemoryClass::MEMORY_CLASS_OTHER},
117 {MemoryItemType::MEMORY_ITEM_TYPE_CONTIGUOUS, MemoryClass::MEMORY_CLASS_OTHER},
118 {MemoryItemType::MEMORY_ITEM_TYPE_COPAGE, MemoryClass::MEMORY_CLASS_OTHER},
119 {MemoryItemType::MEMORY_ITEM_TYPE_FILE, MemoryClass::MEMORY_CLASS_OTHER},
120 {MemoryItemType::MEMORY_ITEM_TYPE_GUARD, MemoryClass::MEMORY_CLASS_OTHER},
121 {MemoryItemType::MEMORY_ITEM_TYPE_HEAP, MemoryClass::MEMORY_CLASS_NATIVE_HEAP},
122 {MemoryItemType::MEMORY_ITEM_TYPE_IO, MemoryClass::MEMORY_CLASS_OTHER},
123 {MemoryItemType::MEMORY_ITEM_TYPE_KSHARE, MemoryClass::MEMORY_CLASS_OTHER},
124 {MemoryItemType::MEMORY_ITEM_TYPE_MALLOC, MemoryClass::MEMORY_CLASS_NATIVE_HEAP},
125 {MemoryItemType::MEMORY_ITEM_TYPE_PREHISTORIC, MemoryClass::MEMORY_CLASS_OTHER},
126 {MemoryItemType::MEMORY_ITEM_TYPE_RESERVE, MemoryClass::MEMORY_CLASS_OTHER},
127 {MemoryItemType::MEMORY_ITEM_TYPE_SHMM, MemoryClass::MEMORY_CLASS_OTHER},
128 {MemoryItemType::MEMORY_ITEM_TYPE_STACK, MemoryClass::MEMORY_CLASS_STACK},
129 {MemoryItemType::MEMORY_ITEM_TYPE_UNKNOWN, MemoryClass::MEMORY_CLASS_OTHER},
130 {MemoryItemType::MEMORY_ITEM_TYPE_VNODES, MemoryClass::MEMORY_CLASS_OTHER},
131 {MemoryItemType::MEMORY_ITEM_TYPE_OTHER, MemoryClass::MEMORY_CLASS_OTHER},
132 {MemoryItemType::MEMORY_ITEM_TYPE_GRAPH_GL, MemoryClass::MEMORY_CLASS_GRAPH},
133 {MemoryItemType::MEMORY_ITEM_TYPE_GRAPH_GRAPHICS, MemoryClass::MEMORY_CLASS_GRAPH},
134 };
135
MapNameToType(const std::string & name)136 MemoryItemType MapNameToType(const std::string& name)
137 {
138 // do not adjust match order, strictly follow the order from high priority to suffix to prefix
139 for (const auto& suffix : HIGH_PRIORITY_SUFFIX_LIST) {
140 if (StringUtil::EndWith(name, suffix.first)) {
141 return suffix.second;
142 }
143 }
144 for (const auto& prefix : HIGH_PRIORITY_PREFIX_LIST) {
145 if (StringUtil::StartWith(name, prefix.first)) {
146 return prefix.second;
147 }
148 }
149 for (const auto& suffix : SUFFIX_LIST) {
150 if (StringUtil::EndWith(name, suffix.first)) {
151 return suffix.second;
152 }
153 }
154 for (const auto& prefix : PREFIX_LIST) {
155 if (StringUtil::StartWith(name, prefix.first)) {
156 return prefix.second;
157 }
158 }
159 return MemoryItemType::MEMORY_ITEM_TYPE_OTHER;
160 }
161
MapTypeToClass(MemoryItemType type)162 MemoryClass MapTypeToClass(MemoryItemType type)
163 {
164 if (TYPE_TO_CLASS_MAP.find(type) == TYPE_TO_CLASS_MAP.end()) {
165 return MemoryClass::MEMORY_CLASS_OTHER;
166 }
167 return TYPE_TO_CLASS_MAP.at(type);
168 }
169
IsNameLine(const std::string & line,std::string & name,uint64_t & iNode)170 bool IsNameLine(const std::string& line, std::string& name, uint64_t& iNode)
171 {
172 int currentReadBytes = 0;
173 if (sscanf_s(line.c_str(), "%*llx-%*llx %*s %*llx %*s %llu%n", &iNode, ¤tReadBytes) != 1) {
174 return false;
175 }
176
177 uint32_t len = static_cast<uint32_t>(currentReadBytes);
178 while (len < line.size() && line[len] == ' ') {
179 len++;
180 }
181 if (len < line.size()) {
182 name = line.substr(len, line.size());
183 }
184 return true;
185 }
186
GetTypeAndValue(const std::string & line,std::string & type,uint64_t & value)187 bool GetTypeAndValue(const std::string& line, std::string& type, uint64_t& value)
188 {
189 std::size_t pos = line.find(':');
190 if (pos != std::string::npos) {
191 type = line.substr(0, pos);
192 auto valueStr = line.substr(pos + 1);
193 value = strtoull(valueStr.c_str(), nullptr, 10); // 10 : convert string to decimal
194 return true;
195 }
196 return false;
197 }
198
UpdateMemoryItem(MemoryItem & item,const std::string & type,uint64_t value)199 void UpdateMemoryItem(MemoryItem& item, const std::string& type, uint64_t value)
200 {
201 static std::map<std::string, std::function<void(MemoryItem&, uint64_t)>> updateHandlers = {
202 {"Rss", [] (MemoryItem& item, uint64_t value) {item.rss = value;} },
203 {"Pss", [] (MemoryItem& item, uint64_t value) {item.pss = value;} },
204 {"Shared_Clean", [] (MemoryItem& item, uint64_t value) {item.sharedClean = value;} },
205 {"Shared_Dirty", [] (MemoryItem& item, uint64_t value) {item.sharedDirty = value;} },
206 {"Private_Clean", [] (MemoryItem& item, uint64_t value) {item.privateClean = value;} },
207 {"Private_Dirty", [] (MemoryItem& item, uint64_t value) {item.privateDirty = value;} },
208 {"SwapPss", [] (MemoryItem& item, uint64_t value) {item.swapPss = value;} },
209 {"Swap", [] (MemoryItem& item, uint64_t value) {item.swap = value;} },
210 {"Size", [] (MemoryItem& item, uint64_t value) {item.size = value;} },
211 };
212 if (updateHandlers.find(type)!= updateHandlers.end()) {
213 updateHandlers[type](item, value);
214 }
215 }
216
AddItemToVec(MemoryItem & item,std::vector<MemoryItem> & items)217 void AddItemToVec(MemoryItem& item, std::vector<MemoryItem>& items)
218 {
219 item.allPss = item.pss + item.swapPss;
220 item.allSwap = item.swap + item.swapPss;
221 item.type = MapNameToType(item.name);
222 items.push_back(item);
223 }
224
225 /*
226 * parse every single line in smaps, different memory segment info is parsed to different memory item
227 * line : a single line content in smaps
228 * item : memory item to parse
229 * items : memory items collection
230 * isNameParsed : flag indicating whether memory segment name be parsed
231 */
ParseMemoryItem(const std::string & line,MemoryItem & item,std::vector<MemoryItem> & items,bool & isNameParsed)232 void ParseMemoryItem(const std::string& line, MemoryItem& item, std::vector<MemoryItem>& items, bool& isNameParsed)
233 {
234 // end with B means this line contains value like Pss: 0 kB
235 if (StringUtil::EndWith(line, "B")) {
236 std::string type;
237 uint64_t value;
238 if (GetTypeAndValue(line, type, value)) {
239 UpdateMemoryItem(item, type, value);
240 }
241 return;
242 }
243 std::string name;
244 uint64_t iNode = 0;
245 /* judge whether is name line of one memory segment like below
246 * f6988000-f6998000 rw-p 00000000 00:00 0 [anon:native_heap:brk]
247 */
248 if (!IsNameLine(line, name, iNode)) {
249 return;
250 }
251 if (isNameParsed) {
252 // when parse a new memory segment, the item of previous memory segment add to vector
253 AddItemToVec(item, items);
254 item.ResetValue();
255 }
256 if (!Parameter::IsUserMode()) {
257 std::vector<std::string> elements;
258 StringUtil::SplitStr(line, " ", elements);
259 if (elements.size() > 1) {
260 std::string address = elements[0];
261 auto pos = address.find("-");
262 item.startAddr = address.substr(0, pos);
263 item.endAddr = address.substr(pos + 1);
264 item.permission = elements[1];
265 }
266 }
267 isNameParsed = true;
268 item.name = name;
269 item.iNode = iNode;
270 }
271
GetGraphMemoryItems(int32_t pid,std::vector<MemoryItem> & items,GraphicMemOption option)272 void GetGraphMemoryItems(int32_t pid, std::vector<MemoryItem>& items, GraphicMemOption option)
273 {
274 auto collector = UCollectUtil::GraphicMemoryCollector::Create();
275 bool isLowLatencyMode = (option == GraphicMemOption::LOW_LATENCY);
276 auto glResult = collector->GetGraphicUsage(pid, GraphicType::GL, isLowLatencyMode);
277 auto glValue = (glResult.retCode == UCollect::UcError::SUCCESS) ? static_cast<uint64_t>(glResult.data) : 0;
278 auto graphResult = collector->GetGraphicUsage(pid, GraphicType::GRAPH, isLowLatencyMode);
279 auto graphValue =
280 (graphResult.retCode == UCollect::UcError::SUCCESS) ? static_cast<uint64_t>(graphResult.data) : 0;
281 MemoryItem glMemoryItem = {
282 .type = MemoryItemType::MEMORY_ITEM_TYPE_GRAPH_GL,
283 .pss = glValue,
284 .allPss = glValue};
285 MemoryItem graphMemoryItem = {
286 .type = MemoryItemType::MEMORY_ITEM_TYPE_GRAPH_GRAPHICS,
287 .pss = graphValue,
288 .allPss = graphValue};
289 items.push_back(glMemoryItem);
290 items.push_back(graphMemoryItem);
291 }
292
293 /*
294 * assemble memory items to memory details, one memory detail contains multiple memory items
295 * and aggregate value of memory items of same class
296 * processMemoryDetail : detailed memory information for the specified process
297 * items : memory items to assemble
298 */
AssembleMemoryDetails(ProcessMemoryDetail & processMemoryDetail,const std::vector<MemoryItem> & items)299 void AssembleMemoryDetails(ProcessMemoryDetail& processMemoryDetail, const std::vector<MemoryItem>& items)
300 {
301 // classify memory items according to the memory item type
302 std::map<MemoryClass, std::vector<MemoryItem>> rawDetails;
303 for (const auto& item : items) {
304 auto memoryClass = MapTypeToClass(item.type);
305 if (rawDetails.find(memoryClass) == rawDetails.end()) {
306 rawDetails[memoryClass] = {item};
307 } else {
308 rawDetails[memoryClass].emplace_back(item);
309 }
310 }
311
312 std::vector<MemoryDetail> details;
313 for (auto& pair : rawDetails) {
314 MemoryDetail detail;
315 detail.memoryClass = pair.first;
316 // aggregate value of memory items to one memory detail
317 for (const auto& item : pair.second) {
318 detail.totalRss += item.rss;
319 detail.totalPss += item.pss;
320 detail.totalSwapPss += item.swapPss;
321 detail.totalSwap += item.swap;
322 detail.totalAllPss += item.allPss;
323 detail.totalAllSwap += item.allSwap;
324 detail.totalSharedDirty += item.sharedDirty;
325 detail.totalPrivateDirty += item.privateDirty;
326 detail.totalSharedClean += item.sharedClean;
327 detail.totalPrivateClean += item.privateClean;
328 }
329 detail.items.swap(pair.second);
330 details.emplace_back(detail);
331 // aggregate value of memory details to the final return value
332 processMemoryDetail.totalRss += detail.totalRss;
333 processMemoryDetail.totalPss += detail.totalPss;
334 processMemoryDetail.totalSwapPss += detail.totalSwapPss;
335 processMemoryDetail.totalSwap += detail.totalSwap;
336 processMemoryDetail.totalAllPss += detail.totalAllPss;
337 processMemoryDetail.totalAllSwap += detail.totalAllSwap;
338 processMemoryDetail.totalSharedDirty += detail.totalSharedDirty;
339 processMemoryDetail.totalPrivateDirty += detail.totalPrivateDirty;
340 processMemoryDetail.totalSharedClean += detail.totalSharedClean;
341 processMemoryDetail.totalPrivateClean += detail.totalPrivateClean;
342 }
343 processMemoryDetail.details.swap(details);
344 }
345 }
346
ParseSmaps(int32_t pid,const std::string & smapsPath,ProcessMemoryDetail & processMemoryDetail,GraphicMemOption option)347 bool ParseSmaps(int32_t pid, const std::string& smapsPath, ProcessMemoryDetail& processMemoryDetail,
348 GraphicMemOption option)
349 {
350 std::ifstream file(smapsPath.c_str());
351 if (!file.is_open()) {
352 HIVIEW_LOGW("open file fail, pid: %{public}d.", pid);
353 return false;
354 }
355 std::string line;
356 MemoryItem item;
357 std::vector<MemoryItem> items;
358 bool isNameParsed = false;
359 while (getline(file, line)) {
360 while (isspace(line.back())) {
361 line.pop_back();
362 }
363 ParseMemoryItem(line, item, items, isNameParsed);
364 }
365 if (isNameParsed) {
366 AddItemToVec(item, items);
367 }
368 if (option != GraphicMemOption::NONE) {
369 GetGraphMemoryItems(pid, items, option);
370 }
371 processMemoryDetail.pid = pid;
372 processMemoryDetail.name = CommonUtils::GetProcFullNameByPid(pid);
373 AssembleMemoryDetails(processMemoryDetail, items);
374 return true;
375 }
376 } // HiViewDFX
377 } // OHOS
378