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