1 /*
2  * Copyright (c) 2023 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 #ifndef OHOS_MEMORY_DUMP_COMMAND_DISPATCHER_H
16 #define OHOS_MEMORY_DUMP_COMMAND_DISPATCHER_H
17 
18 #include "memory_level_constants.h"
19 #include "memory_level_manager.h"
20 #ifdef USE_PURGEABLE_MEMORY
21 #include "purgeable_mem_manager.h"
22 #endif
23 
24 namespace OHOS {
25 namespace Memory {
26 constexpr unsigned int ONTRIM_LEVEL_PARAM_SIZE = 1;
27 constexpr unsigned int RECLAIM_TYPE_PARAM_SIZE = 1;
28 constexpr unsigned int RECLAIM_HEAP_ID_PARAM_SIZE = 2;
29 constexpr unsigned int RECLAIM_ASHM_ID_PARAM_SIZE = 2;
30 constexpr unsigned int RECLAIM_SUBSCRIBER_ID_PARAM_SIZE = 1;
31 constexpr unsigned int FIRST_INDEX = 0;
32 constexpr unsigned int SECOND_INDEX = 1;
33 constexpr unsigned int THIRD_INDEX = 2;
34 constexpr unsigned int APP_STATE_PARAM_SIZE = 3;
35 
36 #define CHECK_SIZE(container, len, fd, actionIfFailed) \
37     do {                                               \
38         if ((container).size() != (len)) {             \
39             dprintf(fd, "size error\n");               \
40             actionIfFailed;                            \
41         }                                              \
42     } while (0)
43 
HasCommand(const std::map<std::string,std::vector<std::string>> & keyValuesMapping,const std::string & command)44 inline bool HasCommand(const std::map<std::string, std::vector<std::string>> &keyValuesMapping,
45                        const std::string &command)
46 {
47     return keyValuesMapping.find(command) != keyValuesMapping.end();
48 }
49 
ShowHelpInfo(const int fd)50 void ShowHelpInfo(const int fd)
51 {
52     dprintf(fd, "Usage:\n");
53     dprintf(fd, "-h                          |help for memmgrservice dumper\n");
54     dprintf(fd, "-a                          |dump all info\n");
55     dprintf(fd, "-e                          |dump event observer\n");
56     dprintf(fd, "-r                          |dump reclaim info and adj\n");
57     dprintf(fd, "-c                          |dump config\n");
58     dprintf(fd, "-m                          |show malloc state\n");
59 #ifdef USE_PURGEABLE_MEMORY
60     dprintf(fd, "-s                          |show subscriber all the pid which can be reclaimed\n");
61     dprintf(fd, "-d {pid} {uid} {state}      |trigger appstate changed\n\n");
62     dprintf(fd, "-t                          trigger memory onTrim:\n"
63                 "-t 1 ---------------------- level_purgeable\n"
64                 "-t 2 ---------------------- level_moderate\n"
65                 "-t 3 ---------------------- level_low\n"
66                 "-t 4 ---------------------- level_critical\n\n");
67     dprintf(fd, "-f                          trigger purgeable memory Reclaim:\n"
68                 "-f 1 ---------------------- purg_heap all\n"
69                 "-f 2 ---------------------- purg_ashmem all\n"
70                 "-f 3 ---------------------- purg_subscriber all\n"
71                 "-f 4 ---------------------- purg all purgeable memory\n"
72                 "-f 1 -id {userId} {size} -- purg_heap by memCG and size(KB). if userId=0, reclaim system_lru\n"
73                 "-f 2 -id {ashmId} {time} -- purg_ashm by ashmId, which can get from /proc/purgeable_ashmem_trigger\n"
74                 "-f 3 -id {pid} ------------ purg_subscriber by pid. if pid=0, reclaim subscriber all\n\n");
75 #endif
76 }
77 
78 #ifdef USE_PURGEABLE_MEMORY
PrintOntrimError(const int fd)79 void PrintOntrimError(const int fd)
80 {
81     dprintf(fd, "\n error: unrecognized memory level, please input correct format as follows:\n"
82                 "-t 1 ---------------------- level_purgeable\n"
83                 "-t 2 ---------------------- level_moderate\n"
84                 "-t 3 ---------------------- level_low\n"
85                 "-t 4 ---------------------- level_critical\n");
86 }
87 
PrintReclaimError(const int fd)88 void PrintReclaimError(const int fd)
89 {
90     dprintf(fd, "\n error: trigger force reclaim failed, please input correct info as follows:\n"
91                 "-f 1 ---------------------- purg_heap all\n"
92                 "-f 2 ---------------------- purg_ashmem all\n"
93                 "-f 3 ---------------------- purg_subscriber all\n"
94                 "-f 4 ---------------------- purg all purgeable memory\n"
95                 "-f 1 -id {userId} {size} -- purg_heap by memCG and size(KB). if userId=0, reclaim system_lru\n"
96                 "-f 2 -id {ashmId} {time} -- purg_ashm by ashmId, which can get from /proc/purgeable_ashmem_trigger\n"
97                 "-f 3 -id {pid} ------------ purg_subscriber by pid. if pid=0, reclaim subscriber all\n");
98 }
99 
DispatchTriggerMemLevel(const int fd,std::map<std::string,std::vector<std::string>> & keyValuesMapping)100 void DispatchTriggerMemLevel(const int fd, std::map<std::string, std::vector<std::string>> &keyValuesMapping)
101 {
102     std::vector<std::string> values = keyValuesMapping["-t"];
103     CHECK_SIZE(values, ONTRIM_LEVEL_PARAM_SIZE, fd, return);
104 
105     int level;
106     try {
107         level = std::stoi(values[FIRST_INDEX]);
108     } catch (...) {
109         PrintOntrimError(fd);
110         return;
111     }
112 
113     SystemMemoryInfo info = {MemorySource::MANUAL_DUMP, SystemMemoryLevel::UNKNOWN};
114     switch (level) {
115         case MEMORY_LEVEL_PURGEABLE:
116             info.level = SystemMemoryLevel::MEMORY_LEVEL_PURGEABLE;
117             break;
118         case MEMORY_LEVEL_MODERATE:
119             info.level = SystemMemoryLevel::MEMORY_LEVEL_MODERATE;
120             break;
121         case MEMORY_LEVEL_LOW:
122             info.level = SystemMemoryLevel::MEMORY_LEVEL_LOW;
123             break;
124         case MEMORY_LEVEL_CRITICAL:
125             info.level = SystemMemoryLevel::MEMORY_LEVEL_CRITICAL;
126             break;
127         default:
128             PrintOntrimError(fd);
129             return;
130     }
131     MemoryLevelManager::GetInstance().TriggerMemoryLevelByDump(info);
132 }
133 
ParseForceReclaimType(const int fd,std::map<std::string,std::vector<std::string>> & keyValuesMapping,DumpReclaimInfo & dumpInfo)134 void ParseForceReclaimType(const int fd, std::map<std::string, std::vector<std::string>> &keyValuesMapping,
135                            DumpReclaimInfo &dumpInfo)
136 {
137     dumpInfo.reclaimType = PurgeableMemoryType::UNKNOWN;
138     dumpInfo.ifReclaimTypeAll = true;
139 
140     std::vector<std::string> values = keyValuesMapping["-f"];
141     CHECK_SIZE(values, RECLAIM_TYPE_PARAM_SIZE, fd, return);
142 
143     int type;
144     try {
145         type = std::stoi(values[FIRST_INDEX]);
146     } catch (...) {
147         return;
148     }
149     switch (type) {
150         case PURGEABLE_TYPE_HEAP:
151             dumpInfo.reclaimType = PurgeableMemoryType::PURGEABLE_HEAP;
152             break;
153         case PURGEABLE_TYPE_ASHMEM:
154             dumpInfo.reclaimType = PurgeableMemoryType::PURGEABLE_ASHMEM;
155             break;
156         case PURGEABLE_TYPE_SUBSCRIBER:
157             dumpInfo.reclaimType = PurgeableMemoryType::PURGEABLE_SUBSCRIBER;
158             break;
159         case PURGEABLE_TYPE_ALL:
160             dumpInfo.reclaimType = PurgeableMemoryType::PURGEABLE_ALL;
161             break;
162         default:
163             return;
164     }
165 }
166 
ParseForceReclaimId(const int fd,std::map<std::string,std::vector<std::string>> & keyValuesMapping,DumpReclaimInfo & dumpInfo)167 bool ParseForceReclaimId(const int fd, std::map<std::string, std::vector<std::string>> &keyValuesMapping,
168                          DumpReclaimInfo &dumpInfo)
169 {
170     dumpInfo.ifReclaimTypeAll = false;
171     std::vector<std::string> values = keyValuesMapping["-id"];
172     switch (dumpInfo.reclaimType) {
173         case PurgeableMemoryType::PURGEABLE_HEAP:
174             CHECK_SIZE(values, RECLAIM_HEAP_ID_PARAM_SIZE, fd, return false); // {userId} {size}
175             try {
176                 dumpInfo.memcgUserId = std::stoi(values[FIRST_INDEX]);
177                 dumpInfo.reclaimHeapSizeKB = std::stoi(values[SECOND_INDEX]);
178             } catch (...) {
179                 return false;
180             }
181             break;
182         case PurgeableMemoryType::PURGEABLE_ASHMEM:
183             CHECK_SIZE(values, RECLAIM_ASHM_ID_PARAM_SIZE, fd, return false); // {ashmId} {time}
184             try {
185                 dumpInfo.ashmId = std::stoul(values[FIRST_INDEX]);
186                 dumpInfo.ashmTime = std::stoul(values[SECOND_INDEX]);
187             } catch (...) {
188                 return false;
189             }
190             break;
191         case PurgeableMemoryType::PURGEABLE_SUBSCRIBER:
192             CHECK_SIZE(values, RECLAIM_SUBSCRIBER_ID_PARAM_SIZE, fd, return false); // {pid}
193             if (values[0] == std::string("0")) { // reclaim subscriber all when pid = 0
194                 dumpInfo.ifReclaimTypeAll = true;
195                 return true;
196             }
197             try {
198                 dumpInfo.subscriberPid = std::stoi(values[FIRST_INDEX]);
199             } catch (...) {
200                 return false;
201             }
202             break;
203         default:
204             return false;
205     }
206     return true;
207 }
208 
PurgeableMemoryDump(int fd,std::map<std::string,std::vector<std::string>> & keyValuesMapping)209 bool PurgeableMemoryDump(int fd, std::map<std::string, std::vector<std::string>> &keyValuesMapping)
210 {
211     if (HasCommand(keyValuesMapping, "-s")) {
212         PurgeableMemManager::GetInstance().DumpSubscribers(fd);
213         return true;
214     }
215     if (HasCommand(keyValuesMapping, "-t")) {
216         DispatchTriggerMemLevel(fd, keyValuesMapping);
217         return true;
218     }
219     if (HasCommand(keyValuesMapping, "-f")) {
220         DumpReclaimInfo dumpInfo;
221         ParseForceReclaimType(fd, keyValuesMapping, dumpInfo);
222         if (HasCommand(keyValuesMapping, "-id") && !ParseForceReclaimId(fd, keyValuesMapping, dumpInfo)) {
223             dumpInfo.reclaimType = PurgeableMemoryType::UNKNOWN;
224         }
225         if (PurgeableMemManager::GetInstance().ForceReclaimByDump(dumpInfo)) {
226             dprintf(fd, "trigger force reclaim success!\n");
227         } else {
228             PrintReclaimError(fd);
229         }
230         return true;
231     }
232     if (HasCommand(keyValuesMapping, "-d")) {
233         std::vector<std::string> appState = keyValuesMapping["-d"];
234         if (appState.size() < APP_STATE_PARAM_SIZE) {
235             dprintf(fd, "params number is less than %{publid}d!\n", APP_STATE_PARAM_SIZE);
236             return true;
237         }
238         int32_t pid = std::stoi(appState[FIRST_INDEX]);
239         int32_t uid = std::stoi(appState[SECOND_INDEX]);
240         int32_t state = std::stoi(appState[THIRD_INDEX]);
241         PurgeableMemManager::GetInstance().ChangeAppState(pid, uid, state);
242         return true;
243     }
244     return false;
245 }
246 #endif // USE_PURGEABLE_MEMORY
247 
DispatchDumpCommand(const int fd,std::map<std::string,std::vector<std::string>> & keyValuesMapping)248 void DispatchDumpCommand(const int fd, std::map<std::string, std::vector<std::string>> &keyValuesMapping)
249 {
250     if (keyValuesMapping.empty() || HasCommand(keyValuesMapping, "-h")) {
251         ShowHelpInfo(fd);
252         return;
253     }
254     if (HasCommand(keyValuesMapping, "-a")) {
255         MemMgrEventCenter::GetInstance().Dump(fd);
256         ReclaimPriorityManager::GetInstance().Dump(fd);
257         return;
258     }
259     if (HasCommand(keyValuesMapping, "-e")) {
260         MemMgrEventCenter::GetInstance().Dump(fd);
261         return;
262     }
263     if (HasCommand(keyValuesMapping, "-r")) {
264         ReclaimPriorityManager::GetInstance().Dump(fd);
265         return;
266     }
267     if (HasCommand(keyValuesMapping, "-c")) {
268         MemmgrConfigManager::GetInstance().Dump(fd);
269         return;
270     }
271 #ifdef USE_PURGEABLE_MEMORY
272     if (PurgeableMemoryDump(fd, keyValuesMapping)) {
273         return;
274     }
275 #endif
276 }
277 
278 } // namespace Memory
279 } // namespace OHOS
280 #endif // OHOS_MEMORY_DUMP_COMMAND_DISPATCHER_H