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 #define LOG_TAG "DumpHelper"
16 #include "dump_helper.h"
17 
18 #include <securec.h>
19 
20 #include "log_print.h"
21 #include "rdb_types.h"
22 #include "runtime_config.h"
23 #include "string_ex.h"
24 
25 namespace OHOS::DistributedData {
DumpHelper()26 DumpHelper::DumpHelper()
27 {
28     Handler handlerErrorInfo =
29         std::bind(&DumpHelper::DumpErrorInfo, this, std::placeholders::_1, std::placeholders::_2);
30     Handler handlerHelpInfo = std::bind(&DumpHelper::DumpHelpInfo, this, std::placeholders::_1, std::placeholders::_2);
31     Handler handlerAllInfo = std::bind(&DumpHelper::DumpAllInfo, this, std::placeholders::_1, std::placeholders::_2);
32     RegisterErrorInfo();
33     DumpManager::GetInstance().AddHandler("ERROR_INFO", uintptr_t(this), handlerErrorInfo);
34     RegisterHelpInfo();
35     DumpManager::GetInstance().AddHandler("HELP_INFO", uintptr_t(this), handlerHelpInfo);
36     RegisterAllInfo();
37     DumpManager::GetInstance().AddHandler("ALL_INFO", uintptr_t(this), handlerAllInfo);
38 }
39 
GetInstance()40 DumpHelper& DumpHelper::GetInstance()
41 {
42     static DumpHelper instance;
43     return instance;
44 }
45 
Dump(int fd,const std::vector<std::string> & args)46 bool DumpHelper::Dump(int fd, const std::vector<std::string> &args)
47 {
48     std::vector<std::shared_ptr<CommandNode>> commands;
49     ParseCommand(args, commands);
50     GetCommandNodes(fd, commands);
51     if (commands.empty()) {
52         return true;
53     }
54     std::shared_ptr<CommandNode> tmpCommandNode;
55     for (auto const& command : commands) {
56         std::map<std::string, std::vector<std::string>> params;
57         tmpCommandNode = command;
58         while (tmpCommandNode != nullptr) {
59             params.emplace(tmpCommandNode->dumpName, tmpCommandNode->param);
60             if (tmpCommandNode->nextNode == nullptr) {
61                 for (auto const& handler : tmpCommandNode->handlers) {
62                     handler(fd, params);
63                 }
64             }
65             tmpCommandNode = tmpCommandNode->nextNode;
66         }
67     }
68     return true;
69 }
70 
ParseCommand(const std::vector<std::string> & args,std::vector<std::shared_ptr<CommandNode>> & commands)71 void DumpHelper::ParseCommand(const std::vector<std::string> &args, std::vector<std::shared_ptr<CommandNode>> &commands)
72 {
73     std::shared_ptr<CommandNode> command;
74     std::vector<std::string> tmpDumpNames;
75     if (args.size() == 0) {
76         command = std::make_shared<CommandNode>(GetCommand("ALL_INFO"));
77         commands.emplace_back(command);
78         return;
79     }
80     for (size_t i = 0; i < args.size(); i++) {
81         std::vector<std::string> param;
82         command = std::make_shared<CommandNode>(GetCommand(args[i]));
83         if (!command->IsVoid()) {
84             auto it = find(tmpDumpNames.begin(), tmpDumpNames.end(), command->dumpName);
85             if (it != tmpDumpNames.end()) {
86                 ZLOGE("The same command is not allowed");
87                 return;
88             }
89             commands.emplace_back(command);
90             tmpDumpNames.emplace_back(command->dumpName);
91         } else {
92             if (commands.empty()) {
93                 ZLOGE("Invalid Format");
94                 return;
95             }
96             commands.back()->param.emplace_back(args[i]);
97             if (commands.back()->param.size() < commands.back()->minParamsNum ||
98                 commands.back()->param.size() > commands.back()->maxParamsNum) {
99                 ZLOGE("The number of param criteria is not legal");
100                 return;
101             }
102         }
103     }
104     if (commands.size() == 0) {
105         ZLOGE("No command is entered or the command is invalid");
106         return;
107     }
108 }
109 
GetCommand(const std::string & name)110 DumpHelper::CommandNode DumpHelper::GetCommand(const std::string &name)
111 {
112     auto config = DumpManager::GetInstance().GetConfig(name);
113     CommandNode command;
114     if (config.IsVoid()) {
115         return command;
116     }
117     command.dumpName = config.dumpName;
118     command.parentNode = config.parentNode;
119     command.childNode = config.childNode;
120     command.minParamsNum = config.minParamsNum;
121     command.maxParamsNum = config.maxParamsNum;
122     command.handlers = DumpManager::GetInstance().GetHandler(name);
123     command.nextNode = nullptr;
124     return command;
125 }
126 
GetCommandNodes(int fd,std::vector<std::shared_ptr<CommandNode>> & commands)127 void DumpHelper::GetCommandNodes(int fd, std::vector<std::shared_ptr<CommandNode>> &commands)
128 {
129     for (uint32_t i = 1; i < commands.size();) {
130         bool isAdded = false;
131         if (commands[i]->parentNode.empty()) {
132             for (uint32_t j = 0; j < i; j++) {
133                 AddHeadNode(commands[i], commands[j], isAdded);
134             }
135         } else {
136             for (uint32_t j = 0; j < i; j++) {
137                 AddNode(commands[i], commands[j], isAdded);
138             }
139         }
140         if (!isAdded) {
141             i++;
142         } else {
143             commands.erase(commands.begin() + i);
144         }
145     }
146 }
147 
AddHeadNode(std::shared_ptr<CommandNode> & command,std::shared_ptr<CommandNode> & realHeadNode,bool & isAdded)148 void DumpHelper::AddHeadNode(std::shared_ptr<CommandNode> &command, std::shared_ptr<CommandNode> &realHeadNode,
149     bool &isAdded)
150 {
151     auto tmpCommandNode = command;
152     while (!(tmpCommandNode->childNode.empty())) {
153         if (tmpCommandNode->childNode == realHeadNode->dumpName) {
154             command->nextNode = realHeadNode;
155             realHeadNode = command;
156             isAdded = true;
157         }
158         tmpCommandNode = std::make_shared<CommandNode>(GetCommand(tmpCommandNode->childNode));
159     }
160 }
161 
AddNode(std::shared_ptr<CommandNode> & command,std::shared_ptr<CommandNode> & realHeadNode,bool & isAdded)162 void DumpHelper::AddNode(std::shared_ptr<CommandNode> &command, std::shared_ptr<CommandNode> &realHeadNode,
163     bool &isAdded)
164 {
165     auto tmpCommandNode = command;
166     while (!(tmpCommandNode->parentNode.empty())) {
167         auto tmpRealHeadNode = realHeadNode;
168         while (tmpRealHeadNode != nullptr) {
169             if (tmpCommandNode->parentNode == tmpRealHeadNode->dumpName) {
170                 command->nextNode = realHeadNode->nextNode;
171                 realHeadNode->nextNode = command;
172                 isAdded = true;
173             }
174             tmpRealHeadNode = tmpRealHeadNode->nextNode;
175         }
176         tmpCommandNode = std::make_shared<CommandNode>(GetCommand(tmpCommandNode->parentNode));
177     }
178 }
179 
RegisterErrorInfo()180 void DumpHelper::RegisterErrorInfo()
181 {
182     DumpManager::Config errorInfoConfig;
183     errorInfoConfig.fullCmd = "--error-info";
184     errorInfoConfig.abbrCmd = "-e";
185     errorInfoConfig.dumpName = "ERROR_INFO";
186     errorInfoConfig.dumpCaption = { "| Display the recent error messages" };
187     DumpManager::GetInstance().AddConfig(errorInfoConfig.dumpName, errorInfoConfig);
188 }
189 
DumpErrorInfo(int fd,std::map<std::string,std::vector<std::string>> & params)190 void DumpHelper::DumpErrorInfo(int fd, std::map<std::string, std::vector<std::string>> &params)
191 {
192     std::string info;
193     for (const auto &it : errorInfo_) {
194         std::string errorCode = std::to_string(it.errorCode);
195         errorCode.resize(FORMAT_BLANK_SIZE, FORMAT_BLANK_SPACE);
196         info.append(it.errorTime).append(errorCode).append(it.errorInfo).append("\n");
197     }
198     dprintf(fd,
199         "-------------------------------------RecentError------------------------------------\nDate                   "
200         "         ErrorCode                       ErrorMessage\n%s\n",
201         info.c_str());
202 }
203 
FormatHelpInfo(const std::string & cmd,const std::string & cmdAbbr,const std::string & paraExt,const std::string & info,uint32_t & formatMaxSize)204 std::string DumpHelper::FormatHelpInfo(const std::string &cmd, const std::string &cmdAbbr, const std::string &paraExt,
205     const std::string &info, uint32_t &formatMaxSize)
206 {
207     std::string formatInfo;
208     formatInfo.append(" ").append(cmdAbbr).append(paraExt).append(", ").append(cmd).append(paraExt);
209     formatInfo.resize(formatMaxSize + FORMAT_FILL_SIZE, FORMAT_BLANK_SPACE);
210     formatInfo.append(info);
211     return formatInfo;
212 }
213 
GetFormatMaxSize()214 uint32_t DumpHelper::GetFormatMaxSize()
215 {
216     uint32_t formatMaxSize = 0;
217     auto dumpFactory = DumpManager::GetInstance().LoadConfig();
218     dumpFactory.ForEach([&formatMaxSize](const auto &key, auto &value) {
219         if (key == "ALL_INFO") {
220             return false;
221         }
222         std::string helpInfo;
223         helpInfo.append(" ").append(value.fullCmd).append(", ").append(value.abbrCmd);
224         formatMaxSize = helpInfo.size() > formatMaxSize ? helpInfo.size() : formatMaxSize;
225         std::string helpInfoAdd;
226         if (value.countPrintf == PRINTF_COUNT_2) {
227             helpInfoAdd.append(" ")
228                 .append(value.fullCmd)
229                 .append(", ")
230                 .append(value.abbrCmd)
231                 .append(value.infoName)
232                 .append(value.infoName);
233             formatMaxSize = helpInfoAdd.size() > formatMaxSize ? helpInfoAdd.size() : formatMaxSize;
234         }
235         return false;
236     });
237     return formatMaxSize;
238 }
239 
RegisterHelpInfo()240 void DumpHelper::RegisterHelpInfo()
241 {
242     DumpManager::Config helpInfoConfig;
243     helpInfoConfig.fullCmd = "--help-info";
244     helpInfoConfig.abbrCmd = "-h";
245     helpInfoConfig.dumpName = "HELP_INFO";
246     helpInfoConfig.dumpCaption = { "| Display this help message" };
247     DumpManager::GetInstance().AddConfig(helpInfoConfig.dumpName, helpInfoConfig);
248 }
249 
DumpHelpInfo(int fd,std::map<std::string,std::vector<std::string>> & params)250 void DumpHelper::DumpHelpInfo(int fd, std::map<std::string, std::vector<std::string>> &params)
251 {
252     std::string info;
253     uint32_t formatMaxSize = GetFormatMaxSize();
254     auto dumpFactory = DumpManager::GetInstance().LoadConfig();
255     dumpFactory.ForEach([this, &info, &formatMaxSize](const auto &key, auto &value) {
256         if (key == "ALL_INFO") {
257             return false;
258         }
259         info.append(FormatHelpInfo(value.fullCmd, value.abbrCmd, "", value.dumpCaption[0], formatMaxSize)).append("\n");
260         if (value.countPrintf == PRINTF_COUNT_2) {
261             info.append(
262                 FormatHelpInfo(value.fullCmd, value.abbrCmd, value.infoName, value.dumpCaption[1], formatMaxSize))
263                 .append("\n");
264         }
265         return false;
266     });
267     dprintf(fd,
268         "Usage: hidumper -s 1301 -a <option(s)>\nwhere possible options include:\n%s\nWhen -u/-u <UserId>, -b/-b "
269         "<BundleName> or -s/-s <StoreID> is simultaneously selected,\nwe display the lowest level statistics where -u "
270         "> -b > -s\nand the statistics is filterd by the upper level options\n",
271         info.c_str());
272 }
273 
RegisterAllInfo()274 void DumpHelper::RegisterAllInfo()
275 {
276     DumpManager::Config allInfoConfig;
277     allInfoConfig.fullCmd = "--all-info";
278     allInfoConfig.abbrCmd = "-a";
279     allInfoConfig.dumpName = "ALL_INFO";
280     DumpManager::GetInstance().AddConfig(allInfoConfig.dumpName, allInfoConfig);
281 }
282 
DumpAllInfo(int fd,std::map<std::string,std::vector<std::string>> & params)283 void DumpHelper::DumpAllInfo(int fd, std::map<std::string, std::vector<std::string>> &params)
284 {
285     auto dumpFactory = DumpManager::GetInstance().LoadConfig();
286     dumpFactory.ForEach([&fd, &params](const auto &key, auto &value) {
287         if (key == "ALL_INFO" || key == "HELP_INFO") {
288             return false;
289         }
290         std::vector<Handler> handlers = DumpManager::GetInstance().GetHandler(key);
291         if (!(handlers.empty())) {
292             for (auto handlerEach : handlers) {
293                 handlerEach(fd, params);
294             }
295         }
296         return false;
297     });
298 }
299 
AddErrorInfo(int32_t errorCode,const std::string & errorInfo)300 void DumpHelper::AddErrorInfo(int32_t errorCode, const std::string &errorInfo)
301 {
302     ErrorInfo error;
303     error.errorCode = errorCode;
304     error.errorInfo = errorInfo;
305     auto now = std::chrono::system_clock::now();
306     int64_t millSeconds = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count() -
307                            (std::chrono::duration_cast<std::chrono::seconds>(now.time_since_epoch()).count() * 1000);
308     time_t tt = std::chrono::system_clock::to_time_t(now);
309     auto ptm = localtime(&tt);
310     if (ptm != nullptr) {
311         char date[FORMAT_BLANK_SIZE] = { 0 };
312         auto flag = sprintf_s(date, sizeof(date), "%04d-%02d-%02d  %02d:%02d:%02d %03d",
313             (int)ptm->tm_year + DUMP_SYSTEM_START_YEAR, (int)ptm->tm_mon + 1, (int)ptm->tm_mday, (int)ptm->tm_hour,
314             (int)ptm->tm_min, (int)ptm->tm_sec, (int)millSeconds);
315         if (flag < 0) {
316             ZLOGE("get date failed");
317             return;
318         }
319         std::string errorTime = date;
320         errorTime.resize(FORMAT_BLANK_SIZE, FORMAT_BLANK_SPACE);
321         error.errorTime = errorTime;
322     }
323     std::lock_guard<std::mutex> lock(hidumperMutex_);
324     if (errorInfo_.size() + 1 > MAX_RECORED_ERROR) {
325         errorInfo_.pop_front();
326         errorInfo_.push_back(error);
327     } else {
328         errorInfo_.push_back(error);
329     }
330 }
331 } // namespace OHOS::DistributedData
332