1 /*
2 * Copyright (C) 2021 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 #include "peer_binder_catcher.h"
16
17 #include <ctime>
18 #include <cstdio>
19 #include <cstdlib>
20 #include <list>
21 #include <securec.h>
22 #include <sys/wait.h>
23
24 #include "common_utils.h"
25 #include "file_util.h"
26 #include "freeze_json_util.h"
27 #include "hiview_logger.h"
28 #include "log_catcher_utils.h"
29 #include "open_stacktrace_catcher.h"
30 #include "parameter_ex.h"
31 #include "perf_collector.h"
32 #include "string_util.h"
33 namespace OHOS {
34 namespace HiviewDFX {
35 DEFINE_LOG_LABEL(0xD002D01, "EventLogger-PeerBinderCatcher");
36 #ifdef HAS_HIPERF
37 using namespace OHOS::HiviewDFX::UCollectUtil;
38 constexpr char EVENT_LOG_PATH[] = "/data/log/eventlog";
39 #endif
PeerBinderCatcher()40 PeerBinderCatcher::PeerBinderCatcher() : EventLogCatcher()
41 {
42 name_ = "PeerBinderCatcher";
43 }
44
Initialize(const std::string & perfCmd,int layer,int pid)45 bool PeerBinderCatcher::Initialize(const std::string& perfCmd, int layer, int pid)
46 {
47 pid_ = pid;
48 layer_ = layer;
49 perfCmd_ = perfCmd;
50 char buf[BUF_SIZE_512] = {0};
51 int ret = snprintf_s(buf, BUF_SIZE_512, BUF_SIZE_512 - 1,
52 "PeerBinderCatcher -- pid==%d layer_ == %d\n", pid_, layer_);
53 if (ret > 0) {
54 description_ = buf;
55 }
56 return true;
57 }
58
Init(std::shared_ptr<SysEvent> event,const std::string & filePath,std::set<int> & catchedPids)59 void PeerBinderCatcher::Init(std::shared_ptr<SysEvent> event, const std::string& filePath, std::set<int>& catchedPids)
60 {
61 event_ = event;
62 catchedPids_ = catchedPids;
63 if ((filePath != "") && FileUtil::FileExists(filePath)) {
64 binderPath_ = filePath;
65 }
66 }
67
Catch(int fd,int jsonFd)68 int PeerBinderCatcher::Catch(int fd, int jsonFd)
69 {
70 if (pid_ <= 0) {
71 return -1;
72 }
73
74 auto originSize = GetFdSize(fd);
75
76 if (!FileUtil::FileExists(binderPath_)) {
77 std::string content = binderPath_ + " : file isn't exists\r\n";
78 FileUtil::SaveStringToFd(fd, content);
79 return -1;
80 }
81
82 std::set<int> pids = GetBinderPeerPids(fd, jsonFd);
83 if (pids.empty()) {
84 std::string content = "PeerBinder pids is empty\r\n";
85 FileUtil::SaveStringToFd(fd, content);
86 }
87 #ifdef HAS_HIPERF
88 ForkToDumpHiperf(pids);
89 #endif
90 std::string pidStr = "";
91 for (auto pidTemp : pids) {
92 if (pidTemp == pid_ || IsAncoProc(pidTemp)) {
93 HIVIEW_LOGI("Stack of pid %{public}d is catched.", pidTemp);
94 continue;
95 }
96
97 if (catchedPids_.count(pidTemp) == 0) {
98 CatcherStacktrace(fd, pidTemp);
99 pidStr += "," + std::to_string(pidTemp);
100 }
101 CatcherFfrtStack(fd, pidTemp);
102 }
103
104 if (event_ != nullptr) {
105 event_->SetValue(LOGGER_EVENT_PEERBINDER, StringUtil::TrimStr(pidStr, ','));
106 }
107
108 logSize_ = GetFdSize(fd) - originSize;
109 return logSize_;
110 }
111
AddBinderJsonInfo(std::list<OutputBinderInfo> outputBinderInfoList,int jsonFd) const112 void PeerBinderCatcher::AddBinderJsonInfo(std::list<OutputBinderInfo> outputBinderInfoList, int jsonFd) const
113 {
114 if (jsonFd < 0) {
115 return;
116 }
117 std::map<int, std::string> processNameMap;
118 for (OutputBinderInfo outputBinderInfo : outputBinderInfoList) {
119 int pid = outputBinderInfo.pid;
120 if (processNameMap[pid] != "") {
121 continue;
122 }
123 std::string filePath = "/proc/" + std::to_string(pid) + "/cmdline";
124 std::string realPath;
125 if (!FileUtil::PathToRealPath(filePath, realPath)) {
126 continue;
127 }
128 std::ifstream cmdLineFile(realPath);
129 std::string processName;
130 if (cmdLineFile) {
131 std::getline(cmdLineFile, processName);
132 cmdLineFile.close();
133 processName = StringUtil::FormatCmdLine(processName);
134 processNameMap[pid] = processName;
135 } else {
136 HIVIEW_LOGE("Fail to open /proc/%{public}d/cmdline", pid);
137 }
138 }
139 std::list<std::string> infoList;
140 for (auto it = outputBinderInfoList.begin(); it != outputBinderInfoList.end(); it++) {
141 int pid = (*it).pid;
142 std::string info = (*it).info;
143 std::string lineStr = info + " " + std::to_string(pid)
144 + FreezeJsonUtil::WrapByParenthesis(processNameMap[pid]);
145 infoList.push_back(lineStr);
146 }
147 std::string binderInfoJsonStr = FreezeJsonUtil::GetStrByList(infoList);
148 HIVIEW_LOGI("Get FreezeJson PeerBinder jsonStr: %{public}s.", binderInfoJsonStr.c_str());
149 FreezeJsonUtil::WriteKeyValue(jsonFd, "peer_binder", binderInfoJsonStr);
150 }
151
BinderInfoParser(std::ifstream & fin,int fd,int jsonFd) const152 std::map<int, std::list<PeerBinderCatcher::BinderInfo>> PeerBinderCatcher::BinderInfoParser(
153 std::ifstream& fin, int fd, int jsonFd) const
154 {
155 std::map<int, std::list<BinderInfo>> manager;
156 FileUtil::SaveStringToFd(fd, "\nBinderCatcher --\n\n");
157 std::list<OutputBinderInfo> outputBinderInfoList;
158
159 BinderInfoParser(fin, fd, manager, outputBinderInfoList);
160 AddBinderJsonInfo(outputBinderInfoList, jsonFd);
161
162 FileUtil::SaveStringToFd(fd, "\n\nPeerBinder Stacktrace --\n\n");
163 HIVIEW_LOGI("manager size: %{public}zu", manager.size());
164 return manager;
165 }
166
GetFileToList(std::string line,std::vector<std::string> & strList) const167 void PeerBinderCatcher::GetFileToList(std::string line,
168 std::vector<std::string>& strList) const
169 {
170 std::istringstream lineStream(line);
171 std::string tmpstr;
172 while (lineStream >> tmpstr) {
173 strList.push_back(tmpstr);
174 }
175 HIVIEW_LOGD("strList size: %{public}zu", strList.size());
176 }
177
BinderInfoParser(std::ifstream & fin,int fd,std::map<int,std::list<PeerBinderCatcher::BinderInfo>> & manager,std::list<PeerBinderCatcher::OutputBinderInfo> & outputBinderInfoList) const178 void PeerBinderCatcher::BinderInfoParser(std::ifstream& fin, int fd,
179 std::map<int, std::list<PeerBinderCatcher::BinderInfo>>& manager,
180 std::list<PeerBinderCatcher::OutputBinderInfo>& outputBinderInfoList) const
181 {
182 const int DECIMAL = 10;
183 std::string line;
184 bool findBinderHeader = false;
185 while (getline(fin, line)) {
186 FileUtil::SaveStringToFd(fd, line + "\n");
187 if (findBinderHeader) {
188 continue;
189 }
190
191 if (line.find("async") != std::string::npos) {
192 continue;
193 }
194
195 std::vector<std::string> strList;
196 GetFileToList(line, strList);
197 auto stringSplit = [](const std::string& str, uint16_t index) -> std::string {
198 std::vector<std::string> strings;
199 StringUtil::SplitStr(str, ":", strings);
200 if (index < strings.size()) {
201 return strings[index];
202 }
203 return "";
204 };
205
206 if (strList.size() >= 7) { // 7: valid array size
207 BinderInfo info = {0};
208 OutputBinderInfo outputInfo;
209 // 2: binder peer id,
210 std::string server = stringSplit(strList[2], 0);
211 // 0: binder local id,
212 std::string client = stringSplit(strList[0], 0);
213 // 5: binder wait time, s
214 std::string wait = stringSplit(strList[5], 1);
215 if (server == "" || client == "" || wait == "") {
216 HIVIEW_LOGI("server:%{public}s, client:%{public}s, wait:%{public}s",
217 server.c_str(), client.c_str(), wait.c_str());
218 continue;
219 }
220 info.server = std::strtol(server.c_str(), nullptr, DECIMAL);
221 info.client = std::strtol(client.c_str(), nullptr, DECIMAL);
222 info.wait = std::strtol(wait.c_str(), nullptr, DECIMAL);
223 HIVIEW_LOGD("server:%{public}d, client:%{public}d, wait:%{public}d", info.server, info.client, info.wait);
224 manager[info.client].push_back(info);
225 outputInfo.info = StringUtil::TrimStr(line);
226 outputInfo.pid = info.server;
227 outputBinderInfoList.push_back(outputInfo);
228 } else {
229 HIVIEW_LOGI("strList size: %{public}zu, line: %{public}s", strList.size(), line.c_str());
230 }
231 if (line.find("context") != line.npos) {
232 findBinderHeader = true;
233 }
234 }
235 }
236
GetBinderPeerPids(int fd,int jsonFd) const237 std::set<int> PeerBinderCatcher::GetBinderPeerPids(int fd, int jsonFd) const
238 {
239 std::set<int> pids;
240 std::ifstream fin;
241 std::string path = binderPath_;
242 fin.open(path.c_str());
243 if (!fin.is_open()) {
244 HIVIEW_LOGE("open binder file failed, %{public}s.", path.c_str());
245 std::string content = "open binder file failed :" + path + "\r\n";
246 FileUtil::SaveStringToFd(fd, content);
247 return pids;
248 }
249
250 std::map<int, std::list<PeerBinderCatcher::BinderInfo>> manager = BinderInfoParser(fin, fd, jsonFd);
251 fin.close();
252
253 if (manager.size() == 0 || manager.find(pid_) == manager.end()) {
254 return pids;
255 }
256
257 if (layer_ == LOGGER_BINDER_STACK_ONE) {
258 for (auto each : manager[pid_]) {
259 pids.insert(each.server);
260 }
261 } else if (layer_ == LOGGER_BINDER_STACK_ALL) {
262 ParseBinderCallChain(manager, pids, pid_);
263 }
264 return pids;
265 }
266
ParseBinderCallChain(std::map<int,std::list<PeerBinderCatcher::BinderInfo>> & manager,std::set<int> & pids,int pid) const267 void PeerBinderCatcher::ParseBinderCallChain(std::map<int, std::list<PeerBinderCatcher::BinderInfo>>& manager,
268 std::set<int>& pids, int pid) const
269 {
270 for (auto& each : manager[pid]) {
271 if (pids.find(each.server) != pids.end()) {
272 continue;
273 }
274 pids.insert(each.server);
275 ParseBinderCallChain(manager, pids, each.server);
276 }
277 }
278
IsAncoProc(int pid) const279 bool PeerBinderCatcher::IsAncoProc(int pid) const
280 {
281 std::string cgroupPath = "/proc/" + std::to_string(pid) + "/cgroup";
282 std::string firstLine = FileUtil::GetFirstLine(cgroupPath);
283 return firstLine.find("isulad") != std::string::npos;
284 }
285
CatcherFfrtStack(int fd,int pid) const286 void PeerBinderCatcher::CatcherFfrtStack(int fd, int pid) const
287 {
288 std::string content = "PeerBinderCatcher start catcher ffrt stack for pid : " + std::to_string(pid) + "\r\n";
289 FileUtil::SaveStringToFd(fd, content);
290
291 std::string serviceName = (LogCatcherUtils::GetFfrtDumpType(pid) == LogCatcherUtils::APP) ?
292 "ApplicationManagerService" : "SystemAbilityManager";
293 int count = LogCatcherUtils::WAIT_CHILD_PROCESS_COUNT;
294 LogCatcherUtils::ReadShellToFile(fd, serviceName, "--ffrt " + std::to_string(pid), count);
295 }
296
CatcherStacktrace(int fd,int pid) const297 void PeerBinderCatcher::CatcherStacktrace(int fd, int pid) const
298 {
299 std::string content = "PeerBinderCatcher start catcher stacktrace for pid : " + std::to_string(pid) + "\r\n";
300 FileUtil::SaveStringToFd(fd, content);
301
302 LogCatcherUtils::DumpStacktrace(fd, pid);
303 }
304
305 #ifdef HAS_HIPERF
DoExecHiperf(const std::string & fileName,const std::set<int> & pids)306 void PeerBinderCatcher::DoExecHiperf(const std::string& fileName, const std::set<int>& pids)
307 {
308 std::shared_ptr<PerfCollector> perfCollector = PerfCollector::Create();
309 perfCollector->SetOutputFilename(fileName);
310 constexpr int collectTime = 1;
311 perfCollector->SetTimeStopSec(collectTime);
312 if (perfCmd_.find("a") == std::string::npos) {
313 std::vector<pid_t> selectPids;
314 selectPids.push_back(pid_);
315 for (const auto& pid : pids) {
316 if (pid > 0) {
317 selectPids.push_back(pid);
318 }
319 }
320 perfCollector->SetSelectPids(selectPids);
321 perfCollector->SetReport(true);
322 } else {
323 perfCollector->SetTargetSystemWide(true);
324 }
325 perfCollector->SetFrequency(1000); // 1000 : 1kHz
326 CollectResult<bool> ret = perfCollector->StartPerf(EVENT_LOG_PATH);
327 if (ret.retCode == UCollect::UcError::SUCCESS) {
328 HIVIEW_LOGI("Success to call perf result : %{public}d", ret.data);
329 } else {
330 HIVIEW_LOGI("Failed to call perf result : %{public}d", ret.data);
331 }
332 }
333
ForkToDumpHiperf(const std::set<int> & pids)334 void PeerBinderCatcher::ForkToDumpHiperf(const std::set<int>& pids)
335 {
336 #if defined(__aarch64__)
337 if (perfCmd_.empty()) {
338 HIVIEW_LOGI("BinderPeer perf is not configured.");
339 return;
340 }
341
342 if (!Parameter::IsBetaVersion()) {
343 HIVIEW_LOGI("BinderPeer perf is only enabled in beta version.");
344 return;
345 }
346
347 static std::mutex lock;
348 std::unique_lock<std::mutex> mlock(lock);
349 std::string fileName = "hiperf-" + std::to_string(pid_) + ".data";
350 std::string fullPath = std::string(EVENT_LOG_PATH) + "/" + fileName;
351 if (access(fullPath.c_str(), F_OK) == 0) {
352 struct stat statBuf;
353 auto now = time(nullptr);
354 if (stat(fullPath.c_str(), &statBuf) == -1) {
355 HIVIEW_LOGI("Failed to stat file, error:%{public}d.", errno);
356 FileUtil::RemoveFile(fullPath);
357 } else if (now - statBuf.st_mtime < PERF_LOG_EXPIRE_TIME) {
358 HIVIEW_LOGI("Target log has exist, reuse it.");
359 return;
360 } else {
361 FileUtil::RemoveFile(fullPath);
362 }
363 }
364
365 pid_t child = fork();
366 if (child < 0) {
367 // failed to fork child
368 return;
369 } else if (child == 0) {
370 pid_t grandChild = fork();
371 if (grandChild == 0) {
372 DoExecHiperf(fileName, pids);
373 }
374 _exit(0);
375 } else {
376 // do not left a zombie
377 waitpid(child, nullptr, 0);
378 }
379 #endif
380 }
381 #endif
382 } // namespace HiviewDFX
383 } // namespace OHOS