• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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, &currentReadBytes) != 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