1 /*
2 * Copyright (c) 2021-2022 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 "subcommand.h"
17 #include <mutex>
18 #include "debug_logger.h"
19 #include "option.h"
20
21 using namespace std;
22 namespace OHOS {
23 namespace Developtools {
24 namespace HiPerf {
25 static std::map<std::string, std::unique_ptr<SubCommand>> g_SubCommandsMap;
26 std::mutex g_subCommandMutex;
27
28 // parse option first
OnSubCommandOptions(std::vector<std::string> args)29 bool SubCommand::OnSubCommandOptions(std::vector<std::string> args)
30 {
31 // parse common first
32 if (!Option::GetOptionValue(args, "--dumpoptions", dumpOptions_)) {
33 return false;
34 }
35 if (!Option::GetOptionValue(args, "--help", showHelp_)
36 || !Option::GetOptionValue(args, "-h", showHelp_)) {
37 return false;
38 }
39
40 if (showHelp_) {
41 if (!args.empty()) {
42 printf("unknown option '%s'\n", args.front().c_str());
43 return false;
44 }
45 if (OnPreSubCommand()) {
46 return false;
47 }
48 }
49
50 if (ParseOption(args)) {
51 if (dumpOptions_) {
52 DumpOptions();
53 }
54 HLOGD(" args left over: (%zu): %s", args.size(), VectorToString(args).c_str());
55 if (!args.empty()) {
56 printf("unknown option '%s'\n", args.front().c_str());
57 return false;
58 }
59 } else {
60 HLOGD("incorrect option(s)\n");
61 return false;
62 }
63 return true;
64 }
65
CheckRestartOption(std::string appPackage,bool targetSystemWide,bool restart,std::vector<pid_t> & selectPids)66 bool SubCommand::CheckRestartOption(std::string appPackage, bool targetSystemWide, bool restart,
67 std::vector<pid_t> &selectPids)
68 {
69 if (!restart) {
70 return true;
71 }
72 if (appPackage.empty()) {
73 printf("to detect the performance of application startup, --app option must be given\n");
74 return false;
75 }
76 if (targetSystemWide || !selectPids.empty()) {
77 printf("option --restart and -p/-a is conflict, please check usage\n");
78 return false;
79 }
80
81 if (!appPackage.empty()) {
82 printf("please restart %s for profiling within 30 seconds\n", appPackage.c_str());
83 pid_t oldAppPid = GetAppPackagePid(appPackage, -1, CHECK_FREQUENCY, 0);
84 pid_t newAppPid = GetAppPackagePid(appPackage, oldAppPid, CHECK_FREQUENCY, CHECK_TIMEOUT);
85 if (newAppPid < 0 || oldAppPid == newAppPid) {
86 printf("app %s was not restarted within 30 seconds\n", appPackage.c_str());
87 return false;
88 }
89 return true;
90 }
91 return false;
92 }
93
HandleSubCommandExclude(const std::vector<pid_t> & excludeTids,const std::vector<std::string> & excludeThreadNames,std::vector<pid_t> & selectTids)94 bool SubCommand::HandleSubCommandExclude(const std::vector<pid_t> &excludeTids, const std::vector<std::string>
95 &excludeThreadNames, std::vector<pid_t> &selectTids)
96 {
97 if (!excludeTids.empty() && !excludeThreadNames.empty()) {
98 printf("option --exclude-thread and --exclude-tid is conflict, please check usage\n");
99 return false;
100 }
101 if (excludeTids.empty() && excludeThreadNames.empty()) {
102 return true;
103 }
104 if (selectTids.empty()) {
105 printf("No thread is Monitored, while attempt to exclude some threads.\n");
106 return false;
107 }
108 if (!excludeTids.empty()) {
109 ExcludeTidsFromSelectTids(excludeTids, selectTids);
110 return true;
111 }
112 ExcludeThreadsFromSelectTids(excludeThreadNames, selectTids);
113 return true;
114 }
115
ExcludeTidsFromSelectTids(const std::vector<pid_t> & excludeTids,std::vector<pid_t> & selectTids)116 void SubCommand::ExcludeTidsFromSelectTids(const std::vector<pid_t> &excludeTids, std::vector<pid_t> &selectTids)
117 {
118 for (auto excludeTid : excludeTids) {
119 bool hasExclude = false;
120 auto pos = selectTids.begin();
121 while (pos != selectTids.end()) {
122 if (excludeTid == *pos) {
123 pos = selectTids.erase(pos);
124 hasExclude = true;
125 continue;
126 }
127 ++pos;
128 }
129 if (!hasExclude) {
130 printf("No thread id %d was found to exclude.\n", excludeTid);
131 }
132 }
133 }
134
ExcludeThreadsFromSelectTids(const std::vector<std::string> & excludeThreadNames,std::vector<pid_t> & selectTids)135 void SubCommand::ExcludeThreadsFromSelectTids(const std::vector<std::string> &excludeThreadNames,
136 std::vector<pid_t> &selectTids)
137 {
138 for (auto excludeName : excludeThreadNames) {
139 bool hasExclude = false;
140 auto pos = selectTids.begin();
141 while (pos != selectTids.end()) {
142 std::string threadName = virtualRuntime_.ReadThreadName(*pos, true);
143 if (excludeName == threadName) {
144 pos = selectTids.erase(pos);
145 hasExclude = true;
146 continue;
147 }
148 ++pos;
149 }
150 if (!hasExclude) {
151 printf("No thread named %s was found to exclude.\n", excludeName.c_str());
152 }
153 }
154 }
155
RegisterSubCommand(std::string cmdName,std::unique_ptr<SubCommand> subCommand)156 bool SubCommand::RegisterSubCommand(std::string cmdName, std::unique_ptr<SubCommand> subCommand)
157 {
158 HLOGV("%s", cmdName.c_str());
159 if (cmdName.empty()) {
160 HLOGE("unable to register empty subcommand!");
161 return false;
162 }
163 if (cmdName.front() == '-') {
164 HLOGE("unable use '-' at the begin of subcommand '%s'", cmdName.c_str());
165 return false;
166 }
167
168 if (g_SubCommandsMap.count(cmdName) == 0) {
169 std::lock_guard<std::mutex> lock(g_subCommandMutex);
170 g_SubCommandsMap.insert(std::make_pair(cmdName, std::move(subCommand)));
171 return true;
172 } else {
173 HLOGE("subcommand '%s' already registered!", cmdName.c_str());
174 return false;
175 }
176 }
177
ClearSubCommands()178 void SubCommand::ClearSubCommands()
179 {
180 std::lock_guard<std::mutex> lock(g_subCommandMutex);
181 g_SubCommandsMap.clear();
182 }
183
GetSubCommands()184 const std::map<std::string, std::unique_ptr<SubCommand>> &SubCommand::GetSubCommands()
185 {
186 return g_SubCommandsMap;
187 }
188
FindSubCommand(std::string cmdName)189 SubCommand *SubCommand::FindSubCommand(std::string cmdName)
190 {
191 HLOGV("%s", cmdName.c_str());
192 std::lock_guard<std::mutex> lock(g_subCommandMutex);
193 auto found = g_SubCommandsMap.find(cmdName);
194 if (found != g_SubCommandsMap.end()) {
195 // remove the subcmd itself
196 return found->second.get();
197 } else {
198 return nullptr;
199 }
200 }
201 } // namespace HiPerf
202 } // namespace Developtools
203 } // namespace OHOS
204